From e4bc7f361b84165b7a7ac78baefa1b4cfc8dc082 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 10 Jun 2025 10:50:56 +0530 Subject: [PATCH] Revert: DocRef (#32866) - Hardly used anywhere - Too many hardcoded `__value__` calls without which it's not usable. - Another type to worry about --- frappe/database/utils.py | 5 +-- frappe/model/base_document.py | 6 +--- frappe/model/document.py | 14 ++------ frappe/model/meta.py | 1 - frappe/tests/test_doc_ref.py | 61 ----------------------------------- frappe/types/__init__.py | 1 - frappe/types/docref.py | 23 ------------- frappe/types/filter.py | 8 ++--- frappe/utils/response.py | 3 -- pyproject.toml | 1 - 10 files changed, 7 insertions(+), 116 deletions(-) delete mode 100644 frappe/tests/test_doc_ref.py delete mode 100644 frappe/types/docref.py diff --git a/frappe/database/utils.py b/frappe/database/utils.py index 6a54014423..fd4f0092a4 100644 --- a/frappe/database/utils.py +++ b/frappe/database/utils.py @@ -8,11 +8,10 @@ from functools import cached_property, wraps import frappe from frappe.query_builder.builder import MariaDB, Postgres, SQLite from frappe.query_builder.functions import Function -from frappe.types import DocRef Query = str | MariaDB | Postgres | SQLite QueryValues = tuple | list | dict | None -FilterValue = DocRef | str | int | bool +FilterValue = str | int | bool EmptyQueryValues = object() FallBackDateTimeStr = "0001-01-01 00:00:00.000000" @@ -29,8 +28,6 @@ QUERY_TYPE_PATTERN = re.compile(r"\s*([A-Za-z]*)") def convert_to_value(o: FilterValue): - if hasattr(o, "__value__"): - return o.__value__() if isinstance(o, bool): return int(o) return o diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 8a584d33aa..fac66fa4d8 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -22,7 +22,6 @@ from frappe.model.dynamic_links import invalidate_distinct_link_doctypes from frappe.model.naming import set_new_name from frappe.model.utils.link_count import notify_link_count from frappe.modules import load_doctype_module -from frappe.types.docref import DocRef from frappe.utils import ( cached_property, cast_fieldtype, @@ -475,9 +474,6 @@ class BaseDocument: else: value = get_not_null_defaults(df.fieldtype) - if hasattr(value, "__value__"): - value = value.__value__() - d[fieldname] = value return d @@ -865,7 +861,7 @@ class BaseDocument: if not docname: continue - assert isinstance(docname, str | int | DocRef) or ( + assert isinstance(docname, str | int) or ( isinstance(docname, list | tuple | set) and len(docname) == 1 ), f"Unexpected value for field {df.fieldname}: {docname}" diff --git a/frappe/model/document.py b/frappe/model/document.py index b55fdafd2a..81793d50d9 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -24,7 +24,7 @@ from frappe.model.docstatus import DocStatus from frappe.model.naming import set_new_name, validate_name from frappe.model.utils import is_virtual_doctype, simple_singledispatch from frappe.model.workflow import set_workflow_state_on_action, validate_workflow -from frappe.types import DF, DocRef +from frappe.types import DF from frappe.utils import compare, cstr, date_diff, file_lock, flt, get_table_name, now from frappe.utils.data import get_absolute_url, get_datetime, get_timedelta, getdate from frappe.utils.global_search import update_global_search @@ -105,7 +105,7 @@ def get_doc(*args, **kwargs) -> "Document": if not args and kwargs: return get_doc_from_dict(kwargs) else: - raise ValueError("First non keyword argument must be a string, dict or DocRef") + raise ValueError("First non keyword argument must be a string or dict") @get_doc.register(BaseDocument) @@ -113,11 +113,6 @@ def _basedoc(doc: BaseDocument, *args, **kwargs) -> "Document": return doc -@get_doc.register(DocRef) -def _docref(doc_ref: DocRef, **kwargs) -> "Document": - return get_doc(doc_ref.doctype, doc_ref.name, **kwargs) - - @get_doc.register(str) def get_doc_str(doctype: str, name: str | None = None, **kwargs) -> "Document": # if no name: it's a single @@ -143,7 +138,7 @@ def get_doc_from_dict(data: dict[str, Any], **kwargs) -> "Document": raise ImportError(data["doctype"]) -class Document(BaseDocument, DocRef): +class Document(BaseDocument): """All controllers inherit from `Document`.""" doctype: DF.Data @@ -197,9 +192,6 @@ class Document(BaseDocument, DocRef): if isinstance(arg, dict): return self._init_from_kwargs(arg) - if isinstance(arg, DocRef): - return self._init_known_doc(arg.doctype, arg.name, **kwargs) - raise ValueError(f"Unsupported argument type: {type(arg)}") @property diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 790bd1e586..8ac2018bd2 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -41,7 +41,6 @@ from frappe.model.document import Document from frappe.model.utils import is_single_doctype from frappe.model.workflow import get_workflow_name from frappe.modules import load_doctype_module -from frappe.types import DocRef from frappe.utils import cached_property, cast, cint, cstr from frappe.utils.caching import site_cache from frappe.utils.data import add_to_date, get_datetime diff --git a/frappe/tests/test_doc_ref.py b/frappe/tests/test_doc_ref.py deleted file mode 100644 index dc892d4200..0000000000 --- a/frappe/tests/test_doc_ref.py +++ /dev/null @@ -1,61 +0,0 @@ -import frappe -from frappe.model.document import Document, get_doc -from frappe.tests import IntegrationTestCase -from frappe.types import DocRef - -EXTRA_TEST_RECORD_DEPENDENCIES = ["User"] - - -class TestDocRef(IntegrationTestCase): - def test_doc_ref_get_doc(self): - # Test using DocRef with get_doc - doc_ref = DocRef("User", "test@example.com") - user = get_doc(doc_ref) - - # Assert that user is an instance of both Document and DocRef - self.assertIsInstance(user, Document) - self.assertIsInstance(user, DocRef) - - # Check more attributes - self.assertEqual(user.doctype, "User") - self.assertEqual(user.name, "test@example.com") - self.assertEqual(user.email, "test@example.com") - self.assertEqual(user.first_name, "_Test") - - def test_doc_ref_in_query(self): - # Test using DocRef in a database query - user = frappe.get_doc("User", "test@example.com") - - # Assert that user is an instance of both Document and DocRef - self.assertIsInstance(user, Document) - self.assertIsInstance(user, DocRef) - - # Create a test document that references the user - test_doc = frappe.get_doc( - { - "doctype": "ToDo", - "description": "Test ToDo", - "reference_type": "User", - "reference_name": user, # This should work with DocRef - } - ).insert() - - # Getter using the DocRef - result = frappe.db.get_value("ToDo", {"reference_name": user}, ["name", "description"]) - self.assertEqual(result[0], test_doc.name) - self.assertEqual(result[1], "Test ToDo") - # Setter using Document as DocRef - frappe.db.set_value("ToDo", test_doc, "description", "Revised Test ToDo") - test_doc.reload() - self.assertEqual(test_doc.description, "Revised Test ToDo") - - def test_doc_ref_value_representation(self): - # Test the value representation of DocRef - doc_ref = DocRef("User", "test@example.com") - self.assertEqual(doc_ref.__value__(), "test@example.com") - - def test_doc_ref_attributes(self): - # Test DocRef attributes - doc_ref = DocRef("User", "test@example.com") - self.assertEqual(doc_ref.doctype, "User") - self.assertEqual(doc_ref.name, "test@example.com") diff --git a/frappe/types/__init__.py b/frappe/types/__init__.py index bb33bcc053..bf524c7db5 100644 --- a/frappe/types/__init__.py +++ b/frappe/types/__init__.py @@ -1,3 +1,2 @@ -from .docref import DocRef from .filter import Filters, FilterSignature, FilterTuple from .frappedict import _dict diff --git a/frappe/types/docref.py b/frappe/types/docref.py deleted file mode 100644 index d0bcebf07b..0000000000 --- a/frappe/types/docref.py +++ /dev/null @@ -1,23 +0,0 @@ -from typing import Union - -from typing_extensions import override - - -class DocRef: - """A lightweight reference to a document, containing just the doctype and name.""" - - def __init__(self, doctype: str, name: str): - self.doctype = doctype - self.name = name - - def __value__(self) -> str: - # Used when requiring its value representation for db interactions, serializations, etc - return self.name - - @override - def __str__(self) -> str: - return f"{self.doctype} ({self.name or 'n/a'})" - - @override - def __repr__(self) -> str: - return f"<{self.__class__.__name__}: doctype={self.doctype} name={self.name or 'n/a'}>" diff --git a/frappe/types/filter.py b/frappe/types/filter.py index 03686a2bf5..17f64389ca 100644 --- a/frappe/types/filter.py +++ b/frappe/types/filter.py @@ -9,14 +9,12 @@ from typing import Any, NamedTuple, TypeAlias, TypeGuard, TypeVar, cast from pypika import Column from typing_extensions import Self, override -from .docref import DocRef - Doct: TypeAlias = str Fld: TypeAlias = str Op: TypeAlias = str DateTime: TypeAlias = datetime | date _Value: TypeAlias = str | int | float | None | DateTime | Column -_InputValue: TypeAlias = _Value | DocRef | bool +_InputValue: TypeAlias = _Value | bool Value: TypeAlias = _Value | Sequence[_Value] InputValue: TypeAlias = _InputValue | Sequence[_InputValue] @@ -55,10 +53,8 @@ class _FilterTuple(NamedTuple): def _type_narrow(v: _InputValue) -> _Value: if isinstance(v, bool): # beware: bool derives int in _Value return int(v) - elif isinstance(v, _Value): + elif isinstance(v, _Value): # type: ignore[redundant-expr] return v - elif isinstance(v, DocRef): # type: ignore[redundant-expr] - return v.__value__() else: raise ValueError( f"Value must be one of types: {', '.join(str(t.__name__) for t in _InputValue.__args__)}; found {type(v)}" diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 7a09b64b8f..679535cd2e 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -248,9 +248,6 @@ def json_handler(obj): elif isinstance(obj, Path): return str(obj) - elif hasattr(obj, "__value__"): # order imporant: defer to __json__ if implemented - return obj.__value__() - else: raise TypeError(f"""Object of type {type(obj)} with value of {obj!r} is not JSON serializable""") diff --git a/pyproject.toml b/pyproject.toml index 9f93daae8c..c54643c640 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -223,7 +223,6 @@ files = [ # start small, with a lot of multiplication potential "frappe/types/__init__.py", "frappe/types/DF.py", - "frappe/types/docref.py", "frappe/types/frappedict.py", "frappe/types/filter.py", ]