How to create multiple images upload in Django CreateView?
Asked Answered
I

3

3

I have a Django project and I created a post operation in this project, so the user will be able to share multiple picture posts. But I have a problem. I can't write multiple image upload function. I looked at a lot of content, but it doesn't work. Either it reports a problem or context is not sent to the HTML page I wanted. Please help me. The multi picture function I want should be under CreateView and should be placed in the same template as it. Also, there should be 4 Image upload buttons, and the last one should be assigned multiple features (to HTML tag).

models.py:

class Photo(models.Model):
    post= models.ForeignKey(Person, on_delete=models.CASCADE)

    image = models.ImageField(upload_to=get_image_filename)
    uploaded_at = models.DateTimeField(auto_now_add=True)

views.py:

class PersonCreateView(CreateView):
    model = Person
    form_class = PersonForm
    success_url = reverse_lazy('person_changelist')

forms.py:

class PhotoForm(forms.ModelForm):
    image = forms.ImageField(label='Image')
    class Meta:
        model = Photo
        fields = ('image', )


class PersonForm(forms.ModelForm):
    title = forms.CharField(max_length=128)
    body = forms.CharField(max_length=245, label="Item Description.")
    file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))

    class Meta:
        model = Person
        widgets = {
            'valyuta': forms.RadioSelect,
            'barter': forms.CheckboxInput,
            
        }
        fields = ('country', 'city', 'ban', 'yurus', 'reng', 'qiymet', 'valyuta', 'yanacaqnovu', 'oturucu', 'squtu', 'buraxilisili', 'hecm', 'seher', 'barter', 'metin')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['city'].queryset = City.objects.none()


        if 'country' in self.data:
            try:
                country_id = int(self.data.get('country'))
                self.fields['city'].queryset = City.objects.filter(country_id=country_id).order_by('name')
            except (ValueError, TypeError):
                pass  # invalid input from the client; ignore and fallback to empty City queryset
        elif self.instance.pk:
            self.fields['city'].queryset = self.instance.country.city_set.order_by('name')
Imprimis answered 26/1, 2020 at 19:41 Comment(7)
so what you want is: A user can upload multiple pictures of himself and you want to accept those multiple images in a single form?Tepper
I mean, say something. I am trying to make an advertisement site, so when a user enters the post creation tab, I want to meet in the multiple image upload area (in the same link) with the texrfields that need to fill in (with the classes I created the textfields or something). I am writing functions in CreateView that I made, but it does not happen. Please can you help me?Imprimis
You should investigate the working of formsets for this to function properly docs.djangoproject.com/en/3.0/topics/forms/formsets/#formsetsTepper
Moreover you must share your code properly, the one that you have shared doesn't even have any indent, so it is really difficult to figure out what the issue might beTepper
What I want is not too complicated. I just want to place a function under PersonCreateView (CreateView) in views.py in that function, too, to load multiple images and it must be placed in the same link as CreateView (success_url = reverse_lazy ('person_changelist'))Imprimis
I am sorry but I still don't get what you want to do. as I understand, you want to create an interface where user is able to upload multiple images (linked to a post) at once and when the form is submitted you want to save those images corresponding to the user.'s post and after saving, you want to redirect the user to the same form's page. is that it?Tepper
I have multiple image upload working by overriding the CreateView post method. I can post an answer if you're still interestedNarcosis
M
1

I was struggling with the same issue but eventually I succeeded. So i want to share my solution. First of all we need to create 2 models, one of them to put ImageField and connect using ForeignKey with the first model.

models.py

class Item(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="items")
    name = models.CharField(max_length=100)


class ItemImage(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    img = models.ImageField(default="store/default_noitem.jpg", upload_to=get_image_dir)

Then create form using inlineformset_factory. It helps especially in the case of working with related objects via a foreign key.

forms.py

from django import forms
from django.forms.models import inlineformset_factory

from .models import Item, ItemImage

class  ItemImageForm(forms.ModelForm):
    class Meta:
        model = ItemImage
        exclude = ()

class ItemForm(forms.ModelForm):
    class Meta:
        model = Item
        fields = ["name",]

ItemImageFormSet = inlineformset_factory(
    Item, ItemImage, form=ItemImageForm,
    fields=['img'], extra=3, can_delete=True  # <- place where you can enter the nr of img
)

And finally

views.py

class ItemCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
    template_name = "items/add_item_form.html"
    success_message = 'Item successfully added!'
    form_class = ItemForm

    def get_context_data(self, **kwargs):
        data = super(ItemCreateView, self).get_context_data(**kwargs)
        data['form_images'] = ItemImageFormSet()
        if self.request.POST:
            data['form_images'] = ItemImageFormSet(self.request.POST, self.request.FILES)
        else:
            data['form_images'] = ItemImageFormSet()
        return data

    def form_valid(self, form):
        context = self.get_context_data()
        form_img = context['form_images']
        with transaction.atomic():
            form.instance.user = self.request.user
            self.object = form.save()
            if form_img.is_valid():
                form_img.instance = self.object
                form_img.save()
        return super(ItemCreateView, self).form_valid(form)

Remember to add (self.request.POST, self.request.FILES) inside formset and enctype="multipart/form-data" inside html where <form> tag is located.

Mensa answered 2/6, 2020 at 18:27 Comment(0)
T
0

See if this works. Create a simple HTML form instead of the Django's model form

<form enctype="multipart/form-data" action="" method="post">
    <input type="file" name="uploaded_images" accept="image/*" multiple>
    <input type="submit" name="upload" value="Upload">
</form>

In your view:

if request.method == 'POST':
    for afile in request.FILES.getlist('uploaded_images'):
        #Save images in respective models with the links to other models
        return redirect('back/to/same/url')

This will enable the user to upload multiple images at once and you will easily be able to work with individual images.

Tepper answered 27/1, 2020 at 16:48 Comment(2)
he's talking about using a CreateView (class-based view) and this is code for a function-based viewNarcosis
Agreed, Looking at my answer after 2 years, feels like I was creating more confusion than a solution. I hope he got his issue resolved.Tepper
M
0

Approaching without PhotoForm class and inlineformset_factory.

models.py

class Photo(models.Model):
    post= models.ForeignKey(
        Person, on_delete=models.CASCADE, related_name="person_images"
    )
    image = models.ImageField(upload_to=get_image_filename)
    uploaded_at = models.DateTimeField(auto_now_add=True)

views.py

class PersonCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
    form_class = PersonForm
    template_name = "pages/add_person.html"
    success_url = reverse_lazy("person_changelist")
    success_message = _("Person created successfully!")

    def form_valid(self, form):
        form.instance.user = self.request.user
        person_data = form.save()

        # Save the images of person.
        with transaction.atomic():
            images = self.request.FILES.getlist("images")
            for image in images:
                form.instance.person_images.create(post=person_data, image=image)
        return super().form_valid(form)
Maltha answered 23/9, 2022 at 3:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.