Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce multiple file upload app enhancements #1

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion api/django-common/django_common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class OwnedModel(models.Model):
A model that has a relationship to the owner in the user model.
"""

owner = models.ForeignKey(get_user_model(), on_delete=models.RESTRICT)
owner = models.ForeignKey(get_user_model(), on_delete=models.RESTRICT, null=True, blank=True)
a1eksb marked this conversation as resolved.
Show resolved Hide resolved

class Meta:
abstract = True
Expand Down
16 changes: 12 additions & 4 deletions api/django-fileupload/django_fileupload/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,29 @@


class FileUploadAdmin(admin.ModelAdmin):
list_display = ("name", "uploaded_by", "uploaded_on", "detected_mime_type", "hr_size", "checksum")
readonly_fields = ("file_upload_batch", "position", "file", "detected_mime_type", "checksum")
list_display = ("name", "uploaded_by", "uploaded_on", "deleted_on", "detected_mime_type", "hr_size", "checksum")
readonly_fields = ("deleted_on", "file_upload_batch", "position", "file", "detected_mime_type", "checksum")

def hr_size(self, file_upload: FileUpload):
return hr_size(file_upload.size)

hr_size.short_description = "Size"

def uploaded_on(self, file_upload: FileUpload):
return dt.strftime(file_upload.batch.uploaded_on, DATE_TIME_FORMAT)
return dt.strftime(file_upload.file_upload_batch.uploaded_on, DATE_TIME_FORMAT)

uploaded_on.short_description = "Uploaded on"

def deleted_on(self, file_upload: FileUpload):
if file_upload.deleted_on:
return dt.strftime(file_upload.deleted_on, DATE_TIME_FORMAT)
else:
return "Not deleted"

deleted_on.short_description = "Deleted on"

def uploaded_by(self, file_upload: FileUpload):
return file_upload.batch.owner
return file_upload.file_upload_batch.owner

uploaded_by.short_description = "Uploaded by"

Expand Down
1 change: 1 addition & 0 deletions api/django-fileupload/django_fileupload/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@

class CoreConfig(AppConfig):
name = "django_fileupload"
verbose_name = "File Uploads"
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 5.0 on 2024-07-02 13:16

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("django_fileupload", "0006_rename_mime_type_fileupload_detected_mime_type"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.AlterField(
model_name="fileuploadbatch",
name="owner",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.RESTRICT,
to=settings.AUTH_USER_MODEL,
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2024-07-04 19:21

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("django_fileupload", "0007_alter_fileuploadbatch_owner"),
]

operations = [
migrations.AddField(
model_name="fileupload",
name="deleted_on",
field=models.DateTimeField(editable=False, null=True),
),
]
1 change: 1 addition & 0 deletions api/django-fileupload/django_fileupload/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class FileUpload(models.Model):
file = models.FileField(upload_to=_generate_complete_file_path, storage=FileUploadFileStorage())
detected_mime_type = models.CharField(max_length=100, editable=False)
checksum = models.CharField(max_length=64, editable=False)
deleted_on = models.DateTimeField(null=True, editable=False)

def __str__(self):
return self.file.path
Expand Down
2 changes: 1 addition & 1 deletion api/django-fileupload/django_fileupload/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class FileUploadSerializer(serializers.ModelSerializer):

class Meta:
model = FileUpload
fields = ("id", "name", "checksum")
fields = ("id", "name", "checksum", "deleted_on")


class FileUploadBatchSerializer(serializers.ModelSerializer):
Expand Down
24 changes: 23 additions & 1 deletion api/django-fileupload/django_fileupload/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from django.utils import timezone

from django_common.postgresql import exclusive_insert_table_lock
from django_common.renderers import PassthroughRenderer
Expand All @@ -26,6 +27,9 @@ class FileUploadBatchViewSet(
queryset = FileUploadBatch.objects.all()
serializer_class = FileUploadBatchSerializer
parser_classes = (MultiPartParser,)
has_owner = True
# Maximum file size in bytes. For example 5 * 1024 * 1024 is 5 MB.
max_file_size = None
a1eksb marked this conversation as resolved.
Show resolved Hide resolved

# Workaround for "drf-yasg" (see https://github.com/axnsan12/drf-yasg/issues/503).
def get_serializer_class(self):
Expand All @@ -50,6 +54,10 @@ def create(self, request, *args, **kwargs):
files = request.FILES.getlist("files")
if self.verify_file_count(request, len(files)):
for file_position, file in enumerate(files):

if self.max_file_size and file.size > self.max_file_size:
raise ValidationError(_(f"File size exceeds the maximum allowed size of {self.max_file_size / (1024 ** 2)} MB."))
a1eksb marked this conversation as resolved.
Show resolved Hide resolved

file_name_parts = os.path.splitext(file.name)
if self.verify_file_extension(request, file_position, file_name_parts):
if self.verify_file_checksum(request, file_position, file_name_parts,
Expand All @@ -59,7 +67,10 @@ def create(self, request, *args, **kwargs):
raise ValidationError(_("Files with incorrect extension in the request."))
response = []
with exclusive_insert_table_lock(FileUploadBatch):
file_upload_batch = FileUploadBatch.objects.create(owner=request.user)
a1eksb marked this conversation as resolved.
Show resolved Hide resolved
if self.has_owner:
file_upload_batch = FileUploadBatch.objects.create(owner=request.user)
else:
file_upload_batch = FileUploadBatch.objects.create()
a1eksb marked this conversation as resolved.
Show resolved Hide resolved
# Metadata needs to be added here as FileUpload.objects.create(...) may depend on it.
self.add_metadata(request, file_upload_batch)
for file_position, file in enumerate(files):
Expand Down Expand Up @@ -96,4 +107,15 @@ class FileUploadViewSet(
mixins.DestroyModelMixin,
viewsets.GenericViewSet,
):
keep_after_deletion = False
a1eksb marked this conversation as resolved.
Show resolved Hide resolved

def destroy(self, request, *args, **kwargs):
file_upload = self.get_object()
if self.keep_after_deletion:
file_upload.deleted_on = timezone.now()
a1eksb marked this conversation as resolved.
Show resolved Hide resolved
file_upload.save()
else:
file_upload.delete()

return Response(status=status.HTTP_204_NO_CONTENT)
pass