Building a Video Sharing Website: Part 2 Index Page and Authentication
In this tutorial we will continue the tutorial on building a video sharing web application. In this tutorial we will add user authentication, create a ListView to show videos on the index page and restrict certain actions to only specific users. We only want the user who posted a video to be able to edit or delete it and we only want registered users to be able to be able to post videos to the website. To add this functionality, we will need to create the ability for users to create accounts and log into their accounts. We could build this from scratch but instead, we are going to use AllAuth to do this.
Setting Up AllAuth
AllAuth is a Django library that we can install that will allow us to quickly set up an authentication system for our website. It comes out of the box with all of the views we will need including email verification and a way to manage verified vs unverified accounts.
To set this up we just need to follow a couple steps. First we need to install it.
pip install django-allauth
Once it is installed we need to open up our settings.py file and make the following changes:
AUTHENTICATION_BACKENDS = [
...
# Needed to login by username in Django admin, regardless of `allauth`
'django.contrib.auth.backends.ModelBackend', # `allauth` specific authentication methods, such as login by e-mail
'allauth.account.auth_backends.AuthenticationBackend',
...
]INSTALLED_APPS = [
...
# The following apps are required:
'django.contrib.auth',
'django.contrib.messages',
'django.contrib.sites', 'allauth',
'allauth.account',
'allauth.socialaccount',
]SITE_ID = 1
This is all we need to set up the basics, there is more you can add if you want to go beyond this, The documentation goes into more detail about this.
After this is set up, all we need to do is migrate the changes to the database:
./manage.py migrate
Now with these changes, we will have a working authenticaton system set up. You can go to the following url routes to see the default templates:
accounts/signup/
accounts/login/
accounts/logout/
Customizing AllAuth Templates
If you take a look at the default templates you will realize a problem right away. They don’t look the best out of the box. Luckily this isn’t hard to fix. To override the default templates, you will need to create a templates folder in the root project folder and copy the files found here into that templates folder. Once you have those files added, any changes made to these files will overwrite the default. You can customize these files however you would like to get the look and style that you want.
Restricting User Access
Now that we have a way for users to register and login, we want to only allow registered users to post videos and only the uploader to be able to edit or delete a video. To do this, we will need to make use of Django’s Mixins. Mixins provide extra functionality to class based views. We will make use of LoginRequired and UserPassesTest. LoginRequired will only allow logged in users to access a view and UserPassesTest will only allow access if the user passes a defined test, which we will write in a minute. First let’s add LoginRequired, here is an example on how to add it to the CreateView:
from django.contrib.auth.mixins import LoginRequiredMixinclass CreateVideo(LoginRequiredMixin, CreateView):
model = Video
fields = ['title', 'description', 'video_file', 'thumbnail']
template_name = 'videos/create_video.html' def form_valid(self, form):
form.instance.uploader = self.request.user
return super().form_valid(form) def get_success_url(self):
return reverse('video-detail', kwargs={'pk': self.object.pk})
All you will need to do is import it and add it as the first parameter for the view.
Next, we need to only allow the same user who posted a video to have access to editing or deleting it. To do this we need to add the UserPassesTest Mixin, here is an example on how to add it to the UpdateView:
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixinclass UpdateVideo(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Video
fields = ['title', 'description']
template_name = 'videos/create_video.html' def get_success_url(self):
return reverse('video-detail', kwargs={'pk': self.object.pk})
def test_func(self):
video = self.get_object()
return self.request.user == video.uploader
To add this mixin we once again need to import it and add it as a parameter. We are also adding the LoginRequired mixin like before. We also need to write the test function. This is called test_func and only takes one parameter of self. Here we can test if the uploader is the same user as the logged in user. We get the current video object and check to see if the request.user (logged in user) is equal to video.uploader (original user who posted the video). It returns True or False, if it returns True access is granted to the user.
Index Page List View
Finally we want to add a view to display all videos as a list on the index page. Later we will add more functionality to this page but for now, we will just stick to a basic listview.
views.py
class Index(ListView):
model = Video
template_name = 'videos/index.html'
order_by = '-date_posted'
We need to get the video model and set the template name. We are also ordering the list by the reverse of date_posted to have the newest videos posted first.
videoSharer/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from videos import views as video_viewsurlpatterns = [
path('admin/', admin.site.urls),
path('', video_views.Index.as_view(), name='index'),
path('videos/', include('videos.urls')),
path('accounts/', include('allauth.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
We then need to update our urls.py file in the root directory of the project. All we need to change is the index route to a class based view using .as_view() like we did for our other class based views.
Now we can update our html template to loop through the video list and display it to the user.
{% extends 'videos/base.html' %}{% block content %}<div class="container">
<div class="row justify-content-center">
{% for object in object_list %}
<div class="card col-md-3 col-sm-12 mr-md-2 mt-5 p-3 border-0">
<a href="{% url 'video-detail' object.pk %}"><img src="/media/{{ object.thumbnail }}" width="256" height="144"></a>
<div class="card-body">
<a class="link-text" href="{% url 'video-detail' object.pk %}"><h5 class="text-center">{{ object.title }}</h5></a>
<p class="text-muted text-center m-0">{{ object.uploader }}</p>
<p class="text-muted text-center">{{ object.date_posted | date:"M d, Y" }}</p>
</div>
</div>
{% endfor %}
</div>
</div>{% endblock content %}
That is the final piece for this tutorial. In future videos we will continue adding more features to this web application.