From 21fb038d0febea5deddf5facd30b2effd05fb702 Mon Sep 17 00:00:00 2001 From: Jan Schrewe Date: Thu, 21 Nov 2013 14:37:17 +0100 Subject: [PATCH] Make sure not to use LazyDocumentMetaWrapper as doc's meta for #61 --- mongodbforms/documentoptions.py | 41 +++++++++++++++++++++++---------- mongodbforms/documents.py | 36 ++++++++++++++--------------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/mongodbforms/documentoptions.py b/mongodbforms/documentoptions.py index f1c2146..efbabf9 100644 --- a/mongodbforms/documentoptions.py +++ b/mongodbforms/documentoptions.py @@ -44,6 +44,9 @@ def to(self, value): class PkWrapper(object): + editable = False + fake = False + def __init__(self, wrapped): self.obj = wrapped @@ -118,6 +121,8 @@ class DocumentMetaWrapper(MutableMapping): _meta = None concrete_model = None concrete_managers = [] + virtual_fields = [] + auto_created = False def __init__(self, document, meta=None): super(DocumentMetaWrapper, self).__init__() @@ -127,9 +132,10 @@ def __init__(self, document, meta=None): # here for now always the document self.concrete_model = document if meta is None: - self._meta = getattr(document, '_meta', {}) - else: - self._meta = meta + meta = getattr(document, '_meta', {}) + if isinstance(meta, LazyDocumentMetaWrapper): + meta = meta._meta + self._meta = meta try: self.object_name = self.document.__name__ @@ -143,9 +149,9 @@ def __init__(self, document, meta=None): self._setup_document_fields() # Setup self.pk if the document has an id_field in it's meta # if it doesn't have one it's an embedded document - if 'id_field' in self._meta: - self.pk_name = self._meta['id_field'] - self._init_pk() + #if 'id_field' in self._meta: + # self.pk_name = self._meta['id_field'] + self._init_pk() def _setup_document_fields(self): for f in self.document._fields.values(): @@ -183,16 +189,27 @@ def _init_pk(self): The function also adds a _get_pk_val method to the document. """ - pk_field = getattr(self.document, self.pk_name) + if 'id_field' in self._meta: + self.pk_name = self._meta['id_field'] + pk_field = getattr(self.document, self.pk_name) + else: + pk_field = None self.pk = PkWrapper(pk_field) - self.pk.name = self.pk_name - self.pk.attname = self.pk_name - - self.document._pk_val = pk_field def _get_pk_val(self): return self._pk_val - patch_document(_get_pk_val, self.document) + + if pk_field is not None: + self.pk.name = self.pk_name + self.pk.attname = self.pk_name + self.document._pk_val = pk_field + patch_document(_get_pk_val, self.document) + else: + self.pk.fake = True + # this is used in the admin and used to determine if the admin + # needs to add a hidden pk field. It does not for embedded fields. + # So we pretend to have an editable pk field and just ignore it otherwise + self.pk.editable = True @property def app_label(self): diff --git a/mongodbforms/documents.py b/mongodbforms/documents.py index aa53846..8c66c07 100644 --- a/mongodbforms/documents.py +++ b/mongodbforms/documents.py @@ -832,7 +832,13 @@ def inlineformset_factory(document, form=DocumentForm, class EmbeddedDocumentFormSet(BaseDocumentFormSet): def __init__(self, data=None, files=None, save_as_new=False, prefix=None, queryset=[], parent_document=None, **kwargs): - self.parent_document = parent_document + if parent_document is not None: + self.parent_document = parent_document + + if 'instance' in kwargs: + instance = kwargs.pop('instance') + if parent_document is None: + self.parent_document = instance queryset = getattr(self.parent_document, self.form._meta.embedded_field) @@ -848,7 +854,8 @@ def _construct_form(self, i, **kwargs): # a huge amount of time iterating over the list field on form __init__ emb_list = getattr(self.parent_document, self.form._meta.embedded_field) - if emb_list is not None and len(emb_list) < i: + + if emb_list is not None and len(emb_list) > i: defaults['position'] = i defaults.update(kwargs) @@ -878,22 +885,15 @@ def save(self, commit=True): objs = objs or [] if commit and self.parent_document is not None: - # The thing about formsets is that the base use case is to edit - # *all* of the associated objects on a model. As written, using - # these FormSets this way will cause the existing embedded - # documents to get saved along with a copy of themselves plus any - # new ones you added. - # - # The only way you could do "updates" of existing embedded document - # fields is if those embedded documents had ObjectIDs of their own, - # which they don't by default in Mongoengine. - # - # In this case it makes the most sense to simply replace the - # embedded field with the new values gathered form the formset, - # rather than adding the new values to the existing values, because - # the new values will almost always contain the old values (with - # the default use case.) - setattr(self.parent_document, self.form._meta.embedded_field, objs) + field = self.parent_document._fields.get(self.form._meta.embedded_field, None) + if isinstance(field, EmbeddedDocumentField): + try: + obj = objs[0] + except IndexError: + obj = None + setattr(self.parent_document, self.form._meta.embedded_field, obj) + else: + setattr(self.parent_document, self.form._meta.embedded_field, objs) self.parent_document.save() return objs