Django零基础(三)
文章目录
- 前言
- 一、普通用户输入
- 1.添加新主题
- 1.1 表单模型
- 1.2 新网页URl设定
- 1.3 修改views.py
- 1.4 新建网页
- 1.5 topics链接
- 2.添加新条目
- 2.1 条目模型
- 2.2 URL设定
- 2.3 修改views.py
- 2.4 新建网页
- 2.5 topic链接
- 3.编辑条目
- 3.1 编辑条目URL
- 3.2 修改views.py
- 3.3 新建网页
- 3.4 topic链接
- 二、创建用户账户
- 1.新建应用程序users
- 1.1 创建app
- 1.2 修改urls.py
- 1.3 创建登录页面
- 1.4 创建页面模板
- 1.5 base中添加链接
- 2.注销
- 2.1 注销URL
- 2.2 修改views.py
- 2.3 链接视图
- 3.注册
- 3.1 定义URL
- 3.2 修改views.py
- 3.3 添加模板
- 3.4 链接视图
- 三、让用户有自己的数据
- 1.限制对页面的访问
- 1.1 限制对topics页面的访问
- 1.2 修改settings.py
- 1.3 全面限制访问
- 2.将数据关联到用户
- 2.1 修改模型Topic
- 2.2 确定用户
- 2.3 迁移数据库
- 3.只允许用户访问自己的主题
- 3.1 修改views.py
- 3.2 保护用户主题
- 3.3 保护页面edit_entry
- 3.4 保护页面new_entry
- 3.5 将新主题关联到当前用户
- 总结错误
前言
Windows下利用Django框架进行Web应用开发
Python三剑客第一本踩坑,学习笔记接上一节
如果寻找错误解决办法可以直接跳到总结查看错误类型再查看相应小节即可
一、普通用户输入
当下我们的网页只能让管理员添加数据,接下来我们来让普通用户能够输入数据
1.添加新主题
首先让用户能够添加新主题,创建一个基于表单的页面
1.1 表单模型
在learning_logs文件夹下创建forms.py文件,输入:
from django import forms
from .models import Topic
class TopicForm(forms.ModelForm):class Meta:model = Topicfields = ['text']labels = {'text': ''}
我们首先导入模块forms以及要使用的模型Topic,再定义一个TopicForm的类继承forms.ModelForm,最简单的ModelForm版本只包含一个内嵌的Meta类,它告诉Django根据哪个模型创建表单,以及表单中包含哪些字段。最后一行让Django不要为字段text添加标签。
1.2 新网页URl设定
在urls.py中添加新主题的网页
# 用于添加新主题的网页path('topics/new_topic/', views.new_topic, name = 'new_topic'),
1.3 修改views.py
函数new_topic()需要处理两种情形:刚进入new_topic网页(在这种情况下应显示一个空表单);对提交的表单数据进行处理并将用户重定向到网页topics。添加代码:
from django.http import HttpResponseRedirect
from django.urls import reverse
from .forms import TopicForm
--snip--
def new_topic(request):"""添加新主题"""if request.method != 'POST':# 未提交数据:创建一个新表单form = TopicForm()else:# POST提交的数据对数据进行处理form = TopicForm(request.POST)if form.is_valid():form.save()return HttpResponseRedirect(reverse('learning_logs:topics'))context = {'form': form}return render(request, 'learning_logs/new_topic.html', context)
django2.0后版本把原来的 django.core.urlresolvers 包 更改为了 django.urls包
1.4 新建网页
在templates/learning_logs下创建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 %}
action告诉服务器将提交的表单数据发送到哪里,method让浏览器以POST请求的方式提交数据。
Django使用{% csrf_token %}来防止攻击者利用表单来获得对服务器未经授权的访问,然后我们显示表单并创建提交按钮。
1.5 topics链接
我们在打开页面topics,添加一个到new_topic的链接,添加代码:
<a href="{% url 'learning_logs:new_topic' %}">Add a new topic:</a>
全部保存,进入网页出现bug:ValueError: Field ‘id’ expected a number but got ‘new_topic’.
找到问题出在urls.py中,网页的网址不应该是topics/new_topic/而应该直接new_topic/,修改后访问成功
2.添加新条目
步骤与添加主题类似
2.1 条目模型
打开forms.py,添加如下代码:
from .models import Entry
class EntryForm(forms.ModelForm):class Meta:model = Entryfields = ['text']labels = {'text': ''}widgets = {'text': forms.Textarea(attrs={'cols': 80})}
小部件(widget)是一个HTML表单元素,我们订制了text的输入文本区域的宽度为80列(默认40列)使用户有足够的输入空间。
2.2 URL设定
打开urls.py,添加条目链接:
# 用于添加新条目的页面path('new_entry/<topic_id>/', views.new_entry, name = 'new_entry'),
2.3 修改views.py
添加代码:
from .forms import EntryForm
--snip--
def new_entry(request, topic_id):"""在特定主题添加新条目"""topic = Topic.objects.get(id = topic_id)if request.method != 'POST':# 未提交数据:创建一个新表单form = EntryForm()else:# POST提交的数据对数据进行处理form = EntryForm(data=request.POST)if form.is_valid():new_entry = form.save(commit = False)new_entry.topic = topicnew_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)
2.4 新建网页
在templates/learning_logs下创建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 %}
我们在顶端显示主题让用户知道是在哪个主题下添加条目;该主题名也是链接可以返回到主题页面。
2.5 topic链接
我们在打开页面topic,添加一个到new_entry的链接,添加代码:
<p><a href="{% url 'learning_logs:new_entry' topic_id %}">add new entry</a>
</p>
全部保存,进入网页可以看到bug:Reverse for ‘new_entry’ with arguments ‘(’’,)’ not found. 1 pattern(s) tried: [‘new_entry/(?P<topic_id>[^/]+)/$’]
经过查询问题出在topic_id中,修改topic.html和new_entry.html中topic_id为topic.id即可(entry中没有topic_id参数)
再次进入网页可以看到添加条目功能
3.编辑条目
下面再创建一个页面让用户能够编辑现有条目
3.1 编辑条目URL
特定主题下编辑的条目ID,修改urls.py,添加代码:
# 用于编辑条目的页面path('edit_entry/<entry_id>/', views.edit_entry, name = 'edit_entry'),
3.2 修改views.py
页面edit_entry收到GET请求时edit_entry()返回一个表单,让用户能够对其进行编辑,该页面收到POST请求后,再将修改后的文本保存到数据库中,添加代码:
from .models import Entry
--snip--
def edit_entry(request, entry_id):"""编辑既有条目"""entry = Entry.objects.get(id = entry_id)topic = entry.topicif request.method != 'POST':# 初次请求,使用当前条目填充表单form = EntryForm(instance = entry)else:# POST提交的数据对数据进行处理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)
instance=entry创建实例过后会使用现有数据填充表单,用户将看到既有数据并能够编辑它们。
3.3 新建网页
在templates/learning_logs下创建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 %}
显示主题,并显示主题下要编辑的条目。
3.4 topic链接
我们在打开页面topic,添加一个到edit_entry的链接,添加代码:
<p><a href="{% url 'learning_logs:edit_entry' entry.id %}">edit entry</a></p>
全部保存,再次进入网页,单击edit entry
save changes后出现bug:name ‘topic_id’ is not defined
重新打开网页可以看到数据已修改成功,问题应该出在页面返回上
和2.5的问题类似,没有传递topic_id参数,把topic_id改为topic.id即可,测试已可重新返回
二、创建用户账户
在这一节我们将建立一个用户注册和身份验证的系统,让用户能够注册账户,进而登录和注销。
1.新建应用程序users
1.1 创建app
我们将创建一个新的应用程序users来完成一系列的操作。创建详细步骤可见第一讲。
(p_env)...\practice python manage.py startapp users
将该程序加入到settings.py我们的应用程序当中
1.2 修改urls.py
修改项目根目录的urls.py,将其包含我们将为应用程序users定义的URL,添加代码:
path('users/', include(('users.urls','users'), namespace = 'users')),
1.3 创建登录页面
在user目录下新建urls.py文件,添加代码:
"""为应用程序users定义URL模式"""from django.urls import path, include
from django.contrib.auth.views import loginfrom . import viewsurlpatterns = [# 登录页面path('login/', login, {'template_name': 'users/login.html'}, name = 'login'),]
1.4 创建页面模板
在如下目录中新建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 'user: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 %}
实参value告诉Django在用户成功登录后将其重定向到什么地方——在这里是主页。
1.5 base中添加链接
下面在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 %}
进入页面看是否能够登录,网络服务错误,显示error:ImportError: cannot import name ‘login’ from ‘django.contrib.auth.views’ (C:\Users\DELL\Desktop\four\Python\Web\practice\p_env\lib\site-packages\django\contrib\auth\views.py)
打开urls.py,推测是Django更新换了包名称,百度之后果然如此,名称改为LoginView,代码修改如下
(有兴趣的朋友也可以把.views去掉来试试导入login包,亲测一样有效)
"""为应用程序users定义URL模式"""from django.urls import path, include
from django.contrib.auth.views import LoginViewfrom . import viewsurlpatterns = [# 登录页面path('login/', LoginView.as_view(template_name= 'users/login.html'), name = 'login'),]
再次进入页面,还是有bug:‘utf-8’ codec can’t decode byte 0xa3 in position 172: invalid start byte
定位后问题出再index.html中,重定位到base.html中,发现是Hello后面的逗号为中文逗号,将其改为英文逗号后可以正常访问(原代码已修改,因此直接复制不会出现此错误)
2.注销
我们不创建用户注销的页面,而让用户只需单击一个链接就能注销并返回主页。
2.1 注销URL
定义注销URL的模式,修改users/urls.py,添加代码如下:
# 注销页面path('logout/', views.logout_view, name = 'logout'),
2.2 修改views.py
添加视图函数logout_view(),修改views.py,添加代码如下:
from django.http import HttpResponseRedirect
from django.urls import reversefrom django.contrib.auth import logoutdef logout_view(request):"""注销用户"""logout(request)return HttpResponseRedirect(reverse('learning_logs:index'))
2.3 链接视图
在base.html中添加注销链接,不登录则隐藏,添加代码:
<a href="{% url 'users:logout' %}">log out</a>
全部保存,进入页面查看,出现log out链接,单击
成功注销
登录,输入之前我们注册的管理员账号(见上一节)
出现bug,没有返回主页,好烦,找了半天错,发现在login.html中%}分隔开了,导致无法定向,把空格删掉就好了(原码已修改,可直接复制)
再次进入,输入错误密码,再输入正确密码,登录成功
3.注册
我们使用Django提供的表单UserCreationForm来让用户注册
3.1 定义URL
定义注册URL的模式,修改users/urls.py,添加代码如下:
# 注册页面path('register/', views.register, name = 'register'),
3.2 修改views.py
添加视图函数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 UserCreationFormdef 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)
3.3 添加模板
添加注册模板register.html,保存到login.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 %}
3.4 链接视图
在base.html中添加注册链接,登录则隐藏,添加代码:
<a href="{% url 'users:register' %}">register</a> -
三、让用户有自己的数据
用户应该能够输入其专有的数据,因此我们将创建一个系统确定各项数据所属的用户,再限制对页面的访问让用户只能使用自己的数据。在本节中我们将修改模型Topic使每个主题都归于特定的用户。
1.限制对页面的访问
我们使用@login_required来限制用户的行为,对于某些页面只允许登录用户访问
1.1 限制对topics页面的访问
每个主题都归特定用户所有,因此应只允许登录的用户请求topics页面,打开learning_logs/views.py,添加如下代码:
from django.contrib.auth.decorators import login_required
--snip--
@login_required
只有用户已登录,Django才会运行topics()的代码,如果未登录就重定向到登录页面。
1.2 修改settings.py
在settings.py文件末尾添加:
# 我的设置
LOGIN_URL = '/users/login/'
进入页面,可以看到点击Topics弹出登录界面:
1.3 全面限制访问
在项目“学习笔记”中,我们不限制对主页,注册页面和注销页面的访问,限制对其他所有页面的访问,打开learning_logs/views.py,对除index()外的每个视图前都增加@login_required(感觉并无必要,因为一般不至于直接链接进主题和条目页面,但书上加了就加吧)
2.将数据关联到用户
现在我们将数据关联到提交它们的用户,我们只需将最高层的数据关联到用户,底层的数据将自动关联。例如在该项目中,应用程序的最高层数据是主题,而所有条目都与主题相关联,
2.1 修改模型Topic
打开models.py文件,建立Topic与用户的外键关系,添加如下代码并保存:
from django.contrib.auth.models import User
owner = models.ForeignKey(User)
2.2 确定用户
迁移数据库时,Django将对数据库进行更改,为执行迁移,Django需要知道该将各个既有主题关联到哪个用户。
启动shell会话来查看所有用户ID
(p_env) ···\practice>python manage.py shell
报错TypeError: init() missing 1 required positional argument: ‘on_delete’,Django2.0以上版本在定义外键时一定要加上on_delete=models.CASCADE否则会报错,修改models.py代码如下:
再次启动shell会话,可以看到我们只有一个管理员用户
2.3 迁移数据库
知道用户ID后我们就可以迁移数据库了
(p_env) ···\practice>python manage.py makemigrations learning_logs
(p_env) ···\practice>python manage.py migrate
数据迁移成功,生成迁移文件0003_topic_owner.py
再次启动shell会话,查看topic外键,可以看到主题都关联到了管理员用户
你也可以使用命令python manage.py flush,这将重置整个数据库,如果这样做就得重新设置用户并重新输入数据
3.只允许用户访问自己的主题
当前不管以哪个用户身份登录都能够看到所有的主题,这节我们实现只向用户显示自己的主题
3.1 修改views.py
打开views.py,修改函数topics()如下
3.2 保护用户主题
我们现在登录之后能够输入链接直接访问到任一主题,为修复该问题,在视图函数topic()获取请求条目前进行检查,添加代码:
from django.http import Http404
# 确认请求的主题属于当前用户if topic.owner != request.user:raise Http404
用户访问不属于自己的主题时将调起404页面,在后续我们会显示更合适的错误页面。
3.3 保护页面edit_entry
禁止用户通过直接输入URL来访问其他用户的条目,修改edit_entry,添加代码:
if topic.owner != request.user:raise Http404
3.4 保护页面new_entry
禁止用户通过直接输入URL来访问其他用户的条目,修改new_entry,添加代码:
if topic.owner != request.user:raise Http404
3.5 将新主题关联到当前用户
修改new_topic.py,如下:
if form.is_valid():new_topic = form.save(commit = False)new_topic.owner = request.usernew_topic.save()
总结错误
输入代码需要非常小心,很多时候是一些很小的错误造成的bug,需要仔细敲
总结一下错误:
一、
1.5 ValueError: Field ‘id’ expected a number but got ‘new_topic’.
2.5 Reverse for ‘new_entry’ with arguments ‘(’’,)’ not found. 1 pattern(s) tried: [‘new_entry/(?P<topic_id>[^/]+)/$’]
3.4 name ‘topic_id’ is not defined
二、
1.5 ‘utf-8’ codec can’t decode byte 0xa3 in position 172: invalid start byte
ImportError: cannot import name ‘login’ from ‘django.contrib.auth.views’
2.3 Page not found
三、
2.2 TypeError: init() missing 1 required positional argument: ‘on_delete’
注册这一块看书时候看漏了,补回去之后登陆页面发现bug
发现views.py处少给user打了个s。。。原代码已修改,试着注册结果又报错:NameError: name ‘authenticated_user’ is not defined,发现还是views处问题,authenticated_user多打了一个e,原码已修改。
修改后可以正常注册了,本节结束。
Django零基础(三)相关推荐
- Django零基础部署机器学习模型(将模型预测打包成API接口/网页URL)
Django零基础部署机器学习模型 Django REST framework项目创建 框架结构了解 部署代码 Django REST framework项目创建 建立一个项目文件夹,使用VScode ...
- agent python实现 建模_零基础三天学会Python爬虫(第二天)
一.Python与urllib2 现在已经迫不及待的想尝试一下url和网络爬虫的配合关系了.我们上一届已经介绍过了,爬虫就是把URL地址中的网络资源读取出来,然后处理(保存到本地,或者打印等等).本篇 ...
- python学会爬虫要多久_零基础三天学会Python爬虫(第二天)
一.Python与urllib2 现在已经迫不及待的想尝试一下url和网络爬虫的配合关系了.我们上一届已经介绍过了,爬虫就是把URL地址中的网络资源读取出来,然后处理(保存到本地,或者打印等等).本篇 ...
- django jsonresponse_0基础掌握Django框架(29)HttpResponse对象
为了更好的学习效果,请搭配视频教程一起学习: Django零基础到项目实战 - 网易云课堂study.163.com HttpResponse对象 Django服务器接收到客户端发送过来的请求后,会 ...
- 浅谈三个星期零基础入门学习Thinkphp5开发restful-api接口的心得和总结
一丢丢心得体会: 首先不得不说一下,学习一门知识,真的就像建一栋高楼一样,地基必须的稳固,否则你辛辛苦苦建的楼可能随时会垮掉,这一点在我学习thinkphp5的路上深有体会,同时了自此我也爱上了写博客 ...
- 零基础学Java需要先具备的三项技能
随着互联网的发展,许多公司都在招Java工程师,很多零基础的Java小白看中了Java的高薪资,也都想转行做Java,但是零基础学Java需要先具备哪些技能呢?本文小千整理了零基础学Java需要先具备 ...
- [python opencv 计算机视觉零基础到实战] 三、numpy与图像编辑
一.学习目标 了解图片的通道与数组结构 了解使用numpy创建一个图片 了解使用numpy对图片的一般操作方法 目录 [python opencv 计算机视觉零基础到实战] 一.opencv的hell ...
- 零基础 Amazon Web Services (AWS) 入门教程图文版(三)
原则上WDCP安装好了,就可以直接使用了,FTP.MySQL什么的应有尽有.但是本站Amazon AWS -- 免费的午餐不好吃一文中说到过这个问题,直接用WDCP的MySQL会导致EBS的I/O急剧 ...
- 【零基础学Java】—List集合(三十九)
[零基础学Java]-List集合(三十九) java.util.list接口 extends Collection接口 list接口的特点: 1.有序的集合,存储元素和取出元素的顺序是一致的(存储1 ...
最新文章
- GitHub的十大JavaScript项目
- 24点游戏c语言去除重复,C语言解24点游戏程序
- Linux fedora35安装crontab定时任务工具
- Java finalize()的使用
- vsftpd.conf配置范例
- HDU - 4866 Shooting(主席树+扫描线)
- C# 模拟Windows键盘事件
- 这本Python书被封年度神作!程序员:比女友强太多!
- MXY-单点登陆系统
- 罗马数字转化为阿拉伯数字
- 移动硬盘安装双系统windows10和ubuntu18.04
- 正则表达式(18)——字符串替换案例:UBB翻译
- python中加注释_python中如何添加注释
- 计算机应用基础知识盘,计算机应用基础知识介绍.ppt
- 两个服务器组虚拟机,linux 两台虚拟机
- 小米4可以刷入linux,给小米4刷上SailfishOS是怎样的体验?附刷机教程
- 为iPart表添加“重量”列
- 欧洲中世纪末期的战争
- 企业全方位改进(CMMI)咨询服务
- matlab打开.mat文件
热门文章
- ﹟cook100调料食谱﹟香煎牛排
- 打卡小程序源码,签到小程序,微擎后端,内附安装教程
- ssm基于javaweb的医疗健康知识管理系统设计与实现毕业设计源码131903
- c++ 将程序锁定到任务栏
- VS2015+QT环境配置后,Lauch Qt Designer打开失败,无法打开*.ui文件
- 在线生成简单二维码图片
- webpack编译提速:使用externals和DllPlugin
- gcc数据对齐之: howto 2.
- 怎么才能让Antd中的嵌套子表格渲染不一样的内容
- 学校机房/实验室电脑免全屏广播控制解决方案