diff --git a/django_mongodb_backend/forms/fields/embedded_model.py b/django_mongodb_backend/forms/fields/embedded_model.py index bbfa9c02..185be44b 100644 --- a/django_mongodb_backend/forms/fields/embedded_model.py +++ b/django_mongodb_backend/forms/fields/embedded_model.py @@ -4,36 +4,6 @@ from django.utils.translation import gettext_lazy as _ -class EmbeddedModelWidget(forms.MultiWidget): - def __init__(self, field_names, *args, **kwargs): - self.field_names = field_names - super().__init__(*args, **kwargs) - # The default widget names are "_0", "_1", etc. Use the field names - # instead since that's how they'll be rendered by the model form. - self.widgets_names = ["-" + name for name in field_names] - - def decompress(self, value): - if value is None: - return [] - # Get the data from `value` (a model) for each field. - return [getattr(value, name) for name in self.field_names] - - -class EmbeddedModelBoundField(forms.BoundField): - def __init__(self, form, field, name, prefix_override=None): - super().__init__(form, field, name) - # prefix_override overrides the prefix in self.field.form_kwargs so - # that nested embedded model form elements have the correct name. - self.prefix_override = prefix_override - - def __str__(self): - """Render the model form as the representation for this field.""" - form = self.field.model_form_cls(instance=self.value(), **self.field.form_kwargs) - if self.prefix_override: - form.prefix = self.prefix_override - return mark_safe(f"{form.as_div()}") # noqa: S308 - - class EmbeddedModelField(forms.MultiValueField): default_error_messages = { "invalid": _("Enter a list of values."), @@ -79,3 +49,33 @@ def prepare_value(self, value): # (rather than a list) for initializing the form in # EmbeddedModelBoundField.__str__(). return self.compress(value) if isinstance(value, list) else value + + +class EmbeddedModelBoundField(forms.BoundField): + def __init__(self, form, field, name, prefix_override=None): + super().__init__(form, field, name) + # prefix_override overrides the prefix in self.field.form_kwargs so + # that nested embedded model form elements have the correct name. + self.prefix_override = prefix_override + + def __str__(self): + """Render the model form as the representation for this field.""" + form = self.field.model_form_cls(instance=self.value(), **self.field.form_kwargs) + if self.prefix_override: + form.prefix = self.prefix_override + return mark_safe(f"{form.as_div()}") # noqa: S308 + + +class EmbeddedModelWidget(forms.MultiWidget): + def __init__(self, field_names, *args, **kwargs): + self.field_names = field_names + super().__init__(*args, **kwargs) + # The default widget names are "_0", "_1", etc. Use the field names + # instead since that's how they'll be rendered by the model form. + self.widgets_names = ["-" + name for name in field_names] + + def decompress(self, value): + if value is None: + return [] + # Get the data from `value` (a model) for each field. + return [getattr(value, name) for name in self.field_names] diff --git a/docs/source/ref/models/models.rst b/docs/source/ref/models/models.rst index a4491228..32b5fc85 100644 --- a/docs/source/ref/models/models.rst +++ b/docs/source/ref/models/models.rst @@ -14,3 +14,6 @@ One MongoDB-specific model is available in ``django_mongodb_backend.models``. any of the normal ``QuerySet`` methods (``all()``, ``filter()``, ``delete()``, etc.) You also cannot call ``Model.save()`` and ``delete()`` on them. + + Embedded model instances won't have a value for their primary key unless + one is explicitly set. diff --git a/docs/source/topics/embedded-models.rst b/docs/source/topics/embedded-models.rst index 94abecfd..0856c726 100644 --- a/docs/source/topics/embedded-models.rst +++ b/docs/source/topics/embedded-models.rst @@ -10,41 +10,44 @@ The basics Let's consider this example:: - from django_mongodb_backend.fields import EmbeddedModelField - from django_mongodb_backend.models import EmbeddedModel + from django.db import models - class Customer(models.Model): - name = models.CharField(...) - address = EmbeddedModelField("Address") - ... + from django_mongodb_backend.fields import EmbeddedModelField + from django_mongodb_backend.models import EmbeddedModel - class Address(EmbeddedModel): - ... - city = models.CharField(...) + + class Customer(models.Model): + name = models.CharField(max_length=255) + address = EmbeddedModelField("Address") + + def __str__(self): + return self.name + + + class Address(EmbeddedModel): + city = models.CharField(max_length=255) + + def __str__(self): + return self.city The API is similar to that of Django's relational fields:: - >>> Customer.objects.create(name="Bob", address=Address(city="New York", ...), ...) - >>> bob = Customer.objects.get(...) - >>> bob.address - - >>> bob.address.city - 'New York' + >>> bob = Customer.objects.create(name="Bob", address=Address(city="New York")) + >>> bob.address + + >>> bob.address.city + 'New York' -Represented in BSON, Bob's structure looks like this: +Represented in BSON, the customer structure looks like this: .. code-block:: js - { - "_id": ObjectId(...), - "name": "Bob", - "address": { - ... - "city": "New York" - }, - ... - } + { + _id: ObjectId('683df821ec4bbe0692d43388'), + name: 'Bob', + address: { city: 'New York' } + } Querying ``EmbeddedModelField`` -------------------------------