Part III: Implementing a Full-Featured Website
| HOUR 16 Managing Sessions and Cookies |
|
| HOUR 17 Customizing Models in the Admin Interface |
|
| HOUR 18 Customizing the Admin Interface |
|
Hour 14. Managing Site Users
What You'll Learn in This Hour |
|
So far in this book, we have neglected one of the most basic parts of most websites—registration and authentication. Django includes several components that aid in registration and authentication.
This hour discusses getting started with the registration and authentication process. We will cover creating User and Group objects as well as adding custom permissions to your models. We will also discuss setting permissions that allow you to control access to parts of the website.
By the Way
Django currently provides a whole series of manipulator objects and built-in views to handle user creation, logins, logouts, password changes, and so on. Those manipulators are currently based on the old form framework, so I avoid using them. The examples that I will discuss cover creating your own Forms using the newforms library. I also will show you how to customize the registration and login process.
Adding Users
The first step in adding registration and authentication to the website is to implement some kind of User object so that you can determine who is trying to access the site. Hour 3, "Adding Models and Objects to Your Website," introduced the admin interface and showed you how to use it to create User objects. Django implements User objects in the admin interface to control access. User objects can also be accessed from your views as part of registration, authentication, and permission control.
The following sections discuss User objects and how to create them from a view function.
Understanding User Objects
Django User objects give you a way to control access to your website by providing a means to force requests to come from authenticated sources. Implementing User objects also allows you to define permissions to specific areas of the website.
The following list describes the fields of a User object:
username is required. It accepts up to 30 alphanumeric and underscore characters.
first_name is optional. It accepts up to 30 characters.
last_name is optional. It accepts up to 30 characters.
email is optional. It accepts a valid email address.
password is required. The password is actually stored as a hash that describes the raw password.
is_staff is Boolean. It defaults to False. It specifies whether the user can access the admin site.
is_active is Boolean. It defaults to True. It specifies whether the user can log into the website.
is_superuser is Boolean. It defaults to False. It specifies whether the user has all permissions to the admin site.
last_login is a datetime object containing the time when the user last logged in.
date_joined is a datetime object containing the time when the User object was created.
groups is a many-to-many field that lists Group objects that the User belongs to.
user_permissions is a many-to-many field that lists permissions assigned to the User.
Anonymous Users
Django implements a special User class to handle anonymous requests. If a request comes from a user who is not currently logged in, the user attribute of the HttpRequest object is a django.contrib.auth.model.AnonymousUser object. The AnonymousUser object is like a normal User object, with the following exceptions:
id is always None.
is_staff is always False.
is_superuser is always False.
is_active is always True.
groups and user_permissions are always empty.
is_anonymous() returns True instead of False.
has_perm() always returns False.
The set_password(), check_password(), save(), delete(), set_groups(), and set_permission() functions raise a NotImplementedError.
Creating User Objects in Views
The admin interface is an excellent way to create objects. However, you will likely want to automate the creation of User objects by providing a web page that allows people to register with your website. When new users register, they will be able to create their own User objects and immediately access the website using their new account.
You can create User objects from the view function by calling the User.objects create_user(username, email, password=None) function and passing it username, email, and password as arguments.
For example, the following code creates a new User object for a user named Tim:
from django.contrib.auth.models import User
user = User.objects.create_user('Tim', 'tim@website.com', 'timspass')
Watch Out!
Not specifying a password when using the create_user() function is not the same as entering a blank string for the password. An unuseable password is added, and the User object function has_useable_password() returns True. This feature can actually be useful if you are importing User objects from some other source using an automated method and you do not want to give them all the same generic password.
Changing User Passwords in Views
You can change user passwords in the Django admin interface. However, you may also want to allow users to change their own passwords. To do so, you can create a view that renders a password change form similar to that of a login form. Then, inside the view, use the User.set_password() function on the User object of the request to change the password of the currently logged-in user.
The following code snippet shows the POST handler from a password change view function that sets the password of the current User object:
if request.method == 'POST':
if request.POST['submit'] == 'Change Password':
newPass = request.POST['password']
request.user.set_password(newPass)
request.user.save()
return HttpResponseRedirect('/home')
This section looks at a practical example of adding a User object to the iFriends website using a custom view. The Person objects are tied to the User objects, so you need to create them at the same time.
You will create a new form template and view function that will allow you to prompt the user for information to use when creating a new User object. You will use the information to create a new Person object and redirect the user to the details page for that object.
Follow these steps to create and enable the new user creation view:
1. | Create and open a file called iFriends/templates/registration/create_user.html in an editor.
| 2. | Add the code shown in Listing 14.1 to the file to extend the site base template and display a form that will be used to create a new user. | 3. | Save the iFriends/templates/registration/create_user.html file.
| 4. | Open the iFriends/Home/views.py file in an editor.
| 5. | Add the following lines of code, shown in Listing 14.2, to import the User and HttpResponseRedirect objects:
from django.http import HttpResponseRedirect
from django.contrib.auth.models import User
| | | 6. | Add the following lines of code, shown in Listing 14.2, to define a NewUserForm class that you will display in the create_user.html template file to collect the information you will need to create the User and Person objects. The password field is set to the PasswordInput Widget so that the password is not displayed in the form:
gender_list = (('M', 'Male'), ('F', 'Female' ))
class NewUserForm(forms.Form):
username = forms.CharField(max_length=30)
password = forms.CharField(max_length=20,
widget=forms.PasswordInput())
first = forms.CharField(max_length=20)
last = forms.CharField(max_length=20)
gender = forms.ChoiceField(choices=gender_list)
email = forms.EmailField(max_length=30)
| 7. | Add the following lines of code, shown in Listing 14.2, to define a create_user() view function that creates an instance of the NewUserForm and renders it using the create_user.html template:
def create_user(request):
message = 'Create New User'
uForm = NewUserForm()
. . .
return render_to_response('registration/create_user.html',{
'uForm': uForm,
'message': message })
| 8. | Add the following lines of code to the create_user() view, shown in Listing 14.2, to handle a POST request from the form's Create button. This generates an instance of the NewUserForm from the POST data:
if request.method == 'POST':
if request.POST['submit'] == 'Create':
postDict = request.POST.copy()
uForm = NewUserForm(postDict)
Did you Know?
The reason we build an instance of the NewUserForm data is to handle errors in the form. If the user creation isn't successful, the form is redisplayed with contents and any errors that were found during validation.
| 9. | Add the following lines of code to the create_user() view, shown in Listing 14.2, to create a new User object from the data in the POST request:
user = User.objects.create_user(
postDict['username'],
postDict['email'],
'password')
user.last_name = postDict['last']
user.first_name = postDict['first']
user.save()
| | | 10. | Add the following lines of code to the create_user() view, shown in Listing 14.2, to create an instance of a Person form using the name, gender, and email from the POST and the User object that was created in step 9. The favoriteURL and desc Fields need to be filled in with some initial values because they are required in the Person model:
perDict = {}
perDict['name'] = "%s %s" % (postDict['first'], postDict['last'])
perDict['email'] = postDict['email']
perDict['gender'] = postDict['gender']
perDict['favoriteURL'] = 'http://www.iFriends.org'
perDict['desc'] = 'New User'
perDict['userID'] = user.id
SaveForm = forms.form_for_model(Person)
pForm = SaveForm(perDict)
| 11. | Add the following lines of code to the create_user() view, shown in Listing 14.2, to validate the People form. If an error occurs, delete the User object and set the error message:
if pForm.is_valid():
. . .
else:
message = 'Form Data Error'
user.delete()
| 12. | Add the following lines of code to the create_user() view, shown in Listing 14.2, to save the People form. If an error occurs, delete the User object and set a database error message:
try:
p = pForm.save()
return HttpResponseRedirect('/People/Info/%d/' % p.id)
except:
message = 'Database Error.'
user.delete()
| 13. | Save the iFriends/Home/views.py file.
| 14. | Open iFriends/urls.py in an editor.
| 15. | Add the following line of code to enable the create_user() view:
(r'^NewUser/$', 'iFriends.Home.views.create_user'),
| | | 16. | Access the following URL in a web browser to verify that the create_user() view, shown in Figure 14.1, correctly displays the creation form:
http://127.0.0.1:8000/NewUser/
| | | 17. | Add valid data to the form, and click the Create button to create the new User and Person objects. A Person details() view similar to the one shown in Figure 14.2 should be displayed for the new Person object.
| | | 18. | Verify that the new User object was created in the admin site by accessing the following URL. Click the new User object you created to bring up the User object admin page, shown in Figure 14.3:
http://127.0.0.1:8000/admin/auth/user/
|
Listing 14.1. Full Contents of iFriends/templates/registration/create_user.html
{% extends "iFriends_base.html" %}
{% block title %}Create User Form{% endblock %}
{% block content %}
<h3>{{ message }}</h3>
<form method="post" action=".">
<table>
{{ uForm }}
</table>
<input type="submit" name="submit" value="Create" />
</form>
{% endblock %}
|
Listing 14.2. Full Contents of iFriends/Home/views.py
Code View: from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect
from datetime import datetime
from django import newforms as forms
from iFriends.People.models import Person, Blogfrom iFriends.Quotes.models import Quote
from django.contrib.auth.models import User
class EmailForm(forms.Form):
title = forms.CharField(max_length=50,
widget=forms.TextInput(attrs={'size':'50'}))
sender = forms.EmailField(max_length=30,
widget=forms.TextInput(attrs={'size':'30'}))
date = forms.DateTimeField()
text = forms.CharField(widget=forms.Textarea(
attrs={'rows':'6','cols':'75'}))
gender_list = (('M', 'Male'), ('F', 'Female' ))
class NewUserForm(forms.Form):
username = forms.CharField(max_length=30)
password = forms.CharField(max_length=20,
widget=forms.PasswordInput())
first = forms.CharField(max_length=20)
last = forms.CharField(max_length=20)
gender = forms.ChoiceField(choices=gender_list)
email = forms.EmailField(max_length=30)
def contact_view(request):
eForm = EmailForm()
return render_to_response('home/contact_form.html', { 'eForm':eForm })
def home_view(request):
quotes = Quote.objects.all()
pList = Person.objects.all()
bList = Blog.objects.all()
return render_to_response('home/homepage.html', {
'quotes': quotes,
'pList': pList,
'bList': bList})
def create_user(request):
message = 'Create New User'
uForm = NewUserForm()
if request.method == 'POST':
if request.POST['submit'] == 'Create':
postDict = request.POST.copy()
uForm = NewUserForm(postDict)
try:
#create User object
user = User.objects.create_user(postDict['username'],
postDict['email'],
postDict['password'])
user.last_name = postDict['last']
user.first_name = postDict['first']
user.save()
#Create a Person object
perDict = {}
perDict['name'] = "%s %s" % (postDict['first'], postDict['last']) perDict['email'] = postDict['email']
perDict['gender'] = postDict['gender']
perDict['favoriteURL'] = 'http://www.iFriends.org'
perDict['desc'] = 'New User'
perDict['userID'] = user.id
SaveForm = forms.form_for_model(Person)
pForm = SaveForm(perDict)
if pForm.is_valid():
try:
p = pForm.save()
return HttpResponseRedirect('/People/Info/%d/' % p.id)
except:
message = 'Database Error.'
user.delete()
else:
message = 'Form Data Error'
user.delete()
except:
message = 'User creation Error'
return render_to_response('registration/create_user.html',{
'uForm': uForm,
'message': message })
|
|