mongoengine icon indicating copy to clipboard operation
mongoengine copied to clipboard

Reverse delete rule not allowed for EmbeddedDocuments

Open Vayel opened this issue 7 years ago • 14 comments

Hi!

I have the following models:

class A(Document):
    name = StringField()

class Embedded(EmbeddedDocument):
    ref = ReferenceField('A', reverse_delete_rule=DENY)

class B(Document):
    children = MapField(EmbeddedDocumentField('Embedded'))

But I obtain the error:

File "/home/.../mongoengine/base/metaclasses.py", line 210, in __new__
    raise InvalidDocumentError(msg)
mongoengine.errors.InvalidDocumentError: Reverse delete rules are not supported for EmbeddedDocuments (field: ref)

What is it due to? Is there a way to bypass this constraint?

Thanks!

Vayel avatar Jul 11 '17 19:07 Vayel

Any update on this?

@Vayel have you got a workaround for this?

vedavidhbudimuri avatar Sep 11 '17 05:09 vedavidhbudimuri

Is there any specific reason to use reverse_delete_rule in EmbeddedDocument? EmbeddedDocument is not saved in collection.

erdenezul avatar Sep 11 '17 06:09 erdenezul

As the default reverse_delete_rule DO_NOTHING for reference field. Is there a way to enable cascading on delete for EmbeddedDocument? (I do get that EmbeddedDocument not saved in collection, but just wanna know if anything like cascading on delete could be done in EmbeddedDocuments as well.)

vedavidhbudimuri avatar Sep 11 '17 06:09 vedavidhbudimuri

I would similarly like to see this available. If I have, say, multiple Comment EmbeddedDocuments contained as a list in a BlogPost Document,

class User(Document):
    name = StringField()

class Comment(EmbeddedDocument):
    content = StringField()
    user = ReferenceField(User, reverse_delete_rule=NULLIFY)

class BlogPost(Document):
    subject = StringField()
    content = StringField()
    comments = ListField(EmbeddedDocumentField(Comment))

The above would allow the user associated with a comment to be deleted and for all of the references on BlogPosts to be removed cleanly. As it stands at the moment (without the reverse_delete_rule) when the user gets deleted then access to BlogPost.comments[x].user raises an exception:

mongoengine.errors.DoesNotExist: Trying to dereference unknown document DBRef('user', ObjectId('...'))

Is there any workaround for this? Or does much work need to be done to allow ReferencesFields on EmbeddedDocument to support reverse_delete_rule?

Cheers.

electricworry avatar Mar 09 '18 10:03 electricworry

Any news on this?

ribas14 avatar Sep 30 '18 23:09 ribas14

Hey, is there any update on this, or has anyone found a workaround? This is a pretty crucial feature to maintain database integrity in applications with nested document fields (which I imagine is common). Currently it seems the only option is to write a cascade rule myself, or change all of our EmbeddedDocument containing Referencefields into their own collections (I do not understand why this is necessary, is the reason technical or am I using EmbeddedDocuments improperly?)

Killerjay666 avatar Jul 26 '19 15:07 Killerjay666

Hello all, I came looking for a solution as well for this case and it seems no one has taken the time to change/update this behaviour.

The workaround I propose is the following; the context is using mongoengine with Flask-Admin for the CRUD handling.

Here are my models:

class Member(Document):
    first_name = StringField(required=True)
    last_name = StringField(required=True, unique_with='first_name')
    default_role = StringField()

class ProjectRole(EmbeddedDocument):
    member = ReferenceField(Member,
                            required=True,)
    role = StringField()


class Project(Document):
    members = ListField(EmbeddedDocumentField(ProjectRole, required=True))
    title = StringField(unique=True, required=True)

The workaround I propose is to make a query upon deletion of a Member that scours the Projects in search of the projects containing the currently-being-deleted member and deleting them from those projects' ListField of EmbeddedDocumentField as show on the Flask-Admin view here:

class MemberView(ModelView):
    def on_model_delete(self, model):
        # find the projects that contain this member and delete the member from
        # the ProjectRole ListField (see models for clarification)
        Project.objects().update(pull__members__member=model)

The on_model_delete function is overwritten from the Flask-Admin framework, but you should be able to handle it in the same way from a standard POST form that deletes the Member instance.

I hope this helps. Cheers

DoubleSentinel avatar Oct 03 '19 10:10 DoubleSentinel

Sad that this isn't get that much attention since 2017. This is something that is required for like chats or as eleectric mentioned for blog posts with comments. If no one is going to take on this of the maintainers or it is by design, please provide a proper solution to handle those cases. It is really unclear for me how to create a list of pre-defined dict structure without the use of embedded documents.

muuvmuuv avatar Dec 05 '19 13:12 muuvmuuv

Tried removing the part where it throws the error (metaclasses.py) haha, but it seems it does not get the ref. We should tell MongoEngine here to respect it as an embedded document and ignore it.

  File "/usr/local/lib/python3.7/site-packages/mongoengine/document.py", line 618, in delete
    **self._object_key).delete(write_concern=write_concern, _from_doc_delete=True)
  File "/usr/local/lib/python3.7/site-packages/mongoengine/queryset/base.py", line 467, in delete
    if doc._collection == document_cls._collection:
AttributeError: type object 'ChatMember' has no attribute '_collection'

muuvmuuv avatar Dec 05 '19 14:12 muuvmuuv

stuck on the same issue here. i would love to spend some time trying to solve it. @muuvmuuv do you still work with mongoengine ? if you do, do you know what part of the package code does the checking for the ReferenceField, I'll pretty much new to solving issues on open source content so any help would be great !

ameenalakhras avatar Jan 05 '21 18:01 ameenalakhras

I'm new to mongoengine an got the same issue here. I think it's important to have reverse_delete_rule for ReferenceFields in EmbeddedDocuments, and most of the default rules make sense.

  1. DO_NOTHING: default behavior
  2. DENY: avoid invalid reference in embedded documents
  3. PULL: remove invalid references from a list in an embedded document
  4. NULLIFY: nullify invalid references in an embedded document
  5. The only option that might not make sense is CASCADE, because embedded documents are not saved in collection. But one could still expect a cascaded process where the parent document of the EmbeddedDocument is deleted from its collection.

shuheng-liu avatar Jan 12 '21 09:01 shuheng-liu

I have the following model definitions:

class Movie(db.Document):
    _id = db.SequenceField(primary_key=True)
    name = db.StringField(required=True)


class Rating(db.EmbeddedDocument):
    movieId = db.ReferenceField(Movie, reverse_delete_rule=db.CASCADE)
    rating = db.IntField()


class User(db.Document):
    _id = db.IntField(primary_key=True)
    rating = db.EmbeddedDocumentListField(Rating)

Rating is a EmbeddedDocument, I want to add a reverse_delete_rule so that when a movie is deleted, a user's rating for that movie is deleted as well.

I think this is a really useful feature.

ghost avatar Apr 29 '21 15:04 ghost

Ill second that. I dont want to switch mongo engine for something else because of this..

BarryFourie avatar Aug 01 '21 10:08 BarryFourie

Still stuck on the same issue

Avarow avatar Apr 04 '24 12:04 Avarow