5.Django(简单登录认证、url路由系统、无名分组、有名分组)
-
串一下流程
浏览器通过输入url发送了给服务器一个请求,这个请求先发送到url路由分发,找到对应的views函数,并且Django将请求信息封装成了一个对象传递给此函数的第一个参数request,执行views视图函数的对应的函数
最终返回一个页面。
render:类似于jinja2,模板渲染系统。(作用:接受html页面数据,将想呈现在前端的数据库数据替换掉,形成最终的html并给return 由return返回给浏览器)
-
构建一个登陆的index.html页面
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="">
用户名:<input type="text" name="username">
密 码 :<input type="password" name="password">
<input type="submit">
</form>
</body>
</html>
// action 不写默认提交到当前路径
// form表单默认是get提交思考:当点击提交,用户名、密码数据会通过url传递到对应的login函数中,我们应该将数据提取出来。如何提取?
request将所有的请求信息都接受到了,他肯定有个方法request.GET,但是现在有个问题,我访问这个url(刷新页面)是get请求,返回给我们一个form表单的页面,然后再次通过get请求提交数据
from django.shortcuts import render
from django.shortcuts import HttpResponse
# Create your views here.
def login(request):
"""
:param request: 可以设置任意形参,但是第一个参数约定俗称为request与类里面的self同理
request 接收所有请求相关的信息
:return:
"""
if request.GET:
username = request.GET['username']
password = request.GET['password']
if username == '牧羊小董' and password == '123':
return HttpResponse('登陆成功')
else:
return HttpResponse('登录失败')
else:
return render(request,'index.html')
虽然我们上面的功能完成了,但是不合理。一般我们提交数据都是通过post提交。这里为什么我们要请求页面用get而提交数据用post?
因为你的路径index对应的login函数处理两次请求,如果两次请求都是get或者post,我们在函数中不易区分。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
用户名:<input type="text" name="username">
密 码 :<input type="password" name="password">
<input type="submit">
</form>
</body>
</html>
from django.shortcuts import render
from django.shortcuts import HttpResponse
# Create your views here.
def login(request):
"""
:param request: 可以设置任意形参,但是第一个参数约定俗称为request与类里面的self同理
request 接收所有请求相关的信息
:return:
"""
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# get方法比直接用['username']有什么好处?
# get取值 不存在返回none 不报错 也可设置不存在返回什么
if username == '牧羊小董' and password == '123':
return HttpResponse('登录成功')
else:
return HttpResponse('登录失败')
else:
return render(request,'index.html')为什么报一个forbidden错误?通过post向后端请求数据时,需要进行中间件的验证(后面会讲到)我们找到settings将这个中间件先注销掉。
request的方法:
request.method 获取请求方式 GET、POST、HEAD、DELETE
request.GET 获取get请求提交的数据,quertdict类型,类似于字典
request.POST 获取post请求提交的数据,quertdict类型,类似于字典
HttpResponse 返回浏览器一个字符串 提示
render 返回浏览器一个html页面。模板系统
url路由系统
url路由系统通过路径映射不同的函数。
-
url格式
from django.conf.urls inport url
urlpatterns = [
url(正则表达式,views视图函数,参数,别名),
url(正则表达式,views视图函数,参数,别名),
url(正则表达式,views视图函数,参数,别名),
url(正则表达式,views视图函数,参数,别名),
url(正则表达式,views视图函数,参数,别名),
]
url底层遵循轮循机制的:
# 循环urlpatterns,找到对应的函数执行,匹配上一个路径就找到对应的函数执行,就不再往下循环了,并给函数穿一个参数request,和wsfiref的environ类似,就是请求信息的所有内容 -
参数说明
-
正则表达式:一个正则表达式字符串 最前面隐藏了
http://ip:端口
-
views视图函数:一个可调用对象,通过为一个视图函数或一个指定视图函数路径的字符串
-
参数:可选的要传递给视图函数的默认参数(字典形式)
-
别名:一个可选的name参数
-
无名分组
浏览器访问后端,肯定会遇到传递数据的情况,我们现在已知的给后端传递数据通过form表单,我们还可以通过url路径给后端传递数据。
我们现在做一个图书管理系统,里面存储着全世界前1000的数据,我们还可以检索,我想查询这些书中有没有2000年出版的,我们就做一个这样的流程
urls代码:
"""first_pro URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from chat import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.login),
url(r'^books/(\d{4})/$',views.books_year),
# 127.0.0.1:8000/books/任意四个数字/
url(r'^books/(\d{4})/(\d{1,2})/$', views.books_year_mouth),
# 127.0.0.1:8000/books/任意1⾄2个数字/
]
views代码:
from django.shortcuts import render
from django.shortcuts import HttpResponse
# Create your views here.
def books(requerst):
return render(requerst,'books.html')
def books_year(request, year):
return HttpResponse(f'出版的年份{year}')
def books_year_mouth(request, year, mouth):
return HttpResponse(f'出版的年份{year},出版⽉份{mouth}')
注意事项:
1、urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。
2、若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。只有加上()才能当做实参传递给对应的views函数。
3、不需要添加一个前导的反斜杠(也就是写在正则前面的那个/),因为每个URL都有。例如,应该是^books⽽不是 ^/books。
4、每个正则表达式前面的r是可选的但是建议加上。
5、^books$以什么结尾,以什么开头,严格限制路径。
6、无名分组给对应的views函数传递的是形式参数(或者默认值参数)
有名分组
-
格式
?P<参数名字>正则匹配
-
代码测试:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.login), url(r'^books/(?P<year>\d{4})/$', views.books_year), url(r'^books/(?P<year>\d{4})/(P<mouth>\d{1,2})$',views.books_year_mouth), ] def books_year(request, year): return HttpResponse(f'出版的年份{year}') def books_year_mouth(request, year, mouth=12): return HttpResponse(f'出版的年份{year},出版⽉份{mouth}')
-
小结:
无名分组相当于 函数的位置传参,从左至右一一对应,例如: url(r'^books/(\d{4})/(\d{1,2})/$', views.books_year_mouth) def books_year_mouth(request,y,n): pass books_year_mouth(⾃动传参请求对象,1989,12) 有名扥组相当于 函数的关键字传参,不用按照顺序但是必须一一对应: url(r'^books/(?P<year>\d{4})/(?P<mouth>\d{1,2})$', views.books_year_mouth) def books_year_mouth(request,mouth,year): pass books_year_mouth(⾃动传参请求对象,year=1989,mouth=12)
url反向解析
-
给路径起别名
url(r'^delete_book/(\d+)/', views.delete_book, name='delete_book')
-
为什么要起别名
比如,后端跳转一个页面:
redirect('/index/')
前端,要拼接一个路径:
<a href="http://127.0.0.1:8000/delete_book/{{ book.id }}" class="btn btn-danger">删除</a>
我们这个路径都写死了,如果有一天,由于项目需求,要让你更改一下某个业务的url。
-
-
这就意味着,无论前端还是后端,只要你用到了之前的路径的所有的代码全部都改。这样的话是没有拓展性的,太low了。所以我们要给url起个别名,以后无论url怎么改动,我们就可以一直使用者别名。
-
起那段如果使用别名拼接路径?
<a href="{% url 'delete_book' book.id %}" class="btn btn-danger">删除</a>
-
后端如何使用起别名?
如果是通过redirect跳转: redirect('别名')
redirect('index') # 之所以redirect可以通过别名访问到对应的真实路径,源码内部利用了reverse反向解析,解析除了真正的路径
redirect(reverse('index')) 多此一举,因为redirect源码内部使用了reverse反向解析。
-
-
url的反向解析
引用一个reverse模块
from django.urls import reverse
print(reverse('index')) # /check_book/
通过reverse模块将别名对应的真实路径反向解析出来。 -