-
-
Save nvie/727282 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from mongoengine import * | |
from mongoengine import DENY, CASCADE, NULLIFY, DO_NOTHING | |
from mongoengine import DeleteForbidden # or something equivalent | |
from mongoengine.queryset import QuerySet | |
class Post(Document): | |
title = StringField() | |
@classmethod | |
def register_delete_rule(cls, document_cls, field_name, rule): | |
""" | |
This method registers the delete rules to apply when removing this | |
object. This could go into the Document class. | |
""" | |
if rule == DO_NOTHING: | |
# This is the default behaviour already | |
return | |
# I don't know if this is the correct way of storing meta-information on | |
# the given Document class, but the essence is that the calling | |
# document_cls/field_name combination (in this case Comment.post) | |
# should be registered to be CASCADED | |
cls.meta['delete_rules'][(document_cls, field_name)] = rule | |
def delete(self, safe=False): | |
# Check first. If *any* delete_rule says DENY, we may not | |
# delete/nullify any of the others, too | |
for key, val in self.meta['delete_rules'].items(): | |
if val == DENY: | |
document_cls, field_name = key | |
raise DeleteForbidden('Delete forbidden because at least the ' | |
'following documents still refer to it: %s.%s' % \ | |
(document_cls, field_name)) | |
for document_cls, field_name in self.meta['delete_rules']: | |
rule = self.meta['delete_rules'][(document_cls, field_name)] | |
if rule == CASCADE: | |
# This line is equivalent to your | |
# Comment.objects(post=self.id).delete(safe=safe) | |
document_cls.objects(**{field_name: self.id}).delete(safe=safe) | |
elif rule == NULLIFY: | |
obj = document_cls.objects(**{field_name: self.id}) | |
obj.field_name = None # See #1 | |
obj.save() # See #2 | |
# | |
# Notes: | |
# 1. This yields a key error for "field_name". Don't know the | |
# correct way of getting this right now from the top of my | |
# head | |
# 2. Probably not atomic this way, don't know the syntax right | |
# now | |
# | |
super(Post, self).delete(safe=safe) | |
class Comment(Document): | |
# The "delete_rule" attribute here would have to store the "backwards" | |
# relation in Post's meta info, for example by setting | |
# | |
# Post.register_delete_rule( | |
# cls, # In this case Comment | |
# attr_that_this_is_assigned_to, # The field name | |
# delete_rule # In this case, CASCADE | |
# ) | |
post = ReferenceField(Post, delete_rule=mongoengine.CASCADE) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Really like this - definitely want it in MongoEngine core.
The
KeyError
on line 44 should be fixable usingsetattr(self, field_name, None)
.WRT atomicity - yeah MongoDB isn't great on that in general. The actual delete operations (i.e. one loop iteration) can optionally be atomic (http://www.mongodb.org/display/DOCS/Removing) but I can't see a way for the operation as a whole to be as there is no transaction support. Shouldn't be a huge issue for most cases though as the
super(Post, self).delete
call comes at the end...I might write up some tests laster on and include this in MongoEngine for the next release (feel free to fork it and add it to the
Document
class if that'd be more convenient).Cheers
H