|
3 | 3 | from django.core import checks
|
4 | 4 | from django.core.exceptions import FieldDoesNotExist
|
5 | 5 | from django.db import models
|
| 6 | +from django.db.models import lookups |
6 | 7 | from django.db.models.fields.related import lazy_related_operation
|
7 | 8 | from django.db.models.lookups import Transform
|
8 | 9 |
|
9 | 10 | from .. import forms
|
| 11 | +from ..query_utils import process_lhs, process_rhs |
10 | 12 | from .json import build_json_mql_path
|
11 | 13 |
|
12 | 14 |
|
@@ -149,6 +151,41 @@ def formfield(self, **kwargs):
|
149 | 151 | )
|
150 | 152 |
|
151 | 153 |
|
| 154 | +@EmbeddedModelField.register_lookup |
| 155 | +class EMFExact(lookups.Exact): |
| 156 | + def model_to_dict(self, instance): |
| 157 | + """Return a dict containing the data in a model instance.""" |
| 158 | + data = {} |
| 159 | + emf_data = {} |
| 160 | + for f in instance._meta.concrete_fields: |
| 161 | + value = f.value_from_object(instance) |
| 162 | + if isinstance(f, EmbeddedModelField): |
| 163 | + emf_data[f"{f.name}"] = self.model_to_dict(value) |
| 164 | + continue |
| 165 | + # Unless explicitly set, primary keys aren't included in embedded |
| 166 | + # models. |
| 167 | + if f.primary_key and value is None: |
| 168 | + continue |
| 169 | + data[f"{f.name}"] = value |
| 170 | + return data, emf_data |
| 171 | + |
| 172 | + def as_mql(self, compiler, connection): |
| 173 | + lhs_mql = process_lhs(self, compiler, connection) |
| 174 | + value = process_rhs(self, compiler, connection) |
| 175 | + if isinstance(value, models.Model): |
| 176 | + value, emf_data = self.model_to_dict(value) |
| 177 | + prefix = self.lhs.as_mql(compiler, connection) |
| 178 | + conditions = [{"$eq": [f"{prefix}.{k}", v]} for k, v in value.items()] |
| 179 | + # TODO: more tests for this logic. Might not work for another |
| 180 | + # layer of embedding. |
| 181 | + while emf_data: |
| 182 | + for k, v in emf_data.items(): |
| 183 | + v, emf_data = v |
| 184 | + conditions += [{"$eq": [f"{prefix}.{k}.{x}", y]} for x, y in v.items()] |
| 185 | + return {"$and": conditions} |
| 186 | + return connection.mongo_operators[self.lookup_name](lhs_mql, value) |
| 187 | + |
| 188 | + |
152 | 189 | class KeyTransform(Transform):
|
153 | 190 | def __init__(self, key_name, ref_field, *args, **kwargs):
|
154 | 191 | super().__init__(*args, **kwargs)
|
@@ -193,7 +230,13 @@ def preprocess_lhs(self, compiler, connection):
|
193 | 230 | previous = previous.lhs
|
194 | 231 | mql = previous.as_mql(compiler, connection)
|
195 | 232 | # The first json_key_transform is the field name.
|
196 |
| - embedded_key_transforms.append(json_key_transforms.pop(0)) |
| 233 | + try: |
| 234 | + field_name = json_key_transforms.pop(0) |
| 235 | + except IndexError: |
| 236 | + # This is a lookup of the embedded model itself. |
| 237 | + pass |
| 238 | + else: |
| 239 | + embedded_key_transforms.append(field_name) |
197 | 240 | return mql, embedded_key_transforms, json_key_transforms
|
198 | 241 |
|
199 | 242 | def as_mql(self, compiler, connection):
|
|
0 commit comments