I'm wanting to create a form that allows multiple image uploads. I've got a Listing
model that looks like this:
class Listing(models.Model):
location = models.CharField("Address/Neighborhood", max_length=250)
class ListingImage(models.Model):
listing = models.ForeignKey(
Listing,
related_name="images",
on_delete=models.SET_NULL,
null=True,
)
image = models.ImageField()
I'm using django-crispy-forms to create the form on the page but I cannot figure out how to get the listing
field onto the page.
class ListingModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
"Location",
Div("location", css_)
)
)
class Meta:
model = Listing
fields = "__all__"
And this is my view:
class ListingCreateView(LoginRequiredMixin, CreateView):
model = Listing
form_class = ListingModelForm
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
CodePudding user response:
If I'm understanding correctly, you want to have a field where you'll be able to add multiple images and then save them all. If I'm correct, then here's an approach you could take.
Add an extra field on the form that you'll set to accept multiple images... for example:
class ListingModelForm(forms.ModelForm):
# Extra field to collect multiple images
listing_images = forms.ImageField(
required=False,
widget=forms.ClearableFileInput(attrs={'name': 'listing_images', 'id': 'listing_images', 'multiple': True})
)
# other portion of your form below...
You can now access the field in the form: {{ form.listing_images }}
, adding multiple images...
Now within the function's post request
you have handling that form... You can do:
if request.method == 'POST':
form = ListingModelForm(data=request.POST, files=request.FILES)
if form.is_valid():
# listing = form.save(commit=False)
# listing.location = 'blah blah'
# listing.save()
# or just use
listing = form.save()
# Looping over the list of images and create for each iteration
for file in request.FILES.getlist('listing_images'):
ListingImage.objects.create(listing=listing, image=file)
# Also note that you could override the save method on the form to include the above forloop
# if you don't want it out here...
Assuming that your image field
on the ListingImage
have a path to upload the images and have configured your MEDIA_URL
and MEDIA_ROOT
settings...
UPDATES
Posted an answer before knowing you were using the class-based view
.
class ListingCreateView(LoginRequiredMixin, CreateView):
model = Listing
form_class = ListingModelForm
def form_valid(self, form):
# form.instance.user = self.request.user
listing = form.save() # Listing must be created first to use as the image reference
for file in self.request.FILES.getlist('listing_images'):
ListingImage.objects.create(listing=listing, image=file)
return super(ListingCreateView, self).form_valid(form)
# or using the post method
def post(self, *args, *kwargs):
form = ListingModelForm(data=request.POST, files=request.FILES)
if form.is_valid():
for file in self.request.FILES.getlist('listing_images'):
ListingImage.objects.create(listing=listing, image=file)
return redirect('to a path...')
So you could just use one of the methods highlighted above on the class-based view
.