CentOS下的Django项目——
六、用户账号的登录注销注册+七、让用户拥有自己的数据

【创建用户账号】

[应用程序users]

1.创建应用程序users:python manage.py startapp users
2.将应用程序users添加到setting.py:
打开learning_log/learning_log/settings.py,修改为

INSTALLED_APPS = [
    
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # My apps
    'learning_logs',
    'users',
]

3.包含应用程序users的URL
打开learning_log/learning_log/urls.py,添加

#匹配http://localhost:8000/users/打头的URL,后面可以继续添加目录
url(r'^users/',include(('users.urls','users'),namespace='users')),

[登录页面]

1.新建learning_log/users/urls.py,内容为

"""为应用程序users定义URL模式"""
from django.conf.urls import url
from django.contrib.auth.views import LoginView
from . import views
urlpatterns=[
	#login      http://localhost:8000/users/login/
	url(r'^login/$',LoginView.as_view(template_name='users/login.html'),name='login'), 
]

2.模板login.html
新建learning_log/users/templates/users/login.html,内容为

{%extends "learning_logs/base.html"%}
{%block content%}
  {%if form.errors%}
    <p>Your username and password didn't match.Please try again.</p>
  {%endif%}
  <form method="post" action="{%url 'users:login'%}">
  {%csrf_token%}
  {{form.as_p}}
  <button name='submit'>log in</button>
  <input type='hidden' name="next" value="{%url 'learning_logs:index'%}"/>
  </form>
{%endblock content%}

3.链接到登录页面
打开learning_log/learning_logs/templates/learning_logs/base.html,修改为

<p>
  <a href="{% url 'learning_logs:index' %}">Learning Log</a> -
  <a href="{% url 'learning_logs:topics' %}">Topics</a> -
  {%if user.is_authenticated%}
   Hello,{{user.username}}.
  {%else%}
    <a href={%url 'users:login'%}>log in</a>
  {%endif%}
</p>
{% block content %}{% endblock content %}

4.使用登录界面
浏览器打开http://localhost:8000/users/login/
ll_admin
[注销]
单击链接注销并返回主页
1.注销url
打开learning_log/users/urls.py,增加

#logout
url(r'^logout/$',views.logout_view,name='logout'),

2.视图函数logout_view()
打开learning_log/users/views.py,添加

from django.http import HttpResponseRedirect
from django.urls import reverse  #django2.0 把原来的django.core.urlresolvers包更改为了django.urls包
from django.contrib.auth import logout
def logout_view(request):
	"""注销用户"""
	logout(request)
	return HttpResponseRedirect(reverse('learning_logs:index'))

3.链接到注销视图
打开learning_log/learning_logs/templates/learning_logs/base.html,修改为

<p>
  <a href="{% url 'learning_logs:index' %}">Learning Log</a> -
  <a href="{% url 'learning_logs:topics' %}">Topics</a> -
  {%if user.is_authenticated%}
    Hello,{{user.username}}.
    <a href="{%url 'users:logout'%}">log out</a>
  {%else%}
    <a href="{%url 'users:login'%}">log in</a>
  {%endif%}
</p>
{% block content %}{% endblock content %}

[注册页面]

新用户注册页面:使用已提供表单UserCreationForm,但自己编写视图函数和模板。
1.注册页面的URL模式
打开learning_log/users/urls.py,增加

#register
url(r'^register/$',views.register,name='register'),

