Beginner Lesson 11: User Management

Transcript

Transcript References
The video mentions copying code from the screen or transcript. This is a reference to the old, paid version of the course. I have now collated all the code together in one place in the source, which you can download here.

User Management

Most modern websites allow some form of user interaction. Common examples include comments on blog posts, allowing users to customize the information they see, editorial control over content and e-commerce.

To make user management easier, Django comes with a user authentication and authorization system built in. With Django, you can both create and edit users in the admin, as well as add code to your views and templates to hide content from unauthorized users.

In this first lesson of the module, we will look at how you can manage users in the admin, create user groups and assign permissions to a user or group.

In later lessons, we will modify our views so only registered users can submit a quote, as well as adding a filter to the quote list so it only displays the quotes submitted by the logged in user. To accomplish this, we will be using Django’s generic views and forms again to register users and log them in to our website.

Users in the Admin

Django’s built-in authentication system is automatically added to the admin interface when you create a new project. With the admin you can:

  • Add, edit and Delete Users
  • Reset user passwords
  • Assign Staff and/or Superuser status to a user
  • Add or remove user permissions
  • Create user groups; and
  • Add users to a group

Add a User

Remember when we created an admin superuser in Module 5? This special user has full access to all models in the admin and can add, change and delete any model record. In a real application, you will want to limit the number of users who have full access to your site.

Adding a new user is easy—click the green plus sign on the right of the Users entry on the admin home page. Enter a username and password and click save to add the new user.

Return to the admin home page and click Users to open the user list. Click on the username to open the user edit screen.

At the top of the user edit screen, you will see options to edit the users password and personal info. Scroll down to the Permissions section and make sure Staff status is checked and Superuser status is unchecked.

Adding User Permissions

What we have created here is considered a normal admin user. Normal admin users—that is, active, non-superuser staff members—are granted admin access through assigned permissions. Each object editable through the admin interface (e.g., quotes and pages) has four permissions: a create permission, a view permission, an edit permission and a delete permission.

Assigning permissions to a user grants the user access to do what is described by those permissions. When you create a user, that user has no permissions. It’s up to you to give the user specific permissions.

We’re going to do that now—we are going to create an author user who has permission to add and edit site pages, but not to be able to delete them. Scroll down the edit page a bit further to the User permissions panel and add the can add page and can change page permissions using the horizontal filter.

Once you have added the permissions, log out and log back in as the new user. The admin dashboard will now only show the pages app, hiding all the other models that the user doesn’t have permission to access.

Add a User Group

This is pretty easy, but what if you have many authors you want to add as users? It’s time consuming to add permissions one at a time to each user. Luckily, Django allows you to create user groups, which is simply a group of permissions that can be added to a user all together, rather than one at a time.

Let’s go ahead and create an author group. You will first have to log out as the author user and log back in as the admin user.

Creating a group is like creating a user—go to the admin front page and click the green add button to the right of the Groups listing and name your new group Author, add the permissions from the horizontal filter and save your new group.

Once you have added the group, you can go back to the user and edit their permissions to add the new group.

Don’t forget to delete the permissions you assigned previously to prevent any permission clashes later. Save the user and now, when you log out and log back in again as the author user, they will have the same restricted view of the admin as when we added the permissions directly.

That’s about it for adding users and user permissions in the admin. In the remaining lessons in the module, we’re going to be working in the front end to manage what content our website users get to see.

Users in the Front End

A common and important feature of modern websites is to hide content from unregistered users. To demonstrate how to restrict content to registered users in Django, we are going to implement an example of restricted access—site visitors must be registered to submit and view quotes.

The first step in the process is to create the user registration system so that customers can register with the Meandco website. As managing users in the front end is such a common requirement, Django’s developers have provided several handy classes and built-in forms and views to make registering and authenticating users a breeze.

Django has built-in authentication forms for logging users in and out and resetting or changing passwords. Each of these forms also has a built-in default view, so you don’t have to create views for logging users in and out of the website.

