In this tutorial, we will continue designing the Hacker News clone website by adding a user authentication feature. Anyone interested in using our application to share the latest news must be authenticated. This means the person has to register on the platform and be able to log in and log out.
Once authenticated, the creator can be able to submit or edit posts. I have already demonstrated how to authenticate users in previous Django project tutorials. Since we are replicating an existing application, we have to show how the particular feature was designed.
Therefore, in this series, we will:
- Create a user authentication feature for registering, sign-in and sign-out functionalities.
- Add more views to allow authenticated users to submit or edit posts.
- Give readers the option to show their appreciation for the news read by upvoting or downvoting the post.
User Authentication
Signup View
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import authenticate, login def signup(request): if request.user.is_authenticated: return redirect('/') if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): form.save() username = form.cleaned_data['username'] password = form.cleaned_data['password1'] user = authenticate(username=username, password=password) login(request, user) return redirect('/') else: return render(request,'signup.html',{'form':form}) else: form = UserCreationForm() return render(request, 'signup.html', {'form':form})
This code is a Django view function that handles user registration. It imports the UserCreationForm
from Django’s built-in authentication forms, as well as the authenticate, login, and logout functions from Django’s authentication system.

The signup function first checks if the user is already authenticated. If they are, it redirects them to the homepage. If the user is not authenticated, the function checks if the request method is POST, meaning that the form has been submitted.
If the form has been submitted, it creates a new instance of the UserCreationForm
with the data from the request. It then checks if the form is valid. If it is, it saves the new user, authenticates them using their username and password, logs them in, and redirects them to the homepage.
If the form is not valid, it returns a response that renders a template called signup.html
and passes in the invalid form to be displayed to the user.

If the request method is not POST, meaning that the form has not been submitted, it creates a new instance of the UserCreationForm
and returns a response that renders the signup.html
template and passes in the empty form to be displayed to the user.
Sign-in View
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth import login def signin(request): if request.user.is_authenticated: return redirect('/') if request.method == 'POST': username = request.POST['username'] password = request.POST['password'] user = authenticate(request, username =username, password = password) if user is not None: login(request,user) return redirect('/') else: form = AuthenticationForm() return render(request,'signin.html',{'form':form}) else: form = AuthenticationForm() return render(request, 'signin.html', {'form':form})
This code checks if the user is already authenticated, and if they are, it redirects them to the homepage.
If the user is not authenticated, the function checks if the request method is POST, which means the form has been submitted. If it has, it retrieves the username and password from the request data and uses the authenticate function to check if the provided credentials are valid.
If the credentials are valid, it logs in the user using the login function and redirects them to the homepage. If the credentials are not valid, it creates a new instance of Django’s built-in AuthenticationForm
and returns a response that renders a template called signin.html
and passes in the form to be displayed to the user.
If the request method is not POST, that means the form has not been submitted, it creates a new instance of the AuthenticationForm
and returns a response that renders the signin.html
template and passes in the empty form to be displayed to the user.
Logout View
from django.contrib.auth import logout def signout(request): logout(request) return redirect('/')
This view function uses the logout
function from Djangoβs authentication system to log out the user associated with the given request. After logging out the user, it redirects them to the homepage.
And here are the urlpatterns
:
path('signin', signin, name='signin'), path('signup', signup, name='signup'), path('signout', signout, name='signout'),
Adding More Views
Submit View
from .forms import PostForm def SubmitPostView(request): if request.user.is_authenticated: form = PostForm() if request.method == "POST": form = PostForm(request.POST) if form.is_valid(): title = form.cleaned_data['title'] url = form.cleaned_data['url'] description = form.cleaned_data['description'] creator = request.user created_on = datetime.now() post = Post(title=title, url=url, description=description, creator = creator, created_on=created_on) post.save() return redirect('/') return render(request,'submit.html',{'form':form}) return redirect('/signin')
This code is a Django view function that handles the submission of a new post. It first checks if the user is authenticated, and if they are not, it redirects them to the signin URL.
If the user is authenticated, it creates a new instance of a form called PostForm
. It then checks if the request method is POST. If the form has been submitted, it creates a new instance of the PostForm
with the data from the request.
It then checks if the form is valid. If it is, it retrieves the title, URL, and description from the form’s cleaned data and uses them to create a new Post object. It also sets the creator attribute of the post to the current user and sets the created_on
attribute to the current date and time. It then saves the new post to the database and redirects the user to the homepage.
If the form is not valid or if the request method is not POST, meaning that the form has not been submitted, it returns a response that renders a template called submit.html
and passes in the form to be displayed to the user.
Here is the PostForm
class. Create a forms.py
file and add it there.
from django import forms from .models import Post class PostForm(forms.ModelForm): class Meta: model = Post fields = ('title','url','description')
The PostForm
class inherits from forms.ModelForm
and defines a nested Meta class that specifies the model and fields to be used by the form.Β A ModelForm
allows you to create and update instances of a specific model.
In this case, the model attribute is set to Post
, and the fields attribute is set to a tuple containing the fields title, URL, and description. This means that the form will include fields for these three attributes of the Post
model.
When an instance of this form is created and validated, it can be used to create or update a Post object with the data entered by the user.
Edit View
def EditPostView(request,id): post = get_object_or_404(Post,id=id) if request.method =='POST': form = PostForm(request.POST, instance=post) if form.is_valid(): form.save() return redirect('/') form = PostForm(instance =post) return render(request,'submit.html',{'form':form})
This view function handles the editing of an existing post. It takes two arguments: request and id. The id argument is used to retrieve the Post object that should be edited.
The function uses the get_object_or_404
function to retrieve the Post object with the given id. If no such object exists, it raises a 404 error.

