Implement MediaEntry().delete() (#540)

Deleting a MediaEntry instance will automatically
delete all related comments and files/attachments. This moves
implementation logic out of views.py and allows to make use of this
functionality when e.g. deleting a User() account.

Whenever a MediaEntry entry is deleted, this will also sql-delete
the corresponding MediaFile entry.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
This commit is contained in:
Sebastian Spaeth 2012-11-15 11:44:50 +01:00
parent cf764377df
commit fdc34b8ba7
2 changed files with 37 additions and 19 deletions

View File

@ -18,7 +18,7 @@
TODO: indexes on foreignkeys, where useful. TODO: indexes on foreignkeys, where useful.
""" """
import logging
import datetime import datetime
import sys import sys
@ -34,6 +34,7 @@ from sqlalchemy.util import memoized_property
from mediagoblin.db.extratypes import PathTupleWithSlashes, JSONEncoded from mediagoblin.db.extratypes import PathTupleWithSlashes, JSONEncoded
from mediagoblin.db.base import Base, DictReadAttrProxy, Session from mediagoblin.db.base import Base, DictReadAttrProxy, Session
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin, CollectionMixin, CollectionItemMixin from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin, CollectionMixin, CollectionItemMixin
from mediagoblin.tools.files import delete_media_files
# It's actually kind of annoying how sqlalchemy-migrate does this, if # It's actually kind of annoying how sqlalchemy-migrate does this, if
# I understand it right, but whatever. Anyway, don't remove this :P # I understand it right, but whatever. Anyway, don't remove this :P
@ -42,6 +43,8 @@ from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin,
# this import-based meddling... # this import-based meddling...
from migrate import changeset from migrate import changeset
_log = logging.getLogger(__name__)
class User(Base, UserMixin): class User(Base, UserMixin):
""" """
@ -122,7 +125,6 @@ class MediaEntry(Base, MediaEntryMixin):
) )
attachment_files_helper = relationship("MediaAttachmentFile", attachment_files_helper = relationship("MediaAttachmentFile",
cascade="all, delete-orphan",
order_by="MediaAttachmentFile.created" order_by="MediaAttachmentFile.created"
) )
attachment_files = association_proxy("attachment_files_helper", "dict_view", attachment_files = association_proxy("attachment_files_helper", "dict_view",
@ -131,7 +133,7 @@ class MediaEntry(Base, MediaEntryMixin):
) )
tags_helper = relationship("MediaTag", tags_helper = relationship("MediaTag",
cascade="all, delete-orphan" cascade="all, delete-orphan" # should be automatically deleted
) )
tags = association_proxy("tags_helper", "dict_view", tags = association_proxy("tags_helper", "dict_view",
creator=lambda v: MediaTag(name=v["name"], slug=v["slug"]) creator=lambda v: MediaTag(name=v["name"], slug=v["slug"])
@ -216,6 +218,37 @@ class MediaEntry(Base, MediaEntryMixin):
id=self.id, id=self.id,
title=safe_title) title=safe_title)
def delete(self, del_orphan_tags=True, **kwargs):
"""Delete MediaEntry and all related files/attachments/comments
This will *not* automatically delete unused collections, which
can remain empty...
:param del_orphan_tags: True/false if we delete unused Tags too
:param commit: True/False if this should end the db transaction"""
# User's CollectionItems are automatically deleted via "cascade".
# Delete all the associated comments
for comment in self.get_comments():
comment.delete(commit=False)
# Delete all related files/attachments
try:
delete_media_files(self)
except OSError, error:
# Returns list of files we failed to delete
_log.error('No such files from the user "{1}" to delete: '
'{0}'.format(str(error), self.get_uploader))
_log.info('Deleted Media entry id "{0}"'.format(self.id))
# Related MediaTag's are automatically cleaned, but we might
# want to clean out unused Tag's too.
if del_orphan_tags:
# TODO: Import here due to cyclic imports!!!
# This cries for refactoring
from mediagoblin.db.util import clean_orphan_tags
clean_orphan_tags(commit=False)
# pass through commit=False/True in kwargs
super(MediaEntry, self).delete(**kwargs)
class FileKeynames(Base): class FileKeynames(Base):
""" """

View File

@ -23,7 +23,6 @@ from mediagoblin.db.models import (MediaEntry, Collection, CollectionItem,
from mediagoblin.tools.response import render_to_response, render_404, redirect from mediagoblin.tools.response import render_to_response, render_404, redirect
from mediagoblin.tools.translate import pass_to_ugettext as _ from mediagoblin.tools.translate import pass_to_ugettext as _
from mediagoblin.tools.pagination import Pagination from mediagoblin.tools.pagination import Pagination
from mediagoblin.tools.files import delete_media_files
from mediagoblin.user_pages import forms as user_forms from mediagoblin.user_pages import forms as user_forms
from mediagoblin.user_pages.lib import send_comment_email from mediagoblin.user_pages.lib import send_comment_email
@ -268,21 +267,7 @@ def media_confirm_delete(request, media):
if request.method == 'POST' and form.validate(): if request.method == 'POST' and form.validate():
if form.confirm.data is True: if form.confirm.data is True:
username = media.get_uploader.username username = media.get_uploader.username
# Delete MediaEntry and all related files, comments etc.
# Delete all the associated comments
for comment in media.get_comments():
comment.delete()
# Delete all files on the public storage
try:
delete_media_files(media)
except OSError, error:
_log.error('No such files from the user "{1}"'
' to delete: {0}'.format(str(error), username))
messages.add_message(request, messages.ERROR,
_('Some of the files with this entry seem'
' to be missing. Deleting anyway.'))
media.delete() media.delete()
messages.add_message( messages.add_message(
request, messages.SUCCESS, _('You deleted the media.')) request, messages.SUCCESS, _('You deleted the media.'))