Django also has a generic UserCreationForm, which is used for registering new users with a website. This generic form, however, doesn’t have a default view—we need to write one.

Which is what we are doing in this lesson – creating a view for registering new users.

Add the Registration View

We are going to create the user registration view with one of Django’s generic editing views—CreateView. To create our new view, add the following to the views.py file in our quotes app. Pause the video and enter the code into your editor or copy from the transcript

CreateView is a useful class that makes creating and showing a blank edit form easy—all you need to do is pass it a template and a form and it will create a blank edit form on the fly. First, we need to import CreateView and the UserCreationForm class into our views.py file. We’re also importing reverse_lazy from django.urls which will allow us to do reverse lookups at runtime. More on reverse_lazy in a minute.

The Register class itself begins on line 13. Let’s look closer at this code:

Line 13 is the Register class declaration. The Register class inherits from CreateView.

In line 14, the template_name attribute tells CreateView what template to use. We will create the register.html template in the next lesson.

In line 15, the form_class attribute tells Django which form to use with CreateView. In this case, we’re using the UserCreationForm class to create the form, rather than writing our own form class from scratch.

In line 16, success_url is the URL that the form will redirect to once the form has been successfully processed.

And in lines 18 to 20, our Register class has a single class method— form_valid(). This is a built-in method that will save our new user’ s information to the database once a valid registration form has been submitted. The form_valid() method then redirects to a success page URL set at runtime by reverse lookup.

I have introduced a new function in this bit of code—reverse_lazy(). In keeping with the Don’t Repeat Yourself (DRY) principle, it’s always advisable to avoid hard-coding URLs.

Django makes this task easy by providing the functions reverse() and reverse_lazy() for reversing URLs. In other words, if you provide either reverse() or reverse_lazy() with the name of the URL, it will look up the URL name and replace it with the corresponding absolute URL.

We have been preparing to use reversible URLs ahead of time by naming our URLs. For example, For URL configuration:

path(‘show’, some_view(), name=’show-something’)


If we were to call reverse(‘show-something’) at runtime, it would return your site URL with show appended to the end.

This allows for highly flexible and dynamic URL generation, however it has a drawback—when a class or a function is compiled, Django doesn’t know what the absolute URL is as it’s not available until runtime.

Python solves this problem quite neatly with lazy evaluation. Put simply, lazy evaluation will only compute the value (in this case the URL) when needed.

In our Register class, Django’s reverse_lazy() function implements Python’s lazy evaluation to wait until runtime to calculate the URL for success_url.

register-success is the name of the URLconf that we will create a bit later in the lesson on URL Configuration and Testing.

As a final exercise, compare the code in this class-based view with the code in the quote_req function. You can see that, in some cases, using generic views can reduce the amount of code needed quite substantially.

That’s it for the user registration view. In the next lesson, we’re going to create the templates for our user registration system.

Create the Templates

Our next task is to create the templates for rendering our registration and login/logout forms. There are three templates that need to be created:

1. login.html. A template to display the login form;

2. register.html. A template to show the user registration form; and

3. success.html. A simple template to tell the user they have successfully registered with the site.

We also need to make a modification to base.html to show user information at the top of the page.

Before creating the templates, create a new folder called registration in your site templates folder. Django expects the login template to be named login.html, so we’re going to create this file inside the registration folder. When you are done, your folder structure should look like this.

The Login Template

Now we’re going to add the template code to the login.html file we just created. Pause the video and enter the code into your editor or copy from the transcript:

This is mostly HTML and Django template code, but there are some lines that need some explanation:

In lines 13 and 21, we’re using Django’s {% url %} tag. This tag performs exactly the same function as reverse_lazy()—it does a reverse lookup of the URL name and replaces it with the actual URL when Django renders the template.

In line 15 we’re rendering the login form fields as a table. If you were wondering where the form fields come from, remember that Django has a built-in view for logging users in, so the form is created automatically for you and passed to the view in the variable form.

