Multiple file uploads with identical name cause `MultipleObjectsReturned`
In the good old time we just stored files with their original filename (as it was presented by the client during upload).
Back then we were not careful with handling these files, thus it was possible, that different users/groups uploaded files with the same name (overwriting each other).
Recently we switched to using random filenames for storage (and later retrieval). Now we notice problematic double uploads due to name collisions in requests.
Traceback:
File "/usr/lib/python3/dist-packages/django/core/handlers/exception.py" in inner
34. response = get_response(request)
File "/usr/lib/python3/dist-packages/django/core/handlers/base.py" in _get_response
115. response = self.process_exception_by_middleware(e, request)
File "/usr/lib/python3/dist-packages/django/core/handlers/base.py" in _get_response
113. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/share/grouprise/dependencies/sentry_sdk/integrations/django/views.py" in sentry_wrapped_callback
67. return callback(request, *args, **kwargs)
File "/usr/lib/python3/dist-packages/django_downloadview/decorators.py" in decorated
36. response = view_func(request, *view_args, **view_kwargs)
File "/usr/lib/python3/dist-packages/django/views/generic/base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "/usr/lib/python3/dist-packages/django/contrib/auth/mixins.py" in dispatch
83. if not self.has_permission():
File "/usr/lib/python3/dist-packages/rules/contrib/views.py" in has_permission
48. obj = self.get_permission_object()
File "/usr/lib/python3/dist-packages/rules/contrib/views.py" in get_permission_object
44. return self.get_object()
File "/usr/share/grouprise/python-lib/grouprise/features/files/views.py" in get_object
21. return get_object_or_404(File, file=self.kwargs["name"])
File "/usr/lib/python3/dist-packages/django/shortcuts.py" in get_object_or_404
93. return queryset.get(*args, **kwargs)
File "/usr/lib/python3/dist-packages/django/db/models/query.py" in get
410. raise self.model.MultipleObjectsReturned(
Exception Type: MultipleObjectsReturned at /-/files/20180319_OZ_Auftakt.pdf
Exception Value: get() returned more than one File -- it returned 3!
The logic for migrating the data could be quite simple:
- If a filename is used by more than one object, then copy the file to a new random filename and change the filename in one of these objects (the other remains unchanged).
- Repeating this procedure, until all "copies" are renamed should solve the issue.
We just need to hope, that the remaining instance is not (by accident) a private one (which would cause old direct links to stop working). But the risk is low and the old links should not be treated as permanent ones, anyway.