Skip to content

Commit ce31837

Browse files
committed
Move Schema key lookup transformer into its own module [skip ci]
1 parent 8b987e6 commit ce31837

File tree

2 files changed

+46
-19
lines changed

2 files changed

+46
-19
lines changed

Diff for: django_pydantic_field/v2/fields.py

+3-19
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@
55
import pydantic
66
from django.core import checks, exceptions
77
from django.core.serializers.json import DjangoJSONEncoder
8-
from django.db.models.expressions import BaseExpression, Col, Value
8+
from django.db.models.expressions import BaseExpression, Value
99
from django.db.models.fields import NOT_PROVIDED
1010
from django.db.models.fields.json import JSONField
11-
from django.db.models.lookups import Transform
1211
from django.db.models.query_utils import DeferredAttribute
1312

1413
from django_pydantic_field.compat import deprecation
1514
from django_pydantic_field.compat.django import GenericContainer
1615

17-
from . import forms, types
16+
from . import forms, types, lookups
1817

1918
if ty.TYPE_CHECKING:
2019
import json
@@ -174,7 +173,7 @@ def get_prep_value(self, value: ty.Any):
174173
def get_transform(self, lookup_name: str):
175174
transform: ty.Any = super().get_transform(lookup_name)
176175
if transform is not None:
177-
transform = SchemaKeyTransformAdapter(transform)
176+
transform = lookups.SchemaKeyTransformAdapter(transform, lookup_name)
178177
return transform
179178

180179
def get_default(self) -> types.ST:
@@ -198,21 +197,6 @@ def value_to_string(self, obj: Model):
198197
return self.get_prep_value(value)
199198

200199

201-
class SchemaKeyTransformAdapter:
202-
"""An adapter for creating key transforms for schema field lookups."""
203-
204-
def __init__(self, transform: type[Transform]):
205-
self.transform = transform
206-
207-
def __call__(self, col: Col | None = None, *args, **kwargs) -> Transform | None:
208-
"""All transforms should bypass the SchemaField's adaptaion with `get_prep_value`,
209-
and routed to JSONField's `get_prep_value` for further processing."""
210-
if isinstance(col, BaseExpression):
211-
col = col.copy()
212-
col.output_field = super(PydanticSchemaField, col.output_field) # type: ignore
213-
return self.transform(col, *args, **kwargs)
214-
215-
216200
@ty.overload
217201
def SchemaField(
218202
schema: type[types.ST | None] | ty.ForwardRef = ...,

Diff for: django_pydantic_field/v2/lookups.py

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from __future__ import annotations
2+
3+
import typing as ty
4+
5+
from django.db.models.expressions import BaseExpression, Col
6+
from django.db.models.lookups import Transform
7+
8+
if ty.TYPE_CHECKING:
9+
ExprT = ty.TypeVar("ExprT", bound=BaseExpression)
10+
11+
12+
class SchemaKeyTransformAdapter:
13+
"""An adapter class that modifies the key transformation behavior for schema fields.
14+
15+
This class acts as an adapter, altering how key transformations are performed on schema fields.
16+
It circumvents the usual adaptation process for `PydanticSchemaField` objects,
17+
instead opting to use the `JSONField`'s own transformation methods.
18+
19+
The goal is to utilize the lookup transformation features provided by the `JSONField.encoder` class.
20+
With the current limitations in Pydantic, it's not feasible to conduct partial value adaptations.
21+
22+
While this approach is not ideal for QuerySet lookups,
23+
it allows `JSONField.encoder` (which defaults to `DjangoJSONEncoder`) to perform essential transformations.
24+
"""
25+
26+
def __init__(self, transform: type[Transform], lookup_name: str):
27+
self.transform = transform
28+
self.lookup_name = lookup_name
29+
30+
def __call__(self, col: Col | None = None, *args, **kwargs) -> Transform | None:
31+
"""All transforms should bypass the SchemaField's adaptaion with `get_prep_value`,
32+
and routed to JSONField's `get_prep_value` for further processing."""
33+
if isinstance(col, BaseExpression):
34+
col = self._get_prep_expression(col)
35+
return self.transform(col, *args, **kwargs)
36+
37+
def _get_prep_expression(self, expr: ExprT) -> ExprT:
38+
from .fields import PydanticSchemaField
39+
40+
if isinstance(expr.output_field, PydanticSchemaField):
41+
expr = expr.copy()
42+
expr.output_field = super(PydanticSchemaField, expr.output_field) # type: ignore
43+
return expr

0 commit comments

Comments
 (0)