Revert: DocRef (#32866)

- Hardly used anywhere
- Too many hardcoded `__value__` calls without which it's not usable.
- Another type to worry about
This commit is contained in:
Ankush Menat 2025-06-10 10:50:56 +05:30 committed by GitHub
parent 49a11a0112
commit e4bc7f361b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 7 additions and 116 deletions

View file

@ -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

View file

@ -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}"

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -1,3 +1,2 @@
from .docref import DocRef
from .filter import Filters, FilterSignature, FilterTuple
from .frappedict import _dict

View file

@ -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'}>"

View file

@ -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)}"

View file

@ -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""")

View file

@ -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",
]