Building a Social Media App Python 3 & Django Beginners Tutorial Part 17: Sharing Posts
In this tutorial we will be adding the ability to share another users post. To do this we will add more fields to our Post model and we will update our forms, views and templates to handle this new feature. Let’s get started by adding to our models.py.
Adding Additional Fields to the Post Model
We need to add some additional fields to handle if a user is sharing another post, to do this we want to create a new post with the original post’s information but also include the sharing user, the date it was shared, and if they entered some text with it, we want to show that as well. So we will create fields for all of these different pieces of data. We will also handle the ordering here in the model instead of using the order_by() method that we were using in our views. This will allow us to order it by multiple fields, we can now order it by the original date but also by the shared date. This will show the shared posts in order by when they were shared.
social/models.py
class Post(models.Model): shared_body = models.TextField(blank=True, null=True) body = models.TextField() image = models.ManyToManyField('Image', blank=True) created_on = models.DateTimeField(default=timezone.now) shared_on = models.DateTimeField(blank=True, null=True) author = models.ForeignKey(User, on_delete=models.CASCADE) shared_user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='+') likes = models.ManyToManyField(User, blank=True, related_name='likes') dislikes = models.ManyToManyField(User, blank=True, related_name='dislikes') class Meta: ordering = ['-created_on', '-shared_on']
Creating a Form for Sharing Posts
We will need to create a new form to handle the ability for a user to enter some text with the shared post. This will just be a regular form with one text field.
social/forms.py
class ShareForm(forms.Form): body = forms.CharField( label='', widget=forms.Textarea(attrs={ 'rows': '3', 'placeholder': 'Say Something...'
}))
Adding a View to Handle the Post Request When Sharing a Post
First things first, we can remove all order_by method calls on any Post objects since we handled that in the models. Next we will need to create a view with a post method to handle when the share form is submitted. This will create the new post with all of the data and then it will redirect back to the post list.
social/views.py
class SharedPostView(View): def post(self, request, pk, *args, **kwargs): original_post = Post.objects.get(pk=pk) form = ShareForm(request.POST) if form.is_valid(): new_post = Post( shared_body = self.request.POST.get('body'), body = original_post.body, author = original_post.author, created_on = original_post.created_on, shared_on = timezone.now(), shared_user = request.user ) new_post.save() for img in original_post.image.all(): new_post.image.add(img), new_post.save() return redirect('post-list')
We also need to add the share form to both the get and post methods in the post list view.
share_form = ShareForm()
And then add it to the context dictionary.
'shareform': share_form
Adding the Share Form to the Post List Template
We can add the share form like we have with the others, using crispy forms and the shareform passed into the context.
social/templates/social/post_list.html
<form method="POST" action="{% url 'share-post' post.pk %}" class="d-none" id="{{ post.pk }}"> {% csrf_token %} {{ shareform | crispy }} <div class="d-grid gap-2"> <button class="btn btn-success mt-3">share the post</button> </div></form>
Adding the button toggle the Share Form
We also want to add a button toggle the share form from shown/hidden like we did for the comment form. We will add the Javascript for this next.
social/templates/social/post_list.html
<p class="post-text"><a class="text-primary post-link" href="{% url 'profile' post.author.profile.pk %}">@{{ post.author }}</a> {{ post.created_on }}<span onclick="shareToggle('{{ post.pk }}')"><i class="far fa-share-square share-btn"></i></span></p>
Adding the Javascript to Toggle the Share Form
This will look very similar to the previous toggle function. We are setting the post id on the form, so we can use that to add or remove the d-none class. The d-none class will hide the element from view by setting the CSS style of display: none; on the element.
static/js/social.js
function shareToggle(parent_id) { const row = document.getElementById(parent_id); if (row.classList.contains('d-none')) { row.classList.remove('d-none'); } else { row.classList.add('d-none'); }}
Updating the Other Templates to Show Shared Posts
To make shared posts show up on the other templates like the post detail or the profile, you can copy over the same logic done on the post list template to those as well.