From fe76ca68aa1846c66cf7ce763af6332d2b7585b6 Mon Sep 17 00:00:00 2001 From: dzhuang Date: Mon, 22 Apr 2024 12:03:22 +0800 Subject: [PATCH 1/5] Added two_fields_in_one_model demo. --- demo/settings.py | 3 +- demo/urls.py | 5 +- demo_multiple_fields_1_model/__init__.py | 0 demo_multiple_fields_1_model/admin.py | 1 + demo_multiple_fields_1_model/apps.py | 9 +++ demo_multiple_fields_1_model/forms.py | 0 demo_multiple_fields_1_model/image_views.py | 78 +++++++++++++++++++ .../migrations/__init__.py | 0 demo_multiple_fields_1_model/models.py | 58 ++++++++++++++ demo_multiple_fields_1_model/receivers.py | 0 .../demo_multiple_fields_1_model/.gitkeep | 0 demo_multiple_fields_1_model/urls.py | 35 +++++++++ demo_multiple_fields_1_model/views.py | 0 13 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 demo_multiple_fields_1_model/__init__.py create mode 100644 demo_multiple_fields_1_model/admin.py create mode 100644 demo_multiple_fields_1_model/apps.py create mode 100644 demo_multiple_fields_1_model/forms.py create mode 100644 demo_multiple_fields_1_model/image_views.py create mode 100644 demo_multiple_fields_1_model/migrations/__init__.py create mode 100644 demo_multiple_fields_1_model/models.py create mode 100644 demo_multiple_fields_1_model/receivers.py create mode 100644 demo_multiple_fields_1_model/templates/demo_multiple_fields_1_model/.gitkeep create mode 100644 demo_multiple_fields_1_model/urls.py create mode 100644 demo_multiple_fields_1_model/views.py diff --git a/demo/settings.py b/demo/settings.py index e9e85f7..0804498 100644 --- a/demo/settings.py +++ b/demo/settings.py @@ -39,7 +39,8 @@ 'galleryfield', 'sorl.thumbnail', 'demo', - 'demo_custom' + 'demo_custom', + 'demo_multiple_fields_1_model' ] MIDDLEWARE = [ diff --git a/demo/urls.py b/demo/urls.py index 0d2ca8a..90ed069 100644 --- a/demo/urls.py +++ b/demo/urls.py @@ -16,6 +16,9 @@ path('admin/', admin.site.urls), ] -urlpatterns += [path(r"custom/", include("demo_custom.urls"))] +urlpatterns += [ + path(r"custom/", include("demo_custom.urls")), + path(r"multifields/", include("demo_multiple_fields_1_model.urls")), +] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/demo_multiple_fields_1_model/__init__.py b/demo_multiple_fields_1_model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/demo_multiple_fields_1_model/admin.py b/demo_multiple_fields_1_model/admin.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/demo_multiple_fields_1_model/admin.py @@ -0,0 +1 @@ + diff --git a/demo_multiple_fields_1_model/apps.py b/demo_multiple_fields_1_model/apps.py new file mode 100644 index 0000000..3ced1ba --- /dev/null +++ b/demo_multiple_fields_1_model/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + + +class DemoCustomConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'demo_multiple_fields_1_model' + + def ready(self): + import demo_multiple_fields_1_model.receivers # noqa diff --git a/demo_multiple_fields_1_model/forms.py b/demo_multiple_fields_1_model/forms.py new file mode 100644 index 0000000..e69de29 diff --git a/demo_multiple_fields_1_model/image_views.py b/demo_multiple_fields_1_model/image_views.py new file mode 100644 index 0000000..d858d49 --- /dev/null +++ b/demo_multiple_fields_1_model/image_views.py @@ -0,0 +1,78 @@ +from django.core.exceptions import PermissionDenied + +from galleryfield.image_views import (ImageCreateView, ImageCropView, + ImageListView) + + +class MyImage1CreateView(ImageCreateView): + target_model = "demo_multiple_fields_1_model.MyImage1" + disable_server_side_crop = False + + def create_instance_from_form(self, form): + self.object = form.save(commit=False) + self.object.creator = self.request.user + self.object.save() + + +class MyImage1ListView(ImageListView): + target_model = "event.MyImage1" + disable_server_side_crop = False + + def get_queryset(self): + queryset = super().get_queryset() + if not self.request.user.is_superuser: + queryset = queryset.filter(creator=self.request.user) + return queryset + + +class MyImage1CropView(ImageCropView): + target_model = "demo_multiple_fields_1_model.MyImage1" + disable_server_side_crop = False + + def get_object(self, queryset=None): + obj = super().get_object(queryset=None) + if not self.request.user.is_superuser and obj.creator != self.request.user: + raise PermissionDenied("May not crop other user's image") + return obj + + def create_cropped_instance_from_form(self, form): + # we don't need to set self.object.creator here because + # it's copied from the original instance. + self.object = form.save() + + +class MyImage2CreateView(ImageCreateView): + target_model = "demo_multiple_fields_1_model.MyImage2" + disable_server_side_crop = False + + def create_instance_from_form(self, form): + self.object = form.save(commit=False) + self.object.creator = self.request.user + self.object.save() + + +class MyImage2ListView(ImageListView): + target_model = "demo_multiple_fields_1_model.MyImage2" + disable_server_side_crop = False + + def get_queryset(self): + queryset = super().get_queryset() + if not self.request.user.is_superuser: + queryset = queryset.filter(creator=self.request.user) + return queryset + + +class MyImage2CropView(ImageCropView): + target_model = "demo_multiple_fields_1_model.MyImage2" + disable_server_side_crop = False + + def get_object(self, queryset=None): + obj = super().get_object(queryset=None) + if not self.request.user.is_superuser and obj.creator != self.request.user: + raise PermissionDenied("May not crop other user's image") + return obj + + def create_cropped_instance_from_form(self, form): + # we don't need to set self.object.creator here because + # it's copied from the original instance. + self.object = form.save() diff --git a/demo_multiple_fields_1_model/migrations/__init__.py b/demo_multiple_fields_1_model/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/demo_multiple_fields_1_model/models.py b/demo_multiple_fields_1_model/models.py new file mode 100644 index 0000000..3757314 --- /dev/null +++ b/demo_multiple_fields_1_model/models.py @@ -0,0 +1,58 @@ +from django.conf import settings +from django.core.files.storage import default_storage +from django.db import models +from django.utils.timezone import now +from django.utils.translation import gettext_lazy as _ +from PIL import Image + +from galleryfield.fields import GalleryField + + +def fileName(image): + file_url = list(image.url.split("/")) + file_url.reverse() + file_name = file_url[0].split(".")[0] + return file_name + + +class MyImage1(models.Model): + photo1 = models.ImageField( + upload_to="my_images1", storage=default_storage, verbose_name=_("Image1") + ) + creator = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=False, + blank=False, + verbose_name=_("Creator"), + on_delete=models.CASCADE, + ) + + @classmethod + def get_image_field(cls): + return cls._meta.get_field("photo1") + + +class MyImage2(models.Model): + photo2 = models.ImageField( + upload_to="my_images2", storage=default_storage, verbose_name=_("Image2") + ) + + @classmethod + def get_image_field(cls): + return cls._meta.get_field("photo2") + + +class MyGallery(models.Model): + album1 = GalleryField( + target_model="demo_multiple_fields_1_model.MyImage1", + verbose_name=_("My photos")) + album2 = GalleryField( + target_model="demo_multiple_fields_1_model.MyImage2", + verbose_name=_("My photos2")) + owner = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=False, + blank=False, + verbose_name=_("Owner"), + on_delete=models.CASCADE, + ) diff --git a/demo_multiple_fields_1_model/receivers.py b/demo_multiple_fields_1_model/receivers.py new file mode 100644 index 0000000..e69de29 diff --git a/demo_multiple_fields_1_model/templates/demo_multiple_fields_1_model/.gitkeep b/demo_multiple_fields_1_model/templates/demo_multiple_fields_1_model/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/demo_multiple_fields_1_model/urls.py b/demo_multiple_fields_1_model/urls.py new file mode 100644 index 0000000..faa51cf --- /dev/null +++ b/demo_multiple_fields_1_model/urls.py @@ -0,0 +1,35 @@ +from demo_multiple_fields_1_model import image_views +from django.urls import path + +urlpatterns = [ + path( + "upload1/", + image_views.MyImage1CreateView.as_view(), + name="event-myimage1-upload", + ), + path( + "fetch1/", + (image_views.MyImage1ListView.as_view()), + name="event-myimage1-fetch", + ), + path( + "crop1/", + image_views.MyImage1CropView.as_view(), + name="event-myimage1-crop", + ), + path( + "upload2/", + image_views.MyImage2CreateView.as_view(), + name="event-myimage2-upload", + ), + path( + "fetch2/", + (image_views.MyImage2ListView.as_view()), + name="event-myimage2-fetch", + ), + path( + "crop2/", + image_views.MyImage2CropView.as_view(), + name="event-myimage2-crop", + ), +] diff --git a/demo_multiple_fields_1_model/views.py b/demo_multiple_fields_1_model/views.py new file mode 100644 index 0000000..e69de29 From 7b91ba5cc5e8225e6d6a8913d116fe2c2283a25b Mon Sep 17 00:00:00 2001 From: dzhuang Date: Mon, 22 Apr 2024 18:24:57 +0800 Subject: [PATCH 2/5] Added admin.py and fixed urls. --- demo_multiple_fields_1_model/admin.py | 18 ++++++++++++++++++ demo_multiple_fields_1_model/urls.py | 12 ++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/demo_multiple_fields_1_model/admin.py b/demo_multiple_fields_1_model/admin.py index 8b13789..ddf7c72 100644 --- a/demo_multiple_fields_1_model/admin.py +++ b/demo_multiple_fields_1_model/admin.py @@ -1 +1,19 @@ +from django import forms +from django.contrib import admin + +from demo_multiple_fields_1_model.models import MyGallery +from galleryfield.mixins import GalleryFormMediaMixin + + +class MyGalleryAdminForm(GalleryFormMediaMixin, forms.ModelForm): + class Meta: + model = MyGallery + exclude = () + + +class MyGalleryAdmin(admin.ModelAdmin): + form = MyGalleryAdminForm + + +admin.site.register(MyGallery, MyGalleryAdmin) diff --git a/demo_multiple_fields_1_model/urls.py b/demo_multiple_fields_1_model/urls.py index faa51cf..ca07827 100644 --- a/demo_multiple_fields_1_model/urls.py +++ b/demo_multiple_fields_1_model/urls.py @@ -5,31 +5,31 @@ path( "upload1/", image_views.MyImage1CreateView.as_view(), - name="event-myimage1-upload", + name="demo_multiple_fields_1_model-myimage1-upload", ), path( "fetch1/", (image_views.MyImage1ListView.as_view()), - name="event-myimage1-fetch", + name="demo_multiple_fields_1_model-myimage1-fetch", ), path( "crop1/", image_views.MyImage1CropView.as_view(), - name="event-myimage1-crop", + name="demo_multiple_fields_1_model-myimage1-crop", ), path( "upload2/", image_views.MyImage2CreateView.as_view(), - name="event-myimage2-upload", + name="demo_multiple_fields_1_model-myimage2-upload", ), path( "fetch2/", (image_views.MyImage2ListView.as_view()), - name="event-myimage2-fetch", + name="demo_multiple_fields_1_model-myimage2-fetch", ), path( "crop2/", image_views.MyImage2CropView.as_view(), - name="event-myimage2-crop", + name="demo_multiple_fields_1_model-myimage2-crop", ), ] From e2bc88697cf147effb0d0a4be3f1eef09665dcfc Mon Sep 17 00:00:00 2001 From: dzhuang Date: Mon, 22 Apr 2024 18:28:13 +0800 Subject: [PATCH 3/5] isort code. --- demo_multiple_fields_1_model/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo_multiple_fields_1_model/admin.py b/demo_multiple_fields_1_model/admin.py index ddf7c72..2c9fe79 100644 --- a/demo_multiple_fields_1_model/admin.py +++ b/demo_multiple_fields_1_model/admin.py @@ -1,7 +1,7 @@ +from demo_multiple_fields_1_model.models import MyGallery from django import forms from django.contrib import admin -from demo_multiple_fields_1_model.models import MyGallery from galleryfield.mixins import GalleryFormMediaMixin From a7d57fbd205da837d316fa439fdbfa045bafeac6 Mon Sep 17 00:00:00 2001 From: dzhuang Date: Mon, 22 Apr 2024 22:54:14 +0800 Subject: [PATCH 4/5] Allow multiple Galleryfields per model. --- galleryfield/templates/galleryfield/widget.html | 8 ++++---- galleryfield/widgets.py | 5 +---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/galleryfield/templates/galleryfield/widget.html b/galleryfield/templates/galleryfield/widget.html index 5236eca..c0789fc 100644 --- a/galleryfield/templates/galleryfield/widget.html +++ b/galleryfield/templates/galleryfield/widget.html @@ -293,9 +293,8 @@ {% endif %} - $('.gallery-widget').each(function () { - const that = $(this), - $fileupload = that, + $(function () { + const $fileupload = $("#id-{{ name }}-gallery-widget"), fileInput = $('#{{ name }}-files'), uploadURL = fileInput.data('action'), sortableOptions = { @@ -306,6 +305,7 @@ singleFileUploads: true, url: uploadURL, type: 'POST', + hiddenFileInput: 'input#id_{{ name }}', messages: { maxFileSize: '{% trans "File is too big" %}', minFileSize: '{% trans "File is too small" %}', @@ -354,7 +354,7 @@ // disable change alert when submit form. if (typeof before_submit !== 'undefined') { - that.closest("form").on("submit", before_submit); + $fileupload.closest("form").on("submit", before_submit); } }); diff --git a/galleryfield/widgets.py b/galleryfield/widgets.py index 041579b..b3c5fc2 100644 --- a/galleryfield/widgets.py +++ b/galleryfield/widgets.py @@ -345,10 +345,7 @@ def get_stringfied_jquery_file_upload_ui_options(self): _width, _height = self.thumbnail_size.split("x") ui_options.update( {"previewMaxWidth": int(_width), - "previewMaxHeight": int(_height), - - # This is used as a CSS selector to fine the input field - "hiddenFileInput": f".{conf.FILES_FIELD_CLASS_NAME}", + "previewMaxHeight": int(_height) }) # Compatibility with Bootstrap 4 and 5 From 025a898bd03dbb7219cfa994bfcc177679b45231 Mon Sep 17 00:00:00 2001 From: dzhuang Date: Tue, 23 Apr 2024 00:46:31 +0800 Subject: [PATCH 5/5] Fix demo_multiple_fields_1_model --- demo_multiple_fields_1_model/apps.py | 2 +- demo_multiple_fields_1_model/image_views.py | 2 +- .../migrations/0001_initial.py | 43 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 demo_multiple_fields_1_model/migrations/0001_initial.py diff --git a/demo_multiple_fields_1_model/apps.py b/demo_multiple_fields_1_model/apps.py index 3ced1ba..1aed434 100644 --- a/demo_multiple_fields_1_model/apps.py +++ b/demo_multiple_fields_1_model/apps.py @@ -1,7 +1,7 @@ from django.apps import AppConfig -class DemoCustomConfig(AppConfig): +class DemoMultipleConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'demo_multiple_fields_1_model' diff --git a/demo_multiple_fields_1_model/image_views.py b/demo_multiple_fields_1_model/image_views.py index d858d49..1c72214 100644 --- a/demo_multiple_fields_1_model/image_views.py +++ b/demo_multiple_fields_1_model/image_views.py @@ -15,7 +15,7 @@ def create_instance_from_form(self, form): class MyImage1ListView(ImageListView): - target_model = "event.MyImage1" + target_model = "demo_multiple_fields_1_model.MyImage1" disable_server_side_crop = False def get_queryset(self): diff --git a/demo_multiple_fields_1_model/migrations/0001_initial.py b/demo_multiple_fields_1_model/migrations/0001_initial.py new file mode 100644 index 0000000..aef6de1 --- /dev/null +++ b/demo_multiple_fields_1_model/migrations/0001_initial.py @@ -0,0 +1,43 @@ +# Generated by Django 4.0.6 on 2024-04-22 03:51 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + +import galleryfield.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='MyImage2', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('photo2', models.ImageField(upload_to='my_images2', verbose_name='Image2')), + ], + ), + migrations.CreateModel( + name='MyImage1', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('photo1', models.ImageField(upload_to='my_images1', verbose_name='Image1')), + ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Creator')), + ], + ), + migrations.CreateModel( + name='MyGallery', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('album1', galleryfield.fields.GalleryField(target_model='demo_multiple_fields_1_model.MyImage1', verbose_name='My photos')), + ('album2', galleryfield.fields.GalleryField(target_model='demo_multiple_fields_1_model.MyImage2', verbose_name='My photos2')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner')), + ], + ), + ]