Adding users to our site is handled by Django using the same concept of models. Each user's username, email, and password (in encrypted form) are stored in the database and can be used to authenticate users who try to login.
We need to tell Django how to handle authentication in order for logging in
and out to work. In buildup/settings.py
, add the following lines into the
MIDDLEWARE_CLASSES
block so it looks like this:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
and then add the following lines to INSTALLED_APPS
so it looks like this:
INSTALLED_APPS = (
'buildup',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
)
and finally, add the following settings to the bottom of the file:
LOGIN_REDIRECT_URL = '/facts/'
LOGIN_URL='/login/'
LOGOUT_URL='/logout/'
These settings tell Django to enable user authentication and also the default urls for logging in and out.
Now we need to tell Django to create the User model for us. Run
foreman run python manage.py syncdb
and create a superuser with a username, email and password when it asks.
In buildup/urls.py
, add a /login/
and /logout/
url. Note that these urls
will be handled by code in django.contrib.auth
, so we don't have to create them!
url(r'^login/$', 'django.contrib.auth.views.login', name='login'),
url(r'^logout/$', 'django.contrib.auth.views.logout', name='logout'),
Now let's create the folder buildup/templates/registration
and add a login
and logout
template. In buildup/templates/registration/login.html
, put the following:
<html>
<head>
<title>Login</title>
</head>
<body>
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{{ form }}
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</body>
</html>
We're using a form to send data to Django, and then Django will check if the username
and password are correct. Notice how similar this template is to the new_facts.html
template we created earlier - we can simplify this later on by sharing parts of the
templates between the two.
In buildup/templates/registration/logged_out.html
, put a simple message like this:
You are now logged out.
Now when you run the server, you should be able to visit http://127.0.0.1:8000/login/
and enter your superuser name and password. When you log in successfully, you will be redirected
to the /facts/
page. You can also use http://127.0.0.1:8000/logout/ to switch users.
Just having user authentication by itself is pretty boring - let's try modifying
the /facts/new
page to only allow logged in users to create a fact. Django has
a helper login_required
which will block pages for users who aren't logged in.
In buildup/views.py
import the login_required
helper at the top of the file:
from django.contrib.auth.decorators import login_required
and then place @login_required
above the new_fact
function so it looks like
this:
@login_required
def new_fact(request):
The @login_required
is a "decorator", which is a way in Python to modify a function
(in this case, new_fact
) with some other behavior: if we're not logged in, redirect
the user to the login page, otherwise run the "decorated" function.
Thats it! Now we have a login protected page for creating facts.
Now new_fact
requires a user to be logged in, but it still asks the user to
enter an author for the fact! We can do this automatically because we know the name
of the user.
First, let's modify the Fact
model in buildup/models.py
to use a "Foreign key"
to the user. Replace the author
field in the Fact
class with the following:
author = models.ForeignKey('auth.User')
This tells Django that each Fact
should have a foreign key to a User, or put simply,
each Fact is associated with a User.
Now that we have modified the Fact model, we have to migrate the database. Normally we can tell Django to "migrate" our models to the new definition, but since we added the User model, we have to start from scratch. Let's remove the database and any existing migrations:
rm buildup.db
rm -r buildup/migrations
and then we can initialize the database with the new model definitions:
foreman run python manage.py syncdb
Now our Fact model has a key to the User it was created by, but we still have to
change the new_facts
view to reflect that. We want the author field of the Fact
to be set automatically by the view like created_date
, so make the following changes:
- The
FactForm
should no longer have anauthor
field because we will set it automatically. - When creating the
Fact
, theauthor
field should be set to a User model instead of a string. You can userequest.user
to get the current User model.
Once this is complete, you should be able to create facts after logging in with your account.
Since users are just Django models, we can create new ones in the Django shell
just like we did for Facts. Enter the shell with foreman run python manage.py shell
and try to create a new user:
from django.contrib.auth.models import User
user = User.objects.create_user('bob', '[email protected]', 'password1234')
user.save()
Now you can create a bunch of users and add Facts associated with each one!
Try adding a /register
url which lets you create a new user on the website. Look
at the view and template for new_fact
to see how you can create and save a model
based on user input.