注销页面

为注销链接添加一个url模式,编写一个视图函数,并在base.html中添加一个注销链接

注销url.py

修改的是users/urls.py

1
2
3
4
5
6
7
8
9
10
11
"""为应用程序users定义url模式"""
from django.conf.urls import url
from django.contrib.auth.views import LoginView

from . import views

urlpatterns = [
# login
url(r'^login/$',LoginView.as_view(template_name='users/login.html'),name='login'),
url(r'^logout/$',views.logout_view,name='logout'), # 新增
]

视图函数logout_view

1
2
3
4
5
6
7
8
9
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth import logout
# Create your views here.

def logout_view(request):
logout(request)
return HttpResponseRedirect(reverse('learning_logs:index'))

链接到注销视图

base.html中添加这个链接,放在标签{百分号 if user.is_authenticated 百分号}中,使得仅当用户登录后才能看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{% draw %}
<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> # new
{% else %}
<a href="{% url 'users:login' %}">log in</a>
{% endif %}
</p>
{% block content %}{% endblock content %}
{% endraw %}

注册页面

使用Django提供的表单UserCreationForm,但编写自己的模板和视图

注册页面的url模式

包含在了users/urls.py

1
2
3
4
5
6
7
8
9
10
11
12
"""为应用程序users定义url模式"""
from django.conf.urls import url
from django.contrib.auth.views import LoginView

from . import views

urlpatterns = [
# login
url(r'^login/$',LoginView.as_view(template_name='users/login.html'),name='login'),
url(r'^logout/$',views.logout_view,name='logout'),
url(r'^register/$',views.register,name='register'), # new
]

视图函数register()

在注册页面首次被请求时,视图函数register()需要显示一个空的注册表单,并在用户提交填写好的注册表单时对其进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django.contrib.auth import login,logout,authenticate  # new
from django.contrib.auth.forms import UserCreationForm # new
# Create your views here.
def logout_view(request):
logout(request)
return HttpResponseRedirect(reverse('learning_logs:index'))

# 定义注册视图函数
def register(request): # new
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)

注册模板

注册页面的模板和登录页面的模板类似,register.html保存在登录页面所在目录中

1
2
3
4
5
6
7
8
9
10
11
12
{% draw %}
{% 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 %}
{% endraw %}

注:使用了方法as_p,使得表单中正确显示所有字段,包括错误消息

链接到注册页面

base.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% draw %}
<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>
<a href="{% url 'users:register'%}">register</a> # new
{% endif %}
</p>
{% block content %}{% endblock content %}
{% endraw %}

注:已经登录的用户看到的是注销链接和问候语,没有登录的则显示登录和注册

让用户拥有自己的数据

用户能够输入其专用的数据,因此创建一个系统,确定各项数据所属的用户,在限制对页面的访问,让用户只能使用自己的数据

使用@login_required限制访问

对于某些页面,只能允许已经登录的用户访问。装饰器(decorator)是放在函数前面定义前面的指令

限制对topics页面的访问

每个主题都归特定的用户所有,因此只允许已登录的用户请求topics页面,在learning_logs/views.py中修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from .forms import TopicForm,EntryForm

from django.contrib.auth.decorators import login_required # new

# Create your views here.
def index(request):
"""学习笔记的主页"""
return render(request,'learning_logs/index.html')
@login_required() # new
def topics(request):
"""显示所有主题"""
topics = Topic.objects.order_by('date_added')
context = {'topics':topics}
return render(request,'learning_logs/topics.html',context)

注:login_required()的代码检查用户是否已登录,仅当用户登录后时,django才运行topics()的代码,如果没有登录就重定向到登录页面,为了实现重定向,修改settings.py,在其末尾添加一行设置

1
2
3
4
5
6
7
8
9
10
11
12
USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'

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

全面限制对项目的访问

将不限制对主页、注册页面和注销页面的访问,并限制对其他所有页面的访问。

所以在learning_logs/views.py中,对除了index()外的每个视图都应用了装饰器@login_required

==操作方式和上述方式一样,只需要添加一行代码即可==

将数据关联到用户

models.py进行修改

1
2
3
4
5
6
7
8
9
from django.db import models
from django.contrib.auth.models import User # new
# Create your models here.
class Topic(models.Model):
text = models.CharField(blank=True,max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User,on_delete=models.CASCADE) # new
def __str__(self):
return self.text

确定有哪些用户

迁移数据库时,django将对数据库进行修改,使其能够存储主题和用户之间的关联,为了执行迁移,必须知道该将各个即有主题关联到哪个用户。将既有主题都关联到一个用户,如超级用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(venv) D:\资料\learning_log>python manage.py makemigrations learning_logs
System check identified some issues:

WARNINGS:
?: (2_0.W001) Your URL pattern '^users/' has a route that contains '(?P<', begins with a '^', or ends with a '$'. This was likely an oversight when migrating to django.urls.path().

You are trying to add a non-nullable field 'owner' to topic without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> 1
Migrations for 'learning_logs':
learning_logs\migrations\0003_auto_20191118_1713.py
- Add field owner to topic
- Alter field text on topic

注:首先执行了makemigrations,指出给既有模型Topic添加一个必不可少的字段,没有默认值;

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

views.py中,对函数topics()进行修改

1
2
3
4
5
6
7
8
9
10
11
# Create your views here.
def index(request):
"""学习笔记的主页"""
return render(request,'learning_logs/index.html')
@login_required()
def topics(request):
"""显示所有主题"""
topics = Topic.objects.filter(owner=request.user).order_by('date_added') # new
topics = Topic.objects.order_by('date_added')
context = {'topics':topics}
return render(request,'learning_logs/topics.html',context)

注:用户登录后,request对象将有一个user属性,这个属性存储了有关用户的信息,Topic.objects.filter(owner=request.user)让Django只能从数据库中获取owner属性为当前用户的Topic对象

保护主题

也就是现在情况下,每个用户登录时都能看到原先加入的条目信息,而不是专属自己的

更改views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.http import HttpResponseRedirect,Http404  # new Http404
from django.urls import reverse
from .forms import TopicForm,EntryForm

from django.contrib.auth.decorators import login_required

# Create your views here.
@login_required()
def topic(request,topic_id):
topic = Topic.objects.get(id=topic_id)

# 确认请求的主题属于当前用户
if topic.owner != request.user: # new
raise Http404

保护页面edit_entry

页面edit_entry的url为https://localhost:8001/edit_entry/entry_id,其中`entry_id`是一个数字,设置保护页面,禁止用户通过输入类似于其前面的url来访问用户的条目

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
login_required()
def edit_entry(request,entry_id):
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
if topic.owner != request.user: # new
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)

将新主题关联到用户

修复代码

views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
@login_required()
def new_topic(request):
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)