Skip to content

Commit 9c7f91a

Browse files
Merge pull request #8 from thewebscraping/chore/fix-permission-and-remove-admin-tags
chore: fix permissions, remove admin tags on template
2 parents 82b9270 + 22ddab4 commit 9c7f91a

File tree

12 files changed

+130
-34
lines changed

12 files changed

+130
-34
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: 54 additions & 5 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
----------
@@ -12,6 +12,8 @@ Features
1212
- Chunked uploads: optimizing large file transfers.
1313
- Prevent uploading existing files with MD5 checksum.
1414
- Easy to use any models.
15+
- Image optimizer, resizer, auto convert to webp (supported webp, png, jpg, jpeg).
16+
- Permissions.
1517

1618

1719
Quickstart
@@ -57,15 +59,24 @@ Change default config: `settings.py`
5759
DJANGO_CHUNK_FILE_UPLOAD = {
5860
"chunk_size": 1024 * 1024 * 2, # # custom chunk size upload (default: 2MB).
5961
"upload_to": "custom_folder/%Y/%m/%d", # custom upload folder.
60-
"is_metadata_storage": True, # save file metadata
62+
"is_metadata_storage": True, # save file metadata,
63+
"remove_file_on_update": True,
64+
"optimize": True,
6165
"js": (
6266
"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js",
6367
"https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.2/spark-md5.min.js",
6468
"https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js",
6569
), # use cdn.
6670
"css": (
6771
"custom.css"
68-
) # custom css path.
72+
), # custom css path.
73+
"image_optimizer": {
74+
"quality": 82,
75+
"compress_level": 9,
76+
"max_width": 1024,
77+
"max_height": 720,
78+
"to_webp": True, # focus convert image to webp type.
79+
}
6980
}
7081

7182
```
@@ -77,14 +88,14 @@ models.py
7788

7889
```python
7990
from django.db import models
80-
from django_chunk_file_upload.models import FileManager
91+
from django_chunk_file_upload.models import FileManagerMixin
8192

8293

8394
class Tag(models.Model):
8495
name = models.CharField(max_length=255)
8596

8697

87-
class YourModel(FileManager):
98+
class YourModel(FileManagerMixin):
8899
tags = models.ManyToManyField(Tag)
89100
custom_field = models.CharField(max_length=255)
90101

@@ -105,16 +116,29 @@ class YourForm(ChunkedUploadFileForm):
105116

106117
views.py
107118

119+
Accepted methods: GET, POST, DELETE (UPDATE, PUT does not work with FormData).
108120
```python
109121
from django_chunk_file_upload.views import ChunkedUploadView
122+
from django_chunk_file_upload.typed import File
123+
from django_chunk_file_upload.permissions import IsAuthenticated
110124
from .forms import YourForm
111125

112126

113127
class CustomChunkedUploadView(ChunkedUploadView):
114128
form_class = YourForm
129+
permission_classes = (IsAuthenticated,)
130+
131+
# file_class = File # file class
132+
# file_status = app_settings.status # default: PENDING (Used when using background task, you can change it to COMPLETED.)
133+
# optimize = True # default: True
134+
# remove_file_on_update = True # update image on admin page.
115135
# chunk_size = 1024 * 1024 * 2 # custom chunk size upload (default: 2MB).
116136
# upload_to = "custom_folder/%Y/%m/%d" # custom upload folder.
117137
# template_name = "custom_template.html" # custom template
138+
139+
# # Run background task like celery when upload is complete
140+
# def background_task(self, instance):
141+
# pass
118142
```
119143

120144
custom_template.html
@@ -139,4 +163,29 @@ urlpatterns = [
139163
]
140164
```
141165

166+
### Permissions
167+
```python
168+
from django_chunk_file_upload.permissions import AllowAny, IsAuthenticated, IsAdminUser, IsSuperUser
169+
```
170+
171+
### File Handlers
172+
```python
173+
from django_chunk_file_upload.typed import (
174+
ArchiveFile,
175+
AudioFile,
176+
BinaryFile,
177+
DocumentFile,
178+
File,
179+
FontFile,
180+
HyperTextFile,
181+
ImageFile,
182+
JSONFile,
183+
MicrosoftExcelFile,
184+
MicrosoftPowerPointFile,
185+
MicrosoftWordFile,
186+
SeparatedFile,
187+
XMLFile,
188+
)
189+
```
190+
142191
This package is under development, only supports create view. There are also no features related to image optimization. Use at your own risk.

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",
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/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

0 commit comments

Comments
 (0)