Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,4 @@ that much better:
* Agustin Barto (https://github.com/abarto)
* Stankiewicz Mateusz (https://github.com/mas15)
* Felix Schultheiß (https://github.com/felix-smashdocs)
* Lake Chan (https://github.com/StoneMoe)
3 changes: 3 additions & 0 deletions mongoengine/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1774,6 +1774,9 @@ def __eq__(self, other):
def __ne__(self, other):
return not self == other

def __hash__(self):
return int("{}{}".format(self.collection_name, self.grid_id).encode().hex(), 16)

@property
def fs(self):
if not self._fs:
Expand Down
13 changes: 13 additions & 0 deletions mongoengine/queryset/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,20 @@ def delete(self, write_concern=None, _from_doc_delete=False, cascade_refs=None):
)

with set_write_concern(queryset._collection, write_concern) as collection:
gridfs_refs = set()
for name, field in doc._fields.items():
if field.__class__.__name__ == "FileField":
gridfs_refs.update(queryset.scalar(name))
if (
field.__class__.__name__ == "ListField"
and field.field.__class__.__name__ == "FileField"
):
for ref_list in queryset.scalar(name):
gridfs_refs.update(ref_list)

result = collection.delete_many(queryset._query)
for ref in gridfs_refs:
ref.delete()

# If we're using an unack'd write concern, we don't really know how
# many items have been deleted at this point, hence we only return
Expand Down
68 changes: 68 additions & 0 deletions tests/fields/test_file_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from mongoengine import *
from mongoengine.connection import get_db
from mongoengine.fields import FileField, ListField

try:
from PIL import Image
Expand Down Expand Up @@ -581,6 +582,73 @@ class Animal(Document):
assert marmot.photos[0].foo == "bar"
assert marmot.photos[0].get().length == 8313

def test_cascade_del_filefield(self):
"""Ensure cascade deletion also remove file chunks"""

class User(Document):
username = StringField()

class Album(Document):
user = ReferenceField("User")
photo = FileField()

User.register_delete_rule(Album, "user", CASCADE)

User.drop_collection()
Album.drop_collection()
self.db["fs.files"].drop()
self.db["fs.chunks"].drop()

user = User(username="bob").save()
assert User.objects.get() == user

album = Album(user=user)
with open(TEST_IMAGE_PATH, "rb") as img:
album.photo.put(img)
album.save()
assert Album.objects.get().user == user

user.delete()
assert User.objects.count() == 0
assert Album.objects.count() == 0
assert self.db["fs.files"].count() == 0
assert self.db["fs.chunks"].count() == 0

def test_cascade_del_complex_field_filefield(self):
"""Ensure cascade deletion also remove file chunks"""

class User(Document):
username = StringField()

class Album(Document):
user = ReferenceField("User")
photos = ListField(FileField())

User.register_delete_rule(Album, "user", CASCADE)

User.drop_collection()
Album.drop_collection()
self.db["fs.files"].drop()
self.db["fs.chunks"].drop()

user = User(username="bob").save()
assert User.objects.get() == user

album = Album(user=user)
with open(TEST_IMAGE_PATH, "rb") as img:
photos_field = album._fields["photos"].field
new_proxy = photos_field.get_proxy_obj("photos", album)
new_proxy.put(img, content_type="image/jpeg", foo="bar")
album.photos.append(new_proxy)
album.save()
assert Album.objects.get().user == user

user.delete()
assert User.objects.count() == 0
assert Album.objects.count() == 0
assert self.db["fs.files"].count() == 0
assert self.db["fs.chunks"].count() == 0


if __name__ == "__main__":
unittest.main()