《Python编程:从入门到实践》第19章笔记:用户/用户注册/身份验证
接上篇django最基本的一些日常用法,这是第19章笔记,希望在做“动手试一试”的时候可以让自己方便参考。
这一章实现了两个功能:
1、让用户能够添加主题Topic和条目Entry,以及编辑既有的条目。
2、建立一个用户注册和身份验证系统,让用户能够注册账户,进而登录和注销。
让用户能够输入数据
一、让用户能够添加新主题(Topic)
关于表单
让用户输入并提交信息的页面都是表单。用户输入时,我们需要进行验证,确认提供的信息是正确的数据类型,而不是恶意的信息。然后我们再对这些有效信息进行处理,并将其保存到数据库的合适地方。
在Django中,可使用ModelForm创建表单
在models.py所在目录创建forms.py文件。
from django import forms
from .models import Topic
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
其中内嵌的Meta类告诉Django根据哪个模型创建表单,以及在表单中包含哪些字段。
URL
在learning_logs/urls.py中
path('new_topic/', views.new_topic, name='new_topic'),
视图
views.py中加入如下内容
from django.http import HttpResponseRedirect
from django.urls import reverse
from .forms import TopicForm
def new_topic(request):
# 如果未提交数据,创建一个新表单
if request.method != 'POST':
form = TopicForm()
# 否则对数据进行处理
else:
# 用户输入的数据存在request.POST中
form = TopicForm(request.POST)
# 检查是否有效,有效就保存
if form.is_valid():
form.save()
#保存后就可离开这个页面了,用reverse()获取也页面topics的URL
return HttpResponseRedirect(reverse('learning_logs:topics'))
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
导入部分
- 导入HttpResponseRedirect类,用户提交主题后使用这个类将用户重定向到网页topics。
- 函数reverse()根据指定的URL模型确定URL。
关于GET请求和POST请求
从服务器读取数据的页面,使用GET请求;需要通过表单提交信息时,通常使用POST请求。
函数new_topic()将请求对象作为参数。用户初次请求该网页时,其浏览器将发送GET请求;用户填写并提交表单时,其浏览器将发送POST请求。根据请求的类型,我们可以确定用户请求的是空表单(GET请求)还是要求对填写好的表单进行处理(POST请求)
模板
创建new_topic.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Add a new topic:</p>
<form action="{% url 'learning_logs:new_topic' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">add topic</button>
</form>
{% endblock content %}
- Django使用模板标签{% csrf_token %}来防止攻击者利用表单来获取对服务器未经授权的访问(跨站请求伪造)
- 修饰符as_p让Django以段落格式渲染所有表单元素。
链接
在页面topics中添加一个到页面new_topic的链接
加入
<a href="{% url 'learning_logs:new_topic' %}">Add a new topic:</a>
二、让用户能够添加新条目(Entry)
表单
form.py
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
labels = {'text': ''}
widgets = {'text': forms.Textarea(attrs={'cols': 80})}
其中设置属性widgets可以覆盖Django选择的默认小部件。这里让Django使用forms.Textarea,定制来字段'text'的输入小部件,将文本区域的宽度设置为80列。
URL
在learning_logs/urls.py中加入:
path('new_entry/<int:topic_id>', views.new_entry, name='new_entry'),
视图
views.py
def new_entry(request, topic_id):
_topic = Topic.objects.get(id=topic_id)
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)
这个与前面new_topic()类似,区别是
- 调用save()时,传递来实参commit=False,让Django创建一个新的Entry对象,并将其存储到new_entry中,但不将它保存到数据库中。
- 调用reverse()时,列表args,其中包含在URL中的所有实参,这里列表中只有一个元素topic_id。
模板
new_entry.html
{% extends "learning_logs/base.html" %}
{% block content %}
<P><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></P>
<p>Add a new entry:</p>
<form action="{% url 'learning_logs:new_entry' topic.id %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">add entry</button>
</form>
{% endblock content %}
其中,表单的实参action包含URL中的topic_id,让视图函数能够将新entry关联到正确的主题。
链接
在topic.html中加入
<p>
<a href="{% url 'learning_logs:new_entry' topic.id %}">add new entry</a>
</p>
三、让用户能够编辑既有条目
URL
path('edit_entry/<int:entry_id>', views.edit_entry, name='edit_entry'),
视图
def edit_entry(request, entry_id):
entry = Entry.objects.get(id=entry_id)
_topic = entry.topic
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)
模板
edit_entry.html
{% extends "learning_logs/base.html" %}
{% block content %}
<P><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></P>
<p>Edit entry:</p>
<form action="{% url 'learning_logs:edit_entry' entry.id %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">save changes</button>
</form>
{% endblock content %}
链接
在topic.html中加入
<p>
<a href="{% url 'learning_logs:edit_entry' entry.id %}">edit entry</a>
</p>
用户注册和身份验证系统
一、创建用户账户
创建应用程序users
输入命令:python manage.py startapp users
将users添加到settings.py中:在INSTALLED_APPS加入 'users'
应用程序users的URL:在项目根目录的urls.py 加入path('users/', include('users.urls')),
登录页面
在users中新建一个urls.py
from django.urls import path
from django.contrib.auth.views import LoginView
from . import views
app_name = 'users'
urlpatterns = [
path('login/', LoginView.as_view(template_name='users/login.html'),
name='login'),
]
这里书上是旧版,前面引用的是from django.contrib.auth.views import login,而新版的Django中内置登录视图不再是函数了,而是类。类视图有个as_view方法,template_name是类视图中的一个变量,默认值是"registration/login.html"。
模板
在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 %}
链接
在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>
用户注销
让用户只需单击一个链接就能注销并返回到主页。
URL
users/urls.py中:path('logout/', views.logout_view, name='logout'),
视图
users/views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth import logout
def logout_view(request):
logout(request)
return HttpResponseRedirect(reverse('learning_logs:index'))
链接
在在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>
注册界面
url:path('register/', views.register, name='register'),
视图
views.py变成这样
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.forms import UserCreationForm
# Create your views here.
def logout_view(request):
logout(request)
return HttpResponseRedirect(reverse('learning_logs:index'))
def register(request):
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
{% 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 %}
链接
base.html加入<a href="{% url 'users:register' %}">register</a>
二、让用户拥有自己的数据
在这里要确定各项数据所属的用户,再限制对页面的访问,让用户只能使用自己的数据。
使用@login_required限制访问
在函数前面加上这个装饰器。它的代码会检查用户是否已经登录,仅当登录时,才运行下面函数的代码。如果未登录就重定向到登录界面。
为了实现重定向,修改settings.py,在末尾添加:LOGIN_URL = '/users/login/'
然后把learning_logs/views.py中除了index的每个函数都加上这个装饰器。
将数据关联到用户
修改models.py中的Topic
from django.contrib.auth.models import User
再在Topic类中加上字段owner:owner = models.ForeignKey(User)
可以通过shell查看现在有哪些用户:

