Skip to content

Commit

Permalink
Merge pull request #1715 from MTG/user_deletion_fixes
Browse files Browse the repository at this point in the history
Update user delete method to remove sounds in chunks
ffont authored Dec 14, 2023
2 parents 6ad7564 + 72ea8b4 commit dc07d3a
Showing 3 changed files with 41 additions and 12 deletions.
24 changes: 16 additions & 8 deletions accounts/models.py
Original file line number Diff line number Diff line change
@@ -50,6 +50,7 @@
from ratings.models import SoundRating
from sounds.models import DeletedSound, License, Sound, Pack, Download, PackDownload, BulkUploadProgress
from tags.models import TaggedItem
from utils.chunks import chunks
from utils.locations import locations_decorator
from utils.mail import transform_unique_email
from utils.search import get_search_engine, SearchEngineException
@@ -449,7 +450,8 @@ def get_info_before_delete_user(self, include_sounds=False, include_other_relate

def delete_user(self, remove_sounds=False,
delete_user_object_from_db=False,
deletion_reason=DeletedUser.DELETION_REASON_DELETED_BY_ADMIN):
deletion_reason=DeletedUser.DELETION_REASON_DELETED_BY_ADMIN,
chunk_size=500):
"""
Custom method for deleting a user from Freesound.
@@ -475,9 +477,20 @@ def delete_user(self, remove_sounds=False,
with all related content. Defaults to False.
deletion_reason (str): reason for the user being deleted. Should be one of the choices defined in
DeletedUser.DELETION_REASON_CHOICES. Defaults to DeletedUser.DELETION_REASON_DELETED_BY_ADMIN.
chunk_size (int): size of the chunks in which sounds will be deleted inside atomic trasactions.
"""

# Run all deletion operations in a single transaction so if there is an error we don't create duplicate
# If required, start by deleting all user's sounds and packs
if remove_sounds:
Pack.objects.filter(user=self.user).update(is_deleted=True)
# Itrate sounds in chunks
sound_ids = Sound.objects.filter(user=self.user).values_list('id', flat=True)
for chunk_ids in chunks(sound_ids, chunk_size):
with transaction.atomic():
Sound.objects.filter(id__in=chunk_ids).delete()

# Now run all deletion operations related to the user (except for sounds/packs).
# Run them in a single transaction so if there is an error we don't create duplicate
# DeletedUser objects
with transaction.atomic():

@@ -531,14 +544,9 @@ def delete_user(self, remove_sounds=False,
# Remove existing OldUsername objects so there are no redirects to the anonymized/deleted user page
OldUsername.objects.filter(user=self.user).delete()

# Remove sounds and packs if requested
if remove_sounds:
Sound.objects.filter(user=self.user).delete()
Pack.objects.filter(user=self.user).update(is_deleted=True)
else:
if not remove_sounds:
Sound.objects.filter(user=self.user).update(is_index_dirty=True)


# If UserDeletionRequest object(s) exist for that user, update the status and set deleted_user property
# NOTE: don't use QuerySet.update method because this won't trigger the pre_save/post_save signals
for udr in UserDeletionRequest.objects.filter(id__in=udr_to_update_ids):
5 changes: 1 addition & 4 deletions general/management/commands/prune_database.py
Original file line number Diff line number Diff line change
@@ -47,14 +47,11 @@
from sounds.models import BulkUploadProgress, DeletedSound, Flag
import tickets
from tickets.models import Ticket, TicketComment
from utils.chunks import chunks

console_logger = logging.getLogger('console')


def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]


class Command(BaseCommand):
24 changes: 24 additions & 0 deletions utils/chunks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#
# Freesound is (c) MUSIC TECHNOLOGY GROUP, UNIVERSITAT POMPEU FABRA
#
# Freesound is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Freesound is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# See AUTHORS file.
#

def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]

0 comments on commit dc07d3a

Please sign in to comment.