dzhuang / django-galleryfield

Django GalleryField with AJAX form widgets for multiple images upload with progress bar and arbitrarily ordering.
MIT License
34 stars 6 forks source link

Use two gallery field in one model #59

Closed sphrlbd closed 5 months ago

sphrlbd commented 5 months ago

Hi, how can I use two gallery field in one model when I do that, all the files will be uploaded in one filed

dzhuang commented 5 months ago

To successfully utilize two GalleryField instances within a single Django model, it's essential to first establish two distinct ImageModel classes. Here's a step-by-step guide using an example app named my_app:

  1. Define the Image Models:

Create two separate models for the images, each with its ImageField. This ensures each GalleryField can reference its specific image model. Crucially, include the get_image_field class method to specify the image field to be used.


from django.db import models
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from django.utils.timezone import now
from galleryfield.models import GalleryField
from django.core.files.storage import default_storage

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)
     creation_time = models.DateTimeField(default=now(), blank=False)

     @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")

MyImage1 Model: Configures photo1 with upload_to path my_images1. MyImage2 Model: Configures photo2 with upload_to path my_images2. Both models should implement the get_image_field method to return their respective image fields. This method is key for the GalleryField to identify the correct image field within the model.

  1. Integrate with the Gallery Model:

In your gallery model, incorporate the two GalleryField instances. Each should reference one of the image models you've defined (MyImage1 and MyImage2). This setup allows each GalleryField to manage a separate set of images.

class MyGallery(models.Model):
    album1 = GalleryField(target_model="my_app.MyImage1", verbose_name=_('My photos'))
    album2 = GalleryField(target_model="my_app.MyImage2", verbose_name=_('My photos2'))
    owner = models.ForeignKey(
            settings.AUTH_USER_MODEL, null=False, blank=False,
                    verbose_name=_('Owner'), on_delete=models.CASCADE)
sphrlbd commented 5 months ago

Thanks. should i configure anything else like urls?

dzhuang commented 5 months ago

should i configure anything else like urls?

urls for what? I you only need to render the images, no need to configure urls.

sphrlbd commented 5 months ago

should i configure anything else like urls?

urls for what? I you only need to render the images, no need to configure urls.

Because it says: 'upload_url' is invalid: 'ImproperlyConfigured': ''event-myimage1-upload' is invalid: 'NoReverseMatch': 'Reverse for 'event-myimage1-upload' not found. 'event-myimage1-upload' is not a valid view function or pattern name.

and after i manually configure the urls both of images will be uploaded to the MyImage2

sphrlbd commented 5 months ago

Can I have a better way to contact to you? I really need this for my project asap. Thanks☺️ @dzhuang

dzhuang commented 5 months ago

Sorry for my late response, I'm buried with works. Can you look into the demo_custom app in the repo which is the custom version of how to use the package.

sphrlbd commented 5 months ago

Yes i wrote everything like your demo but both of the images will be uploaded to the model 2 and i don't know why Thanks anyway.

dzhuang commented 5 months ago

Can you paste the code here?

sphrlbd commented 5 months ago

Sure

image_views.py:

from django.core.exceptions import PermissionDenied
from galleryfield.image_views import ImageCreateView, ImageListView, ImageCropView

class MyImage1CreateView(ImageCreateView):
    target_model = "event.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 = "event.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 = "event.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 = "event.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 = "event.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()

urls.py:

from django.urls import path
from event import image_views

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/<int:pk>",
        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/<int:pk>",
        image_views.MyImage2CropView.as_view(),
        name="event-myimage2-crop",
    ),
]

models.py:

from django.db import models
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from django.utils.timezone import now
from galleryfield.fields import GalleryField
from django.core.files.storage import default_storage
from PIL import Image

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="event.MyImage1", verbose_name=_("My photos"))
    album2 = GalleryField(target_model="event.MyImage2", verbose_name=_("My photos2"))
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=False,
        blank=False,
        verbose_name=_("Owner"),
        on_delete=models.CASCADE,
    )
dzhuang commented 5 months ago

I see. I'll try your code in a few days.

dzhuang commented 5 months ago

@sphrlbd Can you add more code in #62 ? Thanks

dzhuang commented 5 months ago

I can reproduce what you've described. Labeled bug. I need sometime to figure out the workround. Thanks for reporting.

dzhuang commented 5 months ago

The issue should be here

https://github.com/dzhuang/django-galleryfield/blob/6d9d89624dab4efbe9bfe6724a41f787d488adc9/galleryfield/widgets.py#L350-L351

potential duplicated css selectro of fileinputs, and here:

https://github.com/dzhuang/django-galleryfield/blob/6d9d89624dab4efbe9bfe6724a41f787d488adc9/galleryfield/templates/galleryfield/widget.html#L296

there should be no each because the rendered widget should have a unique name or id.

sphrlbd commented 5 months ago

Thanks a lot What exactly should i do to fix that?

dzhuang commented 5 months ago

Now it's fixed by #63 and I'm closing this issue.