Skip to content

Commit 08629f5

Browse files
Merge pull request #9 from thewebscraping/dev
Dev
2 parents 37dbfeb + fea016d commit 08629f5

File tree

15 files changed

+151
-59
lines changed

15 files changed

+151
-59
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ docs/_build
5656
.DS_Store
5757
examples/.DS_Store
5858
examples/db.sqlite3
59+
examples/media/

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Django Chunk File Upload is an alternative utility that helps you easily edit Django's chunked, drag and drop file uploads.
44

5-
<img src="https://i.ibb.co/9y2SgmS/f-P5-Or-Gkxk0-Ynj00ct-G.webp" alt="f-P5-Or-Gkxk0-Ynj00ct-G" border="0">
5+
<img src="https://i.ibb.co/9y2SgmS/f-P5-Or-Gkxk0-Ynj00ct-G.webp" alt="f-P5-Or-Gkxk0-Ynj00ct-G">
66

77
Features
88
----------
@@ -76,7 +76,8 @@ DJANGO_CHUNK_FILE_UPLOAD = {
7676
"max_width": 1024,
7777
"max_height": 720,
7878
"to_webp": True, # focus convert image to webp type.
79-
}
79+
},
80+
"permission_classes": ("django_chunk_file_upload.permissions.AllowAny",) # default: IsAuthenticated
8081
}
8182

8283
```
@@ -88,14 +89,14 @@ models.py
8889

8990
```python
9091
from django.db import models
91-
from django_chunk_file_upload.models import FileManager
92+
from django_chunk_file_upload.models import FileManagerMixin
9293

9394

9495
class Tag(models.Model):
9596
name = models.CharField(max_length=255)
9697

9798

98-
class YourModel(FileManager):
99+
class YourModel(FileManagerMixin):
99100
tags = models.ManyToManyField(Tag)
100101
custom_field = models.CharField(max_length=255)
101102

@@ -116,6 +117,7 @@ class YourForm(ChunkedUploadFileForm):
116117

117118
views.py
118119

120+
Accepted methods: GET, POST, DELETE (UPDATE, PUT does not work with FormData).
119121
```python
120122
from django_chunk_file_upload.views import ChunkedUploadView
121123
from django_chunk_file_upload.typed import File
@@ -126,12 +128,18 @@ from .forms import YourForm
126128
class CustomChunkedUploadView(ChunkedUploadView):
127129
form_class = YourForm
128130
permission_classes = (IsAuthenticated,)
131+
129132
# file_class = File # file class
133+
# file_status = app_settings.status # default: PENDING (Used when using background task, you can change it to COMPLETED.)
130134
# optimize = True # default: True
131135
# remove_file_on_update = True # update image on admin page.
132136
# chunk_size = 1024 * 1024 * 2 # custom chunk size upload (default: 2MB).
133137
# upload_to = "custom_folder/%Y/%m/%d" # custom upload folder.
134138
# template_name = "custom_template.html" # custom template
139+
140+
# # Run background task like celery when upload is complete
141+
# def background_task(self, instance):
142+
# pass
135143
```
136144

137145
custom_template.html

django_chunk_file_upload/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class FileManagerModelAdmin(admin.ModelAdmin):
1111
form = ChunkedUploadFileAdminForm
1212
list_display = (
1313
"id",
14+
"name",
1415
"status",
1516
"created_at",
1617
"updated_at",

django_chunk_file_upload/app_settings.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from __future__ import annotations
22

3+
import importlib
34
from dataclasses import dataclass, field, fields
45

56
from django.conf import settings
67

8+
from . import permissions
79
from .constants import StatusChoices
8-
from .permissions import BasePermission, IsAuthenticated
910

1011

1112
@dataclass(kw_only=True)
@@ -49,7 +50,9 @@ class _LazySettings(_Settings):
4950
is_metadata_storage: bool = False
5051
remove_file_on_update: bool = True
5152
status: StatusChoices = StatusChoices.PENDING
52-
permission_classes: tuple[BasePermission] = (IsAuthenticated,)
53+
permission_classes: tuple[permissions.BasePermission] = (
54+
permissions.IsAuthenticated,
55+
)
5356
optimize: bool = True
5457
image_optimizer: _ImageSettings = field(default_factory=_ImageSettings)
5558

@@ -63,6 +66,22 @@ def from_kwargs(cls, **kwargs) -> "_LazySettings":
6366
image_optimizer = kwargs.pop("image_optimizer", {}) or {}
6467
if image_optimizer and isinstance(image_optimizer, dict):
6568
kwargs["image_optimizer"] = _ImageSettings.from_kwargs(**image_optimizer)
69+
70+
permission_classes = kwargs.pop("permission_classes")
71+
if permission_classes and isinstance(
72+
permission_classes, (tuple, list, set, str)
73+
):
74+
if isinstance(permission_classes, str):
75+
permission_classes = [permission_classes]
76+
77+
perms = []
78+
for permission_class in permission_classes:
79+
paths = permission_class.split(".")
80+
module = importlib.import_module(".".join(paths[:-1]), "")
81+
permission_class = getattr(module, paths[-1])
82+
perms.append(permission_class)
83+
84+
kwargs["permission_classes"] = tuple(perms)
6685
return cls(**kwargs)
6786

6887

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.0.7 on 2024-08-28 02:33
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("django_chunk_file_upload", "0002_alter_filemanager_options_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.RenameIndex(
14+
model_name="filemanager",
15+
new_name="filemanager_checksum_idx",
16+
old_name="file_manager_checksum_idx",
17+
),
18+
]

