Implement MediaEntry().delete() (#540)
authorSebastian Spaeth <Sebastian@SSpaeth.de>
Thu, 15 Nov 2012 10:44:50 +0000 (11:44 +0100)
committerSebastian Spaeth <Sebastian@SSpaeth.de>
Thu, 17 Jan 2013 10:48:07 +0000 (11:48 +0100)
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>
mediagoblin/db/models.py
mediagoblin/user_pages/views.py

index ea915ae5957cdc7b093a2e29b100b91ef3723b9f..aeec8aead3034f6ff19a0c099d2ea3eb662aa5c5 100644 (file)
@@ -18,7 +18,7 @@
 TODO: indexes on foreignkeys, where useful.
 """
 
-
+import logging
 import datetime
 import sys
 
@@ -34,6 +34,7 @@ from sqlalchemy.util import memoized_property
 from mediagoblin.db.extratypes import PathTupleWithSlashes, JSONEncoded
 from mediagoblin.db.base import Base, DictReadAttrProxy, Session
 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
 # 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...
 from migrate import changeset
 
+_log = logging.getLogger(__name__)
+
 
 class User(Base, UserMixin):
     """
@@ -122,7 +125,6 @@ class MediaEntry(Base, MediaEntryMixin):
         )
 
     attachment_files_helper = relationship("MediaAttachmentFile",
-        cascade="all, delete-orphan",
         order_by="MediaAttachmentFile.created"
         )
     attachment_files = association_proxy("attachment_files_helper", "dict_view",
@@ -131,7 +133,7 @@ class MediaEntry(Base, MediaEntryMixin):
         )
 
     tags_helper = relationship("MediaTag",
-        cascade="all, delete-orphan"
+        cascade="all, delete-orphan" # should be automatically deleted
         )
     tags = association_proxy("tags_helper", "dict_view",
         creator=lambda v: MediaTag(name=v["name"], slug=v["slug"])
@@ -216,6 +218,37 @@ class MediaEntry(Base, MediaEntryMixin):
                 id=self.id,
                 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):
     """
index f115c3b8dfdcca26630f04ca84d59539a575ba97..1af04e7fb5742f827b5457253b9ad56a9f5e9cba 100644 (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.translate import pass_to_ugettext as _
 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.lib import send_comment_email
 
@@ -268,21 +267,7 @@ def media_confirm_delete(request, media):
     if request.method == 'POST' and form.validate():
         if form.confirm.data is True:
             username = media.get_uploader.username
-
-            # 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.'))
-
+            # Delete MediaEntry and all related files, comments etc.
             media.delete()
             messages.add_message(
                 request, messages.SUCCESS, _('You deleted the media.'))