注:该模式与URL(http://localhost:8000/users/register)匹配
2.视图函数register()
打开learning_log/users/views.py,
修改from django.contrib.auth import logout为

from django.contrib.auth import logout,login,authenticate

添加

from django.contrib.auth.forms import UserCreationForm

添加

def register(request):
	"""register new user"""
	if request.method != 'POST':
		form=UserCreationForm()
	else:
		form=UserCreationForm(data=request.POST)
		if form.is_valid():
			new_user=form.save()
			authenticated_user=authenticate(username=new_user.username,password=request.POST['password1'])
			login(request,authenticated_user)
			return HttpResponseRedirect(reverse('learning_logs:index'))
	context={'form':form}
	return render(request,'users/register.html',context)

3.注册模板
新建learning_log/users/templates/users/register.html,内容为

{% extends "learning_logs/base.html"%}
{% block content %}
  <form method='post' action="{%url 'users:register'%}">
    {%csrf_token%}
	{{form.as_p}}
    <button name='submit'>register</button>
    <input type="hidden" name="next" value="{%url 'learning_logs:index'%}"/>
  </form>
{% endblock content %}

4.链接到注册页面
打开learning_log/learning_logs/templates/learning_logs/base.html,修改为

<p>
  <a href="{% url 'learning_logs:index' %}">Learning Log</a> -
  <a href="{% url 'learning_logs:topics' %}">Topics</a> -
  {%if user.is_authenticated%}
    Hello,{{user.username}}.
    <a href="{%url 'users:logout'%}">log out</a>
  {%else%}
    <a href="{%url 'users:register'%}">register</a> -
    <a href="{%url 'users:login'%}">log in</a>
  {%endif%}
</p>
{% block content %}{% endblock content %}

5.尝试注册
账号guest,密码ll_guest123

【让用户拥有自己的数据】

[限制数据的显示]

修改模型Topic,让每个主题属于特定的用户,这将影响条目,因为条目属于特定主题
[使用@login_required限制访问]
注:@login_required是装饰器(decorators)
1.限制对topics页面的访问
因为主题属于特定用户,所以只允许已登录的用户请求topics页面
(1)给views.py添加检查登录
打开learning_log/learning_logs/views.py,添加

from django.contrib.auth.decorators import login_required

在def topics(request):前面添加

@login_required

注:login_required检查用户是否已登录,当登录时,才运行topics()
(2)给settings.py添加重定向
为实现用户未登录,则重定向到登录页面,需修改settings.py
打开learning_log/learning_log/settings.py,在其末尾添加

#我的设置
LOGIN_URL='/users/login/'

(3)测验限制效果
浏览器打开http://localhost:8000,尝试不登录和登录查看Topics的结果
2.全面限制对项目“学习笔记”的访问
要求实现:不限制对主页、注册页面和注销页面的访问,限制对其他所有页面的访问
(1)给除index()外的每个视图添加@login_required
打开learning_log/learning_logs/views.py,修改为

@login_required	
def topics(request):
……
@login_required	
def topic(request,topic_id):
……
@login_required	
def new_topic(request):
……
@login_required	
def new_entry(request,topic_id):
……
@login_required		
def edit_entry(request,entry_id):

[将数据关联到用户]

注意:只要将最高层的数据关联到用户,本项目是“主题”归属于特定用户就能确定“条目”归属
1.修改模型Topic
打开learning_log/learning_logs/models.py
添加

from django.contrib.auth.models import User

将Topic模型修改为

class Topic(models.Model):
	"""learning topics"""
	text = models.CharField(max_length=200)
	date_added = models.DateTimeField(auto_now_add=True)
	owner=models.ForeignKey(User,on_delete=models.CASCADE)
	def __str__(self):
		return self.text

2.确定当前有哪些用户
查看已创建的所有用户的ID:
启动Django shell会话:python manage.py shell
from django.contrib.auth.models import User
User.objects.all()
for user in User.objects.all():
print(user.username,user.id)
可查看到用户ll_admin 1 guest 2
3.迁移数据库
(1)迁移数据库
确认数据:python manage.py makemigrations learning_logs
根据提示:选择1(现在提供默认值)(注:2是退出后在models.py里添加默认值)
根据提示,由于ll_admin的ID是1,这里为了让所有既有主题都关联到管理用户ll_admin,选1
执行迁移:python manage.py migrate
注:如果想要一个全新的数据库,可以执行命令python manage.py flush,此时会重建数据库的结构,此时必须重新创建超级用户且原来的数据都将丢失
(2)验证迁移效果
启动Django shell会话:python manage.py shell
from learning_logs.models import Topic
for topic in Topic.objects.all():
print(topic,topic.owner)
此时会显示ll_admin下的所有主题

[将新主题关联到当前用户]

此时添加新主题有问题,需要创建新主题时,指定其owner字段的值
打开learning_log/learning_logs/views.py,将new_topic()修改为

def new_topic(request):
	"""add new topic"""
	if request.method !='POST':
		form=TopicForm
	else:
		form =TopicForm(request.POST)
		if form.is_valid():
			new_topic=form.save(commit=False)  
			new_topic.owner=request.user
			new_topic.save()
			return HttpResponseRedirect(reverse('learning_logs:topics'))
	context={'form':form}
	return render(request,'learning_logs/new_topic.html',context)

[只允许用户访问自己的主题]

(1)实现只向用户显示属于自己的主题
打开learning_log/learning_logs/views.py,修改函数topics()为

def topics(request):
	"""show all topics"""
	topics=Topic.objects.filter(owner=request.user).order_by('date_added') 
	context={'topics':topics}
	return render(request,'learning_logs/topics.html',context)

(2)测试效果
在浏览器里分别登录两个用户,查看Topics,此时只有ll_admin下有主题

[保护用户的主题]

限制对显示单个主题的页面的访问,防止已登录用户输入http://localhost:8000/topics/1(该内容不属于这个用户)
方法:在视图函数topic()获取请求的条目前执行检查
打开learning_log/learning_logs/views.py,将

from django.http import HttpResponseRedirect

修改为

from django.http import HttpResponseRedirect, Http404

def topic(request,topic_id):
	"""show one of the topics and its' items"""
	topic=Topic.objects.get(id=topic_id)
	entries=topic.entry_set.order_by('-date_added')
	context={'topic':topic,'entries':entries}
	return render(request,'learning_logs/topic.html',context)

修改为

def topic(request,topic_id):
	"""show one of the topics and its' items"""
	topic=Topic.objects.get(id=topic_id)
	if topic.owner != request.user:
		raise Http404
	entries=topic.entry_set.order_by('-date_added')
	context={'topic':topic,'entries':entries}
	return render(request,'learning_logs/topic.html',context)

[保护页面edit_entry]

防止已登录用户(guest)输入http://localhost:8000/edit_entry/1(该内容不属于这个用户)
打开learning_log/learning_logs/views.py,将edit_entry()修改为

def edit_entry(request,entry_id):
	"""edit item"""
	entry=Entry.objects.get(id=entry_id)
	topic=entry.topic
	if topic.owner !=request.user:
		raise Http404
	if request.method !='POST':
		form=EntryForm(instance=entry)
	else:
		form=EntryForm(instance=entry,data=request.POST)
		if form.is_valid():
			form.save()
			return HttpResponseRedirect(reverse('learning_logs:topic',args=[topic.id]))
	context={'entry':entry,'topic':topic,'form':form}
	return render(request,'learning_logs/edit_entry.html',context)

[保护页面new_entry]

现在一个用户可在另一个用户的学习笔记中添加条目(如guest输入http://localhost:8000/new_topic/2),防止发生这种事
打开learning_log/learning_logs/views.py,将函数new_entry()修改为

def new_entry(request,topic_id):
	"""add new item about one of the topics"""
	topic=Topic.objects.get(id=topic_id)
	if topic.owner != request.user:
		raise Http404
	if request.method !='POST':
		form=EntryForm()
	else:
		form=EntryForm(data=request.POST)

		if form.is_valid():
			new_entry=form.save(commit=False)
			new_entry.topic=topic
			new_entry.save()
			return HttpResponseRedirect(reverse('learning_logs:topic',args=[topic_id]))
	context={'topic':topic,'form':form}
	return render(request,'learning_logs/new_entry.html',context)

[优化核实当前主题关联到的用户为当前登录的用户]

1.在view.py中,已经核实当前主题关联到的用户为当前登录的用户。将执行这种检查的代码放在一个名为check_topic_owner()的函数中,并在恰当的地方调用这个函数
打开learning_log/learning_logs/views.py,添加

def check_topic_owner(request,topic_id):
	"""检查是否是当前登录用户的主题"""
	topic=Topic.objects.get(id=topic_id)
	if topic.owner != request.user:
		raise Http404

修改topic()、new_entry()、edit_entry()中的

if topic.owner != request.user:
	raise Http404

check_topic_owner(request,topic.id)