fix: meta signature (#28526)

* fix: meta

* fix: test case

save is required:

1. Fetch values
```python
	def get_invalid_links(self, is_submittable=False):
		"""Return list of invalid links and also update fetch values if not set."""
		...
```

2. Is triggered by
```python

	def _validate_links(self):
		if self.flags.ignore_links or self._action == "cancel":
			return
        ...
```

3. Which is triggered by either `_save` or `insert`

----

`reload` does not trigger link fetch
```python
	def reload(self) -> "Self":
		"""Reload document from database"""
		return self.load_from_db()
```

Neither does the new calling path which does not excempt Document
from caching when initializing meta.

It can be proven that this revert would be an alternative fix. But this
seems design by accident and there's no preceivable reason to excempt
Document args from being cached normally.

diff --git a/frappe/model/meta.py b/frappe/model/meta.py
index c4321f0128..87452c812c 100644
--- a/frappe/model/meta.py
+++ b/frappe/model/meta.py
@@ -70,11 +70,10 @@ def get_meta(doctype: str | dict | DocRef, cached=True) -> "_Meta":
 	Returns:
 	    Meta object for the given doctype.
 	"""
-	if cached and (
-		doctype_name := getattr(doctype, "doctype", doctype)
-		if not isinstance(doctype, dict)
-		else doctype.get("doctype")
-	):
+	if cached and not isinstance(doctype, Document):
+		doctype_name = (
+			getattr(doctype, "doctype", doctype) if not isinstance(doctype, dict) else doctype.get("doctype")
+		)
 		if meta := frappe.cache.hget("doctype_meta", doctype_name):
 			return meta

Therefore, we comply the test.
This commit is contained in:
David Arnold 2024-11-20 05:17:38 +01:00 committed by GitHub
parent 15d122025f
commit 3c1392c8fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 18 additions and 15 deletions

View file

@ -520,7 +520,7 @@ class DocType(Document):
self.setup_autoincrement_and_sequence()
try:
frappe.db.updatedb(self.name, Meta(self))
frappe.db.updatedb(self.name, Meta(None, bootstrap=self))
except Exception as e:
print(f"\n\nThere was an issue while migrating the DocType: {self.name}\n")
raise e

View file

@ -39,7 +39,7 @@ class TestToDo(IntegrationTestCase):
def test_fetch_setup(self):
frappe.db.delete("ToDo")
todo_meta = frappe.get_doc("DocType", "ToDo")
todo_meta = frappe.get_meta("ToDo")
todo_meta.get("fields", dict(fieldname="assigned_by_full_name"))[0].fetch_from = ""
todo_meta.save()
@ -48,13 +48,14 @@ class TestToDo(IntegrationTestCase):
todo = frappe.get_doc(doctype="ToDo", description="test todo", assigned_by="Administrator").insert()
self.assertFalse(todo.assigned_by_full_name)
todo_meta = frappe.get_doc("DocType", "ToDo")
todo_meta = frappe.get_meta("ToDo")
todo_meta.get("fields", dict(fieldname="assigned_by_full_name"))[
0
].fetch_from = "assigned_by.full_name"
todo_meta.save()
todo.reload()
todo.save()
self.assertEqual(
todo.assigned_by_full_name, frappe.db.get_value("User", todo.assigned_by, "full_name")
@ -120,7 +121,7 @@ class TestToDo(IntegrationTestCase):
frappe.db.delete("ToDo")
# Allow user changes
todo_meta = frappe.get_doc("DocType", "ToDo")
todo_meta = frappe.get_meta("ToDo")
field = todo_meta.get("fields", dict(fieldname="assigned_by_full_name"))[0]
field.fetch_from = "assigned_by.full_name"
field.fetch_if_empty = 1

View file

@ -20,6 +20,7 @@ import os
import typing
from datetime import datetime
from functools import singledispatchmethod
from types import NoneType
import click
@ -41,7 +42,7 @@ from frappe.model.base_document import (
from frappe.model.document import Document
from frappe.model.workflow import get_workflow_name
from frappe.modules import load_doctype_module
from frappe.types import DocRef, _dict
from frappe.types import DocRef
from frappe.utils import cast, cint, cstr
DEFAULT_FIELD_LABELS = {
@ -59,20 +60,21 @@ DEFAULT_FIELD_LABELS = {
}
def get_meta(doctype: str | dict | DocRef | Document, cached=True) -> "_Meta":
def get_meta(doctype: str | dict | DocRef, cached=True) -> "_Meta":
"""Get metadata for a doctype.
Args:
doctype: The doctype as a string, dict, DocRef, or Document object.
doctype: The doctype as a string, dict, DocRef (also: Document) object.
cached: Whether to use cached metadata (default: True).
Returns:
Meta object for the given doctype.
"""
if cached and not isinstance(doctype, Document):
doctype_name = (
getattr(doctype, "doctype", doctype) if not isinstance(doctype, dict) else doctype.get("doctype")
)
if cached and (
doctype_name := getattr(doctype, "doctype", doctype)
if not isinstance(doctype, dict)
else doctype.get("doctype")
):
if meta := frappe.cache.hget("doctype_meta", doctype_name):
return meta
@ -127,7 +129,7 @@ class Meta(Document):
)
@singledispatchmethod
def __init__(self, arg):
def __init__(self, arg, bootstrap: Document = None):
raise TypeError(f"Unsupported argument type: {type(arg)}")
@__init__.register(str)
@ -145,9 +147,9 @@ class Meta(Document):
super().__init__("DocType", doc_ref.get("doctype"))
self.process()
@__init__.register(Document)
def _(self, doc):
super().__init__(doc.as_dict())
@__init__.register(NoneType)
def _(self, _args, bootstrap):
super().__init__(bootstrap.as_dict())
self.process()
def load_from_db(self):