迁移数据库
python manage.py makemigrations learning_logs
出现提示,选1
It is impossible to add a non-nullable field 'owner' to topic without specifying a default. This is because 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 manually define a default value in models.py.
Select an option:
下面输入的值是将主题(Topic)关联到的用户id号,这里1,关联到eisen
Please enter the default value as valid Python.
The datetime and django.utils.timezone modules are available, so it is possible to provide e.g. timezone.now as a value.
Type 'exit' to exit this prompt
>>> 1
然后python manage.py migrate
然后可通过shell看到每个Topic所属的User了,都是eisen。

只允许用户访问自己的主题
经过上面操作,虽然......
但是登录了eisen后,复制这个页面的URLlocalhost:8000/topics/2,登录JX帐号后再输入这个URL也看到下面的内容了。

做点修改即可,views.py中topics函数中,加个fliter,让Django只从数据库中获取owner属性为当前用户的Topic对象。
_topics = Topic.objects.filter(owner=request.user).order_by('date_added')
再限制用户对单个主题的页面的访问。在topic函数中,加个判断,如果不属于这个用户就404
@login_required
def topic(request, topic_id):
_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中也一样加入这个判断if _topic.owner != request.user:raise Http404
新主题创建时也应关联到当前用户
new_topic函数中,判断有效后,先调用form.save并传递实参commit=False,先修该新主题,在将其保存到数据库中。再将owner属性设置为当前用户。
if form.is_valid():
_new_topic = form.save(commit=False)
_new_topic.owner = request.user
_new_topic.save()
return HttpResponseRedirect(reverse('learning_logs:topics'))
于是就成功了。
最后贴几张图:







《Python编程:从入门到实践》第19章笔记:用户/用户注册/身份验证的更多相关文章
- #Python编程从入门到实践#第四章笔记
#Python编程从入门到实践#第四章笔记 操作列表 1.遍历列表 使用for循环,遍历values列表 for value in values: print(value) 2.数字列表 使 ...
- #Python编程从入门到实践#第三章笔记
列表简介 1.什么是列表 列表:由一系列按也顶顺序排列的元素组成.元素之间可以没有任何关系. 列表:用方括号[]表示,并用逗号分隔其中元素.名称一般为复数 2.访问元素 (1)列表是有序集合 ...
- 《Python编程从入门到实践》第二章_变量和简单数据类型
什么是变量呢? 举例: >>> message = "Hello,Python!" >>> print (message) Hello,Pyth ...
- 设置应用程序的样式并对其进行部署——《Python编程从入门到实践》
我们将使用应用程序django-bootstrap3为Web应用程序设计样式.我们将把项目"学习笔记"部署到Heroku,这个网站能让我们们将项目推送到其服务器,让任何有网络连接的 ...
- Python编程从入门到实践笔记——函数
Python编程从入门到实践笔记——函数 #coding=gbk #Python编程从入门到实践笔记——函数 #8.1定义函数 def 函数名(形参): # [缩进]注释+函数体 #1.向函数传递信息 ...
- Python编程从入门到实践
Python编程从入门到实践1 起步2 变量和简单数据类型3 列表简介4 操作列表5 if语句6 字典7 用户输入和while循环8 函数9 类10 文件和异常11 测试代码12 武装飞船13 外星人 ...
- Python编程从入门到实践笔记——异常和存储数据
Python编程从入门到实践笔记——异常和存储数据 #coding=gbk #Python编程从入门到实践笔记——异常和存储数据 #10.3异常 #Python使用被称为异常的特殊对象来管理程序执行期 ...
- Python编程从入门到实践笔记——文件
Python编程从入门到实践笔记——文件 #coding=gbk #Python编程从入门到实践笔记——文件 #10.1从文件中读取数据 #1.读取整个文件 file_name = 'pi_digit ...
- Python编程从入门到实践笔记——类
Python编程从入门到实践笔记——类 #coding=gbk #Python编程从入门到实践笔记——类 #9.1创建和使用类 #1.创建Dog类 class Dog():#类名首字母大写 " ...
- Python编程从入门到实践笔记——用户输入和while循环
Python编程从入门到实践笔记——用户输入和while循环 #coding=utf-8 #函数input()让程序暂停运行,等待用户输入一些文本.得到用户的输入以后将其存储在一个变量中,方便后续使用 ...
随机推荐
- Docker中Nginx,部署Tomcat,部署es + kibana,Docker的可视化,Commit镜像
docker安装使用Nginx # 搜索信息 docker search nginx # 下载镜像 docker pull nginx # 运行测试 # --name给容器命名 # -p 宿主机端口: ...
- 在Vmware虚拟机(win10)中安装逍遥安卓模拟器遇到的问题及解决办法
0x00 下载正确的安装包 逍遥模拟器官网:逍遥安卓模拟器下载官网 (xyaz.cn) 为什么要强调下载正确的安装包? 因为我在第一次下载的时候就下错了,下的是 逍遥模拟器 - 电脑玩手游神器 (me ...
- 利用 Onekey Theater 改善屏幕显示效果
介绍 Onekey Theater(一键影音),它是联想笔记本带的一键影音功能,使用它能够更改笔记本的显示效果和音效,以此模仿电影院的效果,为用户带来更好是视听效果及享受. 作用 之前的联想笔记本自带 ...
- Vue基础篇之 插槽 slot
- Linux详解(基础、环境配置、项目部署入门)
Linux(CentOS 7)操作系统 消息队列(Kafka.RabbitMQ.RocketMQ),缓存(Redis),搜索引擎(ES),集群分布式(需要购买多台服务器,如果没有服务器我们就只能使用虚 ...
- 从零搭建react+ts组件库(封装antd)
为什么会有这样一篇文章?因为网上的教程/示例只说了怎么做,没有系统详细的介绍引入这些依赖.为什么要这样配置,甚至有些文章还是错的!迫于技术洁癖,我希望更多的开发小伙伴能够真正的理解一个项目搭建各个方面 ...
- Pycharm连接远程服务器并保持文件夹同步
pycharm版本2021 服务器版本 Ubuntu 18 1.连接远程服务器 xxx这部省略了,因为之前就已经连接上了hh,后面用到再补充. 2.保持文件夹同步 1.首先在本地(windows环境创 ...
- C#与SQL Server连接时,如何编写连接字符串?
一.Windows身份验证时: String conStr = "Data Source=数据库服务器地址;Initial Catalog=数据库名称;Integrated Security ...
- DYOJ 【20220317模拟赛】瞬间移动 题解
瞬间移动 题意 三维空间中从 \((0,0,0)\) 开始,每次移动 1,问刚好走 \(N\) 次能到 \((X,Y,Z)\) 的方案数 \(N\le10^7\),答案模 \(998244353\) ...
- C语言 - 基础数据结构和算法 - 单向链表
听黑马程序员教程<基础数据结构和算法 (C版本)>,照着老师所讲抄的, 视频地址https://www.bilibili.com/video/BV1vE411f7Jh?p=1 喜欢的朋友可 ...