Building a Social Media App With Python 3 & Django Beginners Tutorial 16: Uploading Multiple Images
Video Tutorial
In this tutorial we will add the ability to upload multiple images to posts. We will just need to make a few changes to our models, form, views, and templates where we are already handling images. Let’s start with the models.py file.
Updating models.py
To make this work, we will need to add another model for the image. We can then add a ManyToMany field. This will allow us to store multiple images.
class Post(models.Model): body = models.TextField() image = models.ManyToManyField('Image', blank=True, null=True) created_on = models.DateTimeField(default=timezone.now) author = models.ForeignKey(User, on_delete=models.CASCADE) likes = models.ManyToManyField(User, blank=True, related_name='likes') dislikes = models.ManyToManyField(User, blank=True, related_name='dislikes')
class Image(models.Model):
image = models.ImageField(upload_to='uploads/post_photos', blank=True, null=True)
Updating Our Post Form
Next we need to update our post form, we are going to update the image widget. We can add the multiple attribute to allow the user to select multiple images. We also want to remove the image field from the Meta child class. We do this to prevent the form from saving the image field automatically. We will handle that manually in our view.
class PostForm(forms.ModelForm): body = forms.CharField( label='', widget=forms.Textarea(attrs={ 'rows': '3', 'placeholder': 'Say Something...' })) image = forms.ImageField( required=False, widget=forms.ClearableFileInput(attrs={ 'multiple': True })) class Meta: model = Post fields = ['body']
Updating the PostList View
Now let’s save the images and add them to the Post object. We will need to get the images and then loop over them and save them each as an Image object. Once we have an image object saved we can then add them to the ManyToMany field on the Post object. Finally we need to make sure we call the save functions to save everything to the database.
def post(self, request, *args, **kwargs): logged_in_user = request.user posts = Post.objects.filter( author__profile__followers__in=[logged_in_user.id]).order_by('-created_on') form = PostForm(request.POST, request.FILES)
files = request.FILES.getlist('image')
if form.is_valid():
new_post = form.save(commit=False) new_post.author = request.user new_post.save()
for f in files: img = Image(image=f) img.save() new_post.image.add(img)
new_post.save()
context = { 'post_list': posts, 'form': form, } return render(request, 'social/post_list.html', context)
Showing Multiple Images in the Templates
Finally we need to update the templates to show multiple images. There are multiple ways we could go about handling this but I am going to just show them in a grid. We need to update the post list, post detail, and profile templates which are the templates that show posts. We can check if there are posts by checking if the length is greater than 0 and then we can get an iterable list by using post.image.all. Then we can loop over each image and display it on the page. This code will look the same on all of the templates.
{% if post.image.count > 0 %} <div class="row"> {% for img in post.image.all %} <div class="col-md-4"> <img src="{{ img.image.url }}" class="post-image" /> </div> {% endfor %} </div>{% endif %}
This concludes this tutorial. We now should be able to upload multiple images to one post. There are a few more features we could add to this application. We will come back later to add those.