And in line 22 we’ve added a hidden field to the form. When a page link is redirected, which is what happens when a user is sent to the login page, Django will save the original destination in the next template variable. We’re adding the next variable so that, once the form is successfully submitted, the value of next is preserved and Django knows where to redirect the user.

The Register Template

Next, we’re going to create the template for the registration form. Create a new file and name it register.html. Once you’ve created the file, pause the video and enter the code into your editor or copy from the transcript:

The code in the register template is almost identical to the code in the login template, so you should find this template code easy to follow. Note that on line 15 the form variable will contain the form from your Register view class that you created in the last lesson. Like the login form, Django created this form for you, without you having to write any form code.

The Success Template

The last template we need to create is the success template. Create a new file called success.html and enter the following code. Pause the video and enter the code into your editor or copy from the transcript:

This is a simple template designed to show a message to the user when they’ve successfully registered. Note how we are appending the value of the next variable to the reverse lookup URL in a GET parameter. It’s perfectly legal to concatenate text in this way in Django template code.

Modify the Base Template

Finally, we need to make some changes to base.html to show user authentication status at the top of the page. Pause the video and enter the code into your editor or copy from the transcript (changes in bold):

This is simple to follow—on line 18 we have an if statement that will render a logged in message if the user is logged in, or a logged out message if they are not. The template also provides a convenient link to login or logout directly from the page header.

OK, now we have the templates sorted, in the next lesson we’re going to set up the URL configurations so we can navigate our authentication system. We’ll then test our code and templates to make sure it all works.

URL Configuration and Testing

Now that we’ve create our registration view and templates, our third task is to add the URLconfs for our authentication views. Pause the video and enter the code into your editor or copy from the transcript

Let’s have a quick look at what’s going on here:

In line 18 we’ve imported the TemplateView from django.views.generic and in line 19 we import the Register view class from our quotes app.

In line 24, we’re using the TemplateView generic view to render the success template we created in the last lesson directly to the browser.

Line 25 is the URLconf for our user registration form.

And in line 27, we’re including Django’s authentication URLs which provide the URL and view for our login and logout views. A number of other URLs are loaded with auth.urls that we are not using in this course. If you want to dig further into the auth.urls and the cool generic authorization views they give you access too, I’ve included a link in the reference section.

Testing the Authentication System

We have made quite a few changes to the front end, so now it’s time to test to see it all works. Fire up the development server and navigate to http://127.0.0.1:8000/. At the top of the page, you should now see the logged out message.

If you’re still running the development server from earlier, you might still be logged in as admin. If you are, click on Log out so you can test the login function.

Click on the “Log in” link in the menu and the site should now redirect to the login page.

Next, on the login form click the Register here link and you should see the user registration page.

Finally, create a new user to test the registration form redirects to the success page. You should also check the admin to make sure the new user has been created OK.

Now that we have everything linked up correctly, in the next lesson we’re going to modify our quote views to restrict what users can see in the front end.

Like the Content? Grab the Book!

Other than providing you with a convenient resource you can print out, or load up on your device, buying the book also helps support this site.

Book is available in PDF, eBook or as a bundle including both PDF and ebook, plus the source code.

Restricting Users in the Front End

Now that we have set up the authentication system, all we need to do to restrict user access in the front end is to modify the views to ensure that only logged in users can access the quote system.

To require site visitors to log in before they can submit or view quotes, we must modify the views for our quote form and for our quote list display. These views are:

1. The quote request (quote_req) view;

2. The quote list (QuoteList) view; and

3. The quote detail (QuoteView) view.

Django includes built-in functionality that makes this task simple. However, we use two different approaches because quote_req is a function-based view whereas QuoteList and QuoteView are class-based views.

To modify the behavior of a function-based view we use a Python decorator. A decorator is a special function that wraps around another function and modifies its behavior. In Python, a decorator function starts with the @ symbol and must be on the line immediately before the function it modifies.

