From 88fb87ebae26178c26b743ac4c07983c58e12a3d Mon Sep 17 00:00:00 2001 From: Alexandr Karpov Date: Thu, 6 Apr 2023 13:34:08 +0300 Subject: [PATCH] fixed chunked upload --- .../0002_alter_chunkedupload_file.py | 22 +++++++ akarpov/contrib/chunked_upload/settings.py | 4 +- akarpov/contrib/chunked_upload/views.py | 5 +- ...eted_on_remove_file_created_on_and_more.py | 37 ++++++++++++ akarpov/files/migrations/0010_fileintrash.py | 60 +++++++++++++++++++ akarpov/files/models.py | 8 ++- akarpov/files/views.py | 29 +++++---- 7 files changed, 147 insertions(+), 18 deletions(-) create mode 100644 akarpov/contrib/chunked_upload/migrations/0002_alter_chunkedupload_file.py create mode 100644 akarpov/files/migrations/0009_remove_file_completed_on_remove_file_created_on_and_more.py create mode 100644 akarpov/files/migrations/0010_fileintrash.py diff --git a/akarpov/contrib/chunked_upload/migrations/0002_alter_chunkedupload_file.py b/akarpov/contrib/chunked_upload/migrations/0002_alter_chunkedupload_file.py new file mode 100644 index 0000000..d880309 --- /dev/null +++ b/akarpov/contrib/chunked_upload/migrations/0002_alter_chunkedupload_file.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2 on 2023-04-06 10:14 + +import akarpov.contrib.chunked_upload.settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("chunked_upload", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="chunkedupload", + name="file", + field=models.FileField( + max_length=255, + upload_to=akarpov.contrib.chunked_upload.settings.default_upload_to, + ), + ), + ] diff --git a/akarpov/contrib/chunked_upload/settings.py b/akarpov/contrib/chunked_upload/settings.py index df4f325..23f02b5 100644 --- a/akarpov/contrib/chunked_upload/settings.py +++ b/akarpov/contrib/chunked_upload/settings.py @@ -6,8 +6,6 @@ from django.core.serializers.json import DjangoJSONEncoder from django.utils.module_loading import import_string -from akarpov.utils.files import user_file_upload_mixin - # How long after creation the upload will expire DEFAULT_EXPIRATION_DELTA = timedelta(days=1) EXPIRATION_DELTA = getattr( @@ -25,7 +23,7 @@ def default_upload_to(instance, filename): return time.strftime(filename) -UPLOAD_TO = user_file_upload_mixin +UPLOAD_TO = default_upload_to # Storage system diff --git a/akarpov/contrib/chunked_upload/views.py b/akarpov/contrib/chunked_upload/views.py index 0ceeb89..e1fcb5d 100644 --- a/akarpov/contrib/chunked_upload/views.py +++ b/akarpov/contrib/chunked_upload/views.py @@ -5,6 +5,7 @@ from django.utils import timezone from django.views.generic import View +from ...utils.generators import generate_charset from .constants import COMPLETE, http_status from .exceptions import ChunkedUploadError from .models import ChunkedUpload @@ -154,7 +155,9 @@ def create_chunked_upload(self, save=False, **attrs): """ chunked_upload = self.model(**attrs) # file starts empty - chunked_upload.file.save(name="", content=ContentFile(""), save=save) + chunked_upload.file.save( + name=generate_charset(10), content=ContentFile(""), save=save + ) return chunked_upload def is_valid_chunked_upload(self, chunked_upload): diff --git a/akarpov/files/migrations/0009_remove_file_completed_on_remove_file_created_on_and_more.py b/akarpov/files/migrations/0009_remove_file_completed_on_remove_file_created_on_and_more.py new file mode 100644 index 0000000..61d2f2d --- /dev/null +++ b/akarpov/files/migrations/0009_remove_file_completed_on_remove_file_created_on_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2 on 2023-04-06 10:14 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("files", "0008_file_completed_on_file_created_on_file_filename_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="file", + name="completed_on", + ), + migrations.RemoveField( + model_name="file", + name="created_on", + ), + migrations.RemoveField( + model_name="file", + name="filename", + ), + migrations.RemoveField( + model_name="file", + name="offset", + ), + migrations.RemoveField( + model_name="file", + name="status", + ), + migrations.RemoveField( + model_name="file", + name="upload_id", + ), + ] diff --git a/akarpov/files/migrations/0010_fileintrash.py b/akarpov/files/migrations/0010_fileintrash.py new file mode 100644 index 0000000..eb5440b --- /dev/null +++ b/akarpov/files/migrations/0010_fileintrash.py @@ -0,0 +1,60 @@ +# Generated by Django 4.2 on 2023-04-06 10:25 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import model_utils.fields + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("files", "0009_remove_file_completed_on_remove_file_created_on_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="FileInTrash", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="created", + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + editable=False, + verbose_name="modified", + ), + ), + ("file", models.FileField(upload_to="file/trash/")), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="trash_files", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/akarpov/files/models.py b/akarpov/files/models.py index 815cbf0..293875a 100644 --- a/akarpov/files/models.py +++ b/akarpov/files/models.py @@ -10,12 +10,11 @@ from django.urls import reverse from model_utils.models import TimeStampedModel -from akarpov.contrib.chunked_upload.models import AbstractChunkedUpload from akarpov.tools.shortener.models import ShortLink from akarpov.utils.files import user_file_upload_mixin -class File(AbstractChunkedUpload, TimeStampedModel, ShortLink): +class File(TimeStampedModel, ShortLink): """model to store user's files""" name = CharField(max_length=100) @@ -38,6 +37,11 @@ def __str__(self): return f"file: {self.name}" +class FileInTrash(TimeStampedModel): + user = ForeignKey("users.User", related_name="trash_files", on_delete=CASCADE) + file = FileField(blank=False, upload_to="file/trash/") + + class Folder(TimeStampedModel, ShortLink): name = CharField(max_length=100) slug = SlugField(max_length=20, blank=True) diff --git a/akarpov/files/views.py b/akarpov/files/views.py index de96583..5c5dc20 100644 --- a/akarpov/files/views.py +++ b/akarpov/files/views.py @@ -1,6 +1,10 @@ +import os + from django.views.generic import DetailView from django.views.generic.base import TemplateView +from akarpov.contrib.chunked_upload.exceptions import ChunkedUploadError +from akarpov.contrib.chunked_upload.models import ChunkedUpload from akarpov.contrib.chunked_upload.views import ( ChunkedUploadCompleteView, ChunkedUploadView, @@ -31,28 +35,29 @@ class ChunkedUploadDemo(TemplateView): class MyChunkedUploadView(ChunkedUploadView): - model = File + model = ChunkedUpload field_name = "the_file" def check_permissions(self, request): - # Allow non authenticated users to make uploads - pass + if not self.request.user.is_authenticated: + raise ChunkedUploadError( + 403, message="you are not allowed to access this page" + ) class MyChunkedUploadCompleteView(ChunkedUploadCompleteView): - model = File + model = ChunkedUpload def check_permissions(self, request): - # Allow non authenticated users to make uploads - pass + if not self.request.user.is_authenticated: + raise ChunkedUploadError( + 403, message="you are not allowed to access this page" + ) def on_completion(self, uploaded_file, request): - # Do something with the uploaded file. E.g.: - # * Store the uploaded file on another model: - # SomeModel.objects.create(user=request.user, file=uploaded_file) - # * Pass it as an argument to a function: - # function_that_process_file(uploaded_file) - pass + File.objects.create(user=request.user, file=uploaded_file) + if os.path.isfile(uploaded_file.file.path): + os.remove(uploaded_file.file.path) def get_response_data(self, chunked_upload, request): return {