Building a Social Media Site With Python and Django: Part 2 Social Feed and Posts
In this tutorial, we will add a social feed where posts can be viewed and a form where a new post can be added, we are not going to worry about the small details in what should be shown to each user yet, for now we will just show all posts. We will come back later and add those extra details to make each feed unique for each user later.
First we need to set up our Post model in our social/models.py:
class Post(models.Model):
body = models.TextField()
created_on = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
We are just creating a simple data model here, where we have a body of text, a date that the post was created on, and which user that wrote the post. Now with that done, we can create a view for the social feed. This view will first list all the posts when a get request is sent, there will also be a form at the top to submit a new post, let’s do that first:
from django.shortcuts import render
from django.views import View
from .models import Post
from .forms import PostFormclass PostListView(View):
def get(self, request, *args, **kwargs):
posts = Post.objects.all().order_by('-created_on')
form = PostForm() context = {
'post_list': posts,
'form': form,
}
return render(request, 'social/post_list.html', context)
In this view, we get all the posts and order them by newest first. You’ll see that we also instantiated a new form, this will be the form to create a new post. Let’s create a forms.py file in the social app to create the PostForm that we imported here in our views:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
body = forms.CharField(
label='',
widget=forms.Textarea(
attrs={'rows': '3',
'placeholder': 'Say Something...'}
)) class Meta:
model = Post
fields = ['body']
We will use the ModelForm object for this, the ModelForm will allow us to create a form based on a model. In this case, we will create it based off our Post model. In the Meta subclass we can define the information we need, which will be what model we want this form to be for, and what fields. We only want the body text to be a field in the form, so in our fields variable we create a list and only put ‘body’ inside of it. Above this, we can define specific details about each field. We want to remove the label and set some attributes on the input field. We can do this by setting a variable that is named the same as a field and set the data we need. This will create a text area input with 3 rows by default and some placeholder text.
Now with that created, we can create an html template for this view, make sure to name it the same as what you called it in the render function in the view. In this case, we called it post_list.html:
{% extends 'landing/base.html' %}
{% load crispy_forms_tags %}{% block content %}
<div class="container">
<div class="row justify-content-center mt-3">
<div class="col-md-5 col-sm-12">
<h5>Add a Post!</h5>
</div>
</div>
<div class="row justify-content-center mt-3 mb-5">
<div class="col-md-5 col-sm-12">
<form method="POST">
{% csrf_token %}
{{ form | crispy }}
<div class="d-grid gap-2">
<button class="btn btn-success mt-3">Submit!</button>
</div>
</form>
</div>
</div>
{% for post in post_list %}
<div class="row justify-content-center mt-3">
<div class="col-md-5 col-sm-12 border-bottom">
<p><strong>{{ post.author }}</strong> {{ post.created_on }}</p>
<p>{{ post.body }}</p>
</div>
</div>
{% endfor %}
</div>
{% endblock content %}
We can reuse the landing base template that we created last time. At the top we are putting in the form and using crispy forms to make it look good. We can use a for loop to iterate through the posts and display each of them.
Finally, the last step is to create a url route for this view, let’s create a urls.py in our social app:
from django.urls import path
from .views import PostListViewurlpatterns = [
path('', PostListView.as_view(), name='post-list'),
]
Now let’s include this in our root urls file:
from django.contrib import admin
from django.urls import path, includeurlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')),
path('', include('landing.urls')),
path('social/', include('social.urls')),
]
Now you should be able to view the social feed. But the form won’t work yet, let’s make that work now, first we need to handle the form submission as a post request in our view:
class PostListView(View):
def get(self, request, *args, **kwargs):
posts = Post.objects.all().order_by('-created_on')
form = PostForm() context = {
'post_list': posts,
'form': form,
}
return render(request, 'social/post_list.html', context) def post(self, request, *args, **kwargs):
posts = Post.objects.all()
form = PostForm(request.POST) if form.is_valid():
new_post = form.save(commit=False)
new_post.author = request.user
new_post.save() context = {
'post_list': posts,
'form': form,
}
return render(request, 'social/post_list.html', context)
We can do almost the exact same thing that we did in our get request, except now we need to pass in the request.POST data to our form and we can check if the form is valid. If it is, we can set the author to be the current logged in user and save the post. Then we can just render the exact same template and it will show the new post.
With that done, we have a basic social feed created and a way to add new posts. For now we will stop here although we still have some functionality to add. We can’t edit/delete posts and we can’t add comments to posts, we will add this in the next tutorial.