django_chunk_file_upload/models.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
from .constants import StatusChoices, TypeChoices
88

99

10-
class FileManager(models.Model):
10+
class FileManagerMixin(models.Model):
11+
"""File Manager Mixin for Django Models"""
12+
1113
created_at = models.DateTimeField(auto_now_add=True)
1214
updated_at = models.DateTimeField(auto_now=True)
1315
file = models.FileField()
@@ -34,9 +36,25 @@ class FileManager(models.Model):
3436
)
3537
metadata = models.JSONField(default=dict)
3638

39+
class Meta:
40+
abstract = True
41+
42+
def __str__(self):
43+
return self.name
44+
45+
@property
46+
def name(self) -> str:
47+
if "name" in self.metadata and self.metadata["name"]:
48+
return self.metadata["name"]
49+
return self.file.name
50+
51+
52+
class FileManager(FileManagerMixin):
53+
"""File Manager for Django Models"""
54+
3755
class Meta:
3856
db_table = "django_chunk_file_upload"
39-
indexes = [models.Index(fields=["checksum"], name="file_manager_checksum_idx")]
57+
indexes = [models.Index(fields=["checksum"], name="%(class)s_checksum_idx")]
4058
ordering = ("-created_at",)
4159
unique_together = (
4260
"user",

django_chunk_file_upload/permissions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from __future__ import annotations
2+
3+
14
class BasePermission:
25
safe_methods = ("GET", "POST", "DELETE")
36

django_chunk_file_upload/static/js/upload.chunk.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ function deleteFile() {
136136
processData: false,
137137
contentType: false,
138138
error: function (response) {
139-
console.log(response)
140139
let errorMessage = response.statusText;
141140
if (response.responseJSON) {
142141
errorMessage = response.responseJSON.message;

django_chunk_file_upload/templates/django_chunk_file_upload/chunked_upload.html

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% load i18n %}
1+
{% load static i18n %}
22
<!DOCTYPE html>
33
<html lang="en">
44
<head>
@@ -13,30 +13,30 @@
1313
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
1414
crossorigin="anonymous">
1515
<style>
16-
:root {
17-
--input-padding-x: .75rem;
18-
--input-padding-y: .75rem;
19-
}
16+
:root {
17+
--input-padding-x: .75rem;
18+
--input-padding-y: .75rem;
19+
}
2020

21-
html,
22-
body {
23-
height: 100%;
24-
}
21+
html,
22+
body {
23+
height: 100%;
24+
}
2525

26-
body {
27-
display: -ms-flexbox;
28-
display: -webkit-box;
29-
display: flex;
30-
-ms-flex-align: center;
31-
-ms-flex-pack: center;
32-
-webkit-box-align: center;
33-
align-items: center;
34-
-webkit-box-pack: center;
35-
justify-content: center;
36-
padding-top: 40px;
37-
padding-bottom: 40px;
38-
background-color: #f5f5f5;
39-
}
26+
body {
27+
display: -ms-flexbox;
28+
display: -webkit-box;
29+
display: flex;
30+
-ms-flex-align: center;
31+
-ms-flex-pack: center;
32+
-webkit-box-align: center;
33+
align-items: center;
34+
-webkit-box-pack: center;
35+
justify-content: center;
36+
padding-top: 40px;
37+
padding-bottom: 40px;
38+
background-color: #f5f5f5;
39+
}
4040

4141
</style>
4242
{{ form.media }}

django_chunk_file_upload/templates/django_chunk_file_upload/forms/widgets/drag_drop_input.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% load i18n admin_tags %}
1+
{% load i18n %}
22
<div class="dropzone-wrapper">
33
<div class="loader hide"></div>
44
{% if widget.is_initial %}

django_chunk_file_upload/typed.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def file(self) -> Union[InMemoryUploadedFile, TemporaryUploadedFile, None]:
147147

148148
@property
149149
def filename(self) -> str:
150-
return get_filename(self.file.name)
150+
return get_filename(getattr(self.file, "name", "")) or ""
151151

152152
@property
153153
def repl_filename(self) -> str:
@@ -236,11 +236,10 @@ def to_private_attrs():
236236
user = request.user
237237

238238
file = request.FILES.get("file")
239-
extension = get_file_extension(file.name)
240239
pk = make_uuid(user=user, checksum=request.headers.get("x-file-checksum"))
241240
return {
242241
"_id": pk,
243-
"_extension": extension,
242+
"_extension": get_file_extension(file.name) if file else None,
244243
"_file": file,
245244
"_user": user,
246245
"_upload_to": upload_to,
@@ -291,6 +290,7 @@ def to_metadata(self) -> dict:
291290
def to_response(self) -> dict:
292291
metadata = {k: v for k, v in self.to_dict().items() if not k.startswith("_")}
293292
metadata["message"] = str(self.message)
293+
metadata["name"] = self.filename
294294
return metadata
295295

296296
def write(self, mode: str = "ab+"):

django_chunk_file_upload/views.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ def has_change_permission(self, request, obj=None) -> bool:
6262
def has_delete_permission(self, request, obj=None) -> bool:
6363
return self.check_object_permissions(request)
6464

65+
def is_valid(self, form, file_obj) -> bool:
66+
if form.is_valid() and file_obj.is_valid():
67+
return True
68+
return False
69+
6570
def get_model(self):
6671
return self.form_class.Meta.model
6772

@@ -97,18 +102,7 @@ def post(self, request, *args, **kwargs):
97102
def delete(self, request, *args, **kwargs):
98103
"""Override DELETE method from View."""
99104

100-
form, file_obj = self._get_form_file(request, *args, **kwargs)
101-
if self.has_delete_permission(request):
102-
instance = self.get_instance()
103-
if instance and (
104-
self.request.user.is_superuser or self.request.user == instance.user
105-
):
106-
self._delete(instance)
107-
file_obj.message = _("The file deleted successfully.")
108-
return self.ajax_response(None, file_obj, status=200, save=False)
109-
110-
file_obj.message = _("Permission denied.")
111-
return self.ajax_response(None, file_obj, status=400, save=False)
105+
return self._delete(request, *args, **kwargs)
112106

113107
def _get(self, request, *args, **kwargs):
114108
if self.has_view_permission(request):
@@ -117,11 +111,7 @@ def _get(self, request, *args, **kwargs):
117111

118112
def _post(self, request, *args, **kwargs):
119113
form, file_obj = self._get_form_file(request, *args, **kwargs)
120-
if (
121-
self.has_add_permission(self.request)
122-
and file_obj.is_valid()
123-
and form.is_valid()
124-
):
114+
if self.has_add_permission(self.request) and self.is_valid(form, file_obj):
125115
instance = self.get_instance()
126116
return self.chunked_upload(instance, form, file_obj)
127117

@@ -130,7 +120,7 @@ def _post(self, request, *args, **kwargs):
130120

131121
def _update(self, request, *args, **kwargs):
132122
form, file_obj = self._get_form_file(request, *args, **kwargs)
133-
if self.has_change_permission(self.request):
123+
if self.has_change_permission(self.request) and self.is_valid(form, file_obj):
134124
instance = self.get_instance()
135125
if instance:
136126
if self.remove_file_on_update:
@@ -145,10 +135,20 @@ def _update(self, request, *args, **kwargs):
145135
file_obj.message = _("Permission denied.")
146136
return self.ajax_response(None, file_obj, status=400, save=False)
147137

148-
def _delete(self, instance):
149-
if instance:
150-
instance.file.delete()
151-
instance.delete()
138+
def _delete(self, request, *args, **kwargs):
139+
form, file_obj = self._get_form_file(request, *args, **kwargs)
140+
if self.has_delete_permission(request):
141+
instance = self.get_instance()
142+
if instance and (
143+
self.request.user.is_superuser or self.request.user == instance.user
144+
):
145+
instance.file.delete()
146+
instance.delete()
147+
file_obj.message = _("The file deleted successfully.")
148+
return self.ajax_response(None, file_obj, status=200, save=False)
149+
150+
file_obj.message = _("Permission denied.")
151+
return self.ajax_response(None, file_obj, status=400, save=False)
152152

153153
def _get_form_file(
154154
self, request, *args, **kwargs

django_chunk_file_upload/widgets.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class DragDropFileInput(forms.ClearableFileInput):
1111

1212
def get_context(self, name, value, attrs):
1313
context = super().get_context(name, value, attrs)
14+
context["widget"]["attrs"]["required"] = False
1415
context["widget"]["attrs"]["hidden"] = True
1516
context["widget"]["attrs"]["data-id"] = "dropzone"
1617
if value and isinstance(value, (FieldFile, ImageField)):

0 commit comments

Comments
 (0)