If the request method is POST, meaning that the form has been submitted, it creates a new instance of the PostForm
with the data from the request and the instance parameter set to the retrieved Post object. It then checks if the form is valid. If it is, it saves the changes to the post and redirects the user to the homepage.
If the request method is not POST, meaning that the form has not been submitted, it creates a new instance of the PostForm
with the instance parameter set to the retrieved Post object. This pre-populates the form fields with the current values of the post. It then returns a response that renders a template called submit.html and passes in the form to be displayed to the user.
Here are the urlpatterns
:
path('submit', SubmitPostView, name='submit'), path('edit/<int:id>', EditPostView, name='edit'),
Upvote and Downvote Views
from .models import Vote def UpVoteView(request,id): if request.user.is_authenticated: post = Post.objects.get(id=id) votes = Vote.objects.filter(post = post) v = votes.filter(voter = request.user) if len(v) == 0: upvote = Vote(voter=request.user, post=post) upvote.save() return redirect('/') return redirect('/signin')
The function is used to handle upvoting a post by a user. Hereβs what the code does:
- It checks if the user making the request is authenticated (i.e., logged in).
- If the user is authenticated, it retrieves the post with the given id from the database.
- It then retrieves all votes for that post from the database.
- It checks if the user has already voted for that post by filtering the votes for that post by the current user.
- If the user has not already voted for that post, it creates a new Vote object with the current user as the voter and the post as the post being voted for.
- It then saves this new vote to the database.
- Finally, it redirects the user to the homepage.
If the user is not authenticated, it redirects them to the signin page.
def DownVoteView(request,id): if request.user.is_authenticated: post = Post.objects.get(id=id) votes = Vote.objects.filter(post = post) v = votes.filter(voter = request.user) if len(v) != 0: v.delete() return redirect('/') return redirect('/signin')
In this function, it checks if the user has already voted. If so, it deletes it. And then redirects them to the home page.
Finally, the urlpatterns
:
path('vote/<int:id>', UpVoteView, name='vote'), path('downvote/<int:id>', DownVoteView, name='dvote'),
The Templates
Please check the source code for the templates. They all go by the same explanation from the previous series. They all check if the user is authenticated before allowing them to benefit from the appβs functionalities.
From the templates, notice how the names given in the urlpatterns
were used to refer to the URLs. All these were made possible using Django templating engine.
Conclusion
We have come a long way in this Hacker News project created using the Django web framework. In the final part of this project, we will create views for the comment functionality. Thereafter, we test every feature of this app using information from the Hacker News website. If some features do not function as intended, we make the needed adjustments. Stay tuned!

π‘ Recommended: How I Coded a Hacker News Clone in Django