To modify the behavior of a class-based view on the other hand, we use a mixin. A mixin is a special kind of class that contains class methods that can be “mixed in” to other classes without them needing to inherit from the mixin class. We have already used a mixin when we first created the QuoteList view—the get_context_data method from Django’s MultipleObjectMixin.

Decorators and mixins are powerful features of Python and Django, which you will find yourself using regularly in your career as a Python/Django programmer.

They are also a huge topic; I will only touch on a small set of their capabilities in this lesson. I encourage you to expand your understanding of these powerful tools.

The Python wiki has some great information on decorators in Python. Django also uses decorators in several modules.

Mixins are not specific to Python, so multiple references can be found online. Django uses mixins extensively in class-based views, which are covered in detail in the Django documentation.

I have included links to additional resources for decorators and mixins in both the Django and Python sections of the course resources.

Modify the Quote Request View

As I said in the intro, the quote_req view is a function-based view, so we are going to add a decorator. Pause the video and enter the code into your editor or copy from the transcript. Note that I have snipped the middle lines in the screenshot so you can more easily see the code changes.

Here we have imported the login_required function and used it to wrap (decorate) the quote_req function in line 42. Now, when quote_req is called, login_required first checks if the user is logged in and redirects to the login view if they’re not.

Because a decorator is a function, we need to use reverse_lazy() to ensure that Django doesn’t try to evaluate the URL until runtime. So, when Django sees reverse_lazy(‘login’), it will convert it to the login URL at runtime.

In case you were wondering where the login/ URL came from, when we included django.contrib.auth.urls in the last lesson, it included URL patterns for a number of built-in views, including the login view.

Moving on to the rest of the modifications to our view, we also want the quote_req view to save the user information with the quote when it’s submitted. To add the current user’s username to the quote, in line 49, we set the commit property of the save() method to False. This creates a new instance of the Quote model without saving the record to the database.

Then in line 51, we’re setting the username field in the quote object to the current user. Django will pass the current user to the view in the user parameter of the request object, so this line sets the username field in the quote model instance to request.user. To ensure the view doesn’t crash if request.user is not set, the line is wrapped in a try/except clause that will leave username blank in case of error.

Finally, in line 54, the record is saved to the database.

Modify the Quote List View

Next, we modify the QuoteList class to only show quotes submitted by the logged in user. This is another simple process, however, because the QuoteList view is a class based view, this time we will be using a mixin.

Let’s make the modifications to our QuoteList view, and then I will explain what’s going on. Pause the video and enter the code into your editor or copy from the transcript

First, we are adding a new import at the top of the file to import the LoginRequiredMixin.

In line 25 we are adding LoginRequiredMixin to the class declaration.

In Line 26 we provide the login URL to the class. This is functionally identical to passing the login URL to the decorator in a function-based view. Note we have used the reverse_lazy() function again to evaluate the login URL at runtime, rather than hard-code it into the view.

And line 30 is another mixin. get_queryset will return a list of quotes filtered to include only those quotes that were submitted by the logged in user.

Modify the Quote Detail View

The changes we need to make to the quote detail view are identical to the changes we made in the quote list view. As we have already imported the LoginRequiredMixin class, all we need do is make the changes in the class. Pause the video and enter the code into your editor or copy from the transcript:

You might be wondering why we are adding a QuerySet that filters the quotes to only include quotes from the current user when we are retrieving a single record. This is because LoginRequiredMixin only checks if the user is logged in, it doesn’t check if the user has permission to access the quote.

Filtering by the current logged in user ensures that the quote belongs to that user. If not, the QuerySet will be empty and Django will throw a 404 (page not found) error.

You could also implement this so that Django sends some kind of “permission denied” message, but I prefer to use this method. The code is neater and the 404 error doesn’t give a would-be hacker any information on whether something interesting might exist at that URL.

Up Next…

Scroll to Top