Upload multiple images to a post in Django
Most probably you’re reading this because you are probably developing a blog using Django. It’s something I actually was searching all over the internet 2 years ago when I started learning python, and couldn’t find examples online. Took me weeks, but I finally figured out, and was lucky enough to find a StackOverflow question that is asking the same question and I could answer it perfectly.
You’ll just need two models. One for the Post and the other would be for the Images. Your Images model would have a ForeignKey to the Post model:
class Post(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=128)
body = models.CharField(max_length=400)
def get_image_filename(instance, filename):
id = instance.post.id
return "post_images/%s" % (id)
class Images(models.Model):
post = models.ForeignKey(Post, default=None)
image = models.ImageField(upload_to=get_image_filename,
verbose_name='Image')
You need to create a form for each model, but they will be related to each other, as in when the user is filling out the form post he has to complete the image form too for the post to successfully be posted.
class PostForm(forms.ModelForm):
title = forms.CharField(max_length=128)
body = forms.CharField(max_length=245, label="Item Description.")
class Meta:
model = Post
fields = ('title', 'body', )
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image')
class Meta:
model = Images
fields = ('image', )
Now this is the most important part of everything, the views, because this is where uploading multiple images to a single post happens. For us to be able to upload multiple images at once, we will need django formsets to make this happen.
@login_required
def post(request):
ImageFormSet = modelformset_factory(Images,
form=ImageForm, extra=3)
if request.method == 'POST':
postForm = PostForm(request.POST)
formset = ImageFormSet(request.POST, request.FILES,
queryset=Images.objects.none())
if postForm.is_valid() and formset.is_valid():
post_form = postForm.save(commit=False)
post_form.user = request.user
post_form.save()
for form in formset.cleaned_data:
image = form['image']
photo = Images(post=post_form, image=image)
photo.save()
messages.success(request,
"Posted!")
return HttpResponseRedirect("/")
else:
print postForm.errors, formset.errors
else:
postForm = PostForm()
formset = ImageFormSet(queryset=Images.objects.none())
return render(request, 'index.html',
{'postForm': postForm, 'formset': formset},
context_instance=RequestContext(request))
In the view the we are getting both of our forms, and the view will check both forms if they are valid. In that way the user has to fill the form AND upload all the images which in this case are 3 extra=3
. Only then will the post successfully get created.
<form id="post_form" method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
{% for hidden in postForm.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in postForm %}
{{ field }} <br />
{% endfor %}
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
<input type="submit" name="submit" value="Submit" />
</form>