作业讲解、django请求生命周期流程图、路由匹配、反向解析、无名有名反向解析、路由分发、名称空间
作业讲解、django请求生命周期流程图、路由匹配、反向解析、无名有名反向解析、路由分发、名称空间
一、作业讲解(用户管理系统[单表])
主页面
根据后端利用render传入的html页面并支持模板语法,传入将查询的所有数据传给前端页面,利用模板语法的for循环将数据渲染到整个table表中。
数据添加的逻辑
首先搭建个页面在页面上写上一个a标签,a标签的href指向后端提前开设好的添加数据的接口,然后用户一点击接口就可以跳转到添加用户的界面,页面是获取用户和年龄(根据name属性获取用户名或年龄),一点按钮发送一个post请求,后端根据判断请求方式是否为POST请求,若请求为POST请求根据request.POST获取到用户数据,判断用户名是否已存在,则判断该用户名是否已存在在数据库,若存在返回字符串类型的数据利用HttpResponse。若用户名不存在则利用orm写入数据到数据库中。重定向到用户展示数据可以选择操作的页面。如果请求方式不是POST请求则返回添加用户的界面。
数据编辑的逻辑
在主页面的添加a标签的编辑按钮,a标签的href指向后端提前开设好的编辑数据的接口,然后用户一点击接口就可以跳转到编辑用户数据的页面。由于每条用户数据后面都有一个编辑的按钮,为了确保编辑的是当前的用户的数据,在a标签内指向的数据接口后面写上?id=xxx,根据request.GET获取URL后面携带的非敏感数据(在网址里面加了 ?xxx=yyy&zzz=ooo&aaa=bbb ,无论在什么地方,通过request.GET都可以拿取这些数据)。非敏感数据主要是id,利用主键字段id可以查询到表内的数据信息。
后端根据页面的GET请求获取id值,判断用户的请求方式是什么,若当用户点击编辑页面的编辑数据按钮得到POST请求,获取用户编辑后的信息,然后利用orm将数据更新到数据库中。利用id获取用户想要编辑的数据,返回编辑对象到编辑页面,这样页面在编辑时,输入框内会有默认值。
数据删除的逻辑
在主页面的添加a标签的编辑按钮,a标签的href指向后端提前开设好的删除数据的接口,然后用户一点击接口就可以直接删除数据。同样每个用户后面都有删除数据的按钮,根据a标签内指向数据接口后面的?id=xxx,过request.GET都可以拿取这些数据。后端根据request.GET获取到id值,根据id值直接删除用户的信息记录。然后重定向与主页面。
代码展示:
userlist.html # 主页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h1 class="text-center">数据展示</h1>
<a href="/useradd/" class="btn btn-success btn-xs">添加用户</a>
<table class="table-bordered table-hover table table-striped">
<thread>
<tr class="text-center">
<th class="text-center">编号</th>
<th class="text-center">姓名</th>
<th class="text-center">年龄</th>
<th class="text-center">操作</th>
</tr>
</thread>
<tbody>
{% for user_obj in data_queryset %}
<tr class="text-center">
<td>{{ user_obj.id }}</td>
<td>{{ user_obj.name }}</td>
<td>{{ user_obj.age }}</td>
<td>
<a href="/useredit/?id={{ user_obj.id }}" class="btn btn-primary btn-xs">编辑</a>
<a href="/userdel/?id={{ user_obj.id }}" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
useradd.html # 用户添加页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h1 class="text-center">数据添加</h1>
<a href="" class="btn btn-success btn-xs">添加用户</a>
<form action="" method="post">
<p>用户名:<input type="text" name="username" class="form-control"></p>
<p>年龄:<input type="text" name="age" class="form-control"></p>
<input type="submit" class="btn btn-warning btn-block" value="添加数据">
</form>
</div>
</div>
</div>
</body>
</html>
useredit.html # 用户编辑页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h1 class="text-center">数据添加</h1>
<a href="" class="btn btn-success btn-xs">添加用户</a>
<form action="" method="post">
<p>用户名:<input type="text" name="username" class="form-control"></p>
<p>年龄:<input type="text" name="age" class="form-control"></p>
<input type="submit" class="btn btn-warning btn-block" value="添加数据">
</form>
</div>
</div>
</div>
</body>
</html>
view.py
from django.shortcuts import render, HttpResponse, redirect
from app01 import models
# Create your views here.
def userlist(request):
# 查询所有的用户,传递给html页面展示
data_queryset = models.Userinfo.objects.filter() # filter括号内不写参数,相当于没有筛选条件,等价于查询所有
return render(request, 'userlist.html', {'data_queryset': data_queryset})
def useradd(request):
# 判断请求方式
if request.method == 'POST':
name = request.POST.get('username')
age = request.POST.get('age')
# 判断用户名是否已存在
user_obj = models.Userinfo.objects.filter(name=name)
if user_obj:
return HttpResponse('用户名已存在')
# 写入数据库
models.Userinfo.objects.create(name=name,age=age)
return redirect('/userlist/')
# 先返回一个添加用户的页面
return render(request, 'useradd.html')
def useredit(request):
# 获取用户编辑的id值
edit_id = request.GET.get('id')
if request.method == 'POST':
name = request.POST.get('username')
age = request.POST.get('age')
# 更新数据
models.Userinfo.objects.filter(id=edit_id).update(name=name,age=age)
return redirect('/userlist/')
# 获取用户想要编辑的数据
edit_obj = models.Userinfo.objects.filter(id=edit_id).first() # [数据对象 数据对象]
# 返回编辑对象到编辑页面
return render(request, 'useredit.html',{'edit_obj':edit_obj})
def userdel(request):
# 获取用户删除的id值
del_id = request.GET.get('id')
models.Userinfo.objects.filter(id=del_id).delete()
return redirect('/userlist/')
models.py # 创建表
from django.db import models
# Create your models here.
class Userinfo(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
urls.py # 路由
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('userlist/', views.userlist),
# 用户数据添加
path('useradd/', views.useradd),
# 用户数据编辑
path('useredit/', views.useredit),
# 用户数据删除
path('userdel/', views.userdel),
]
二、Django请求生命周期流程图
解释图:
浏览器>> 发送请求(Http请求) >> web服务网关接口(django默认的wsgiref模块不能承受高并发,最大只有1000左右) >> 中间件 >> 缓存数据库(返回给中间件已经缓存过的数据) >> urls.py(路由层) >> views.py(视图层) >> templates(模板层:存放html静态文件) models.py(模型层:存放假架接数据库)>> 数据库(返回给模型层需要的数据)
首先,用户在浏览器中输入url,发送一个GET方法的request请求。
Django中封装了socket的WSGi服务器,监听端口接受这个request 请求,
再进行初步封装,然后传送到中间键中,这个request请求再依次经过中间键,
对请求进行校验或处理,再传输到路由系统中进行路由分发,匹配相对应的视图函数,
再将request请求传输到views中的这个视图函数中,进行业务逻辑的处理,
调用modles中表对象,通过orm拿到数据库(mysql)的数据。
同时拿到templates中相应的模板进行渲染,然后将这个封装了模板response响应传输到中间键中,
依次进行处理,最后通过WSGi再进行封装处理,响应给浏览器展示给用户。
三、路由匹配
1、路由结尾的斜杆
path('网址后缀',函数名)
'''一旦网址后缀匹配上就会自动执行后面的函数,并结束整个路由的整个匹配'''
默认情况下不写斜杆,Django会让浏览器做二次处理,第一次匹配不上会让浏览器加斜杆再次请求。(若还有斜杆没有三次处理)
Django配置文件settings.py中可以配置是否自动添加斜杆
# 控制结尾是否自动加斜杆
APPEND_SLASH = False
2、path转换器
当网址后缀不固定的时候 可以使用转换器来匹配
转换器五种类型:
'int': IntConverter(),
'path': PathConverter(),
'slug': SlugConverter(),
'str': StringConverter(),
'uuid': UUIDConverter(),
# 想匹配一些额外的东西,但是有不知道是什么,需要用到转换器
# 报错,没有得到一个关键字参数,使用path提供的特殊语法时,
# 把<int>匹配到的内容当做参数传递后后面的func函数
# path('func/<int>/', views.func),
# 单写<year>,在func函数需要传一个参数,但是还是会报错
# path('func/<year>/', views.func),
# 将year当做关键字参数传给func函数:def func(request,year):pass
path('func/<int:year>/<str:info>/', views.func)
转换器匹配到的内容会当做视图函数的关键字参数传入,转换器有几个名字,那么视图函数的形参必须对应
def func(request,year,info):
pass
3、re_path正则匹配
re_path(正则表达式,函数名)
一旦网址后缀的正则能够匹配到内容就会自动执行后面的函数,并结束整个路由的匹配。
# 只要有后缀,会利用正则去匹配这个后缀,只要正则能够从用户所输入的后缀里面匹配到内容,就算匹配成功。
四、反向解析
背景:页面上提前写死了很多路由,一旦路由发送变化会导致所有页面相关链接失效,为了防止出现该问题,我们需要使用反向解析
'''反向解析:返回一个结果,该结果可以访问到对应的路由'''
1)路由对应关系起别名
path('register/',views.reg,name='reg_view'),
2)使用反向解析
html页面
{% url 'reg_view' %}
后端
from django.shortcuts import reverse
reverse('reg_view')
# 直接访问这个名字对应的路由,但是看不到结果
# redirect('reg_view')
PS:反向解析的操作方法都一样,对于path()、re_path()、url()
五、无名有名反向解析
'''当路由中有不确定的匹配因素,反向解析的时候需要人为给出一个具体的值'''
1)给对应关系起别名
path('reg/<str:info>/', views.reg, name='reg_view')
2)使用反向解析获取结果
reverse('reg_view', args=('jason',))
前端:{% url 'reg_view' 'jason' %}
PS:反向解析的操作方法都一样,对于path()、re_path()、url()
六、路由分发
背景:若Django项目特别庞大,里面有很多应用,每个应用下有很多对应关系,那么Django自带的路由层里面的代码就会非常多,那么根据应用的不同拆分到不同的应用中。
Django支持每个应用都可以有自己独立的路由层、模板层、静态文件、视图层(默认)、模型层(默认)。
1)创建多个应用,一定要在配置文件中注册
# Application definition
INSTALLED_APPS = [
'app01.apps.App01Config',
'app02',
]
2)在多个应用中编写相同的路由
app01:
from django.urls import path,re_path
from app01 import views
urlpatterns = [
path('test1/', views.test1),
]
app02:
from django.urls import path,re_path
from app02 import views
urlpatterns = [
path('test1/', views.test1),
]
3)路由分发(总路由)
'''总路由只负责分发,部分则视图函数对应'''
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
path('app01/', include('app01.urls')),
# 只要有请求会先看有没有应用的前缀是否为app01,那么所有的后缀都交给app01下的路由操作
path('app02/', include('app02.urls')),
]
>>>简写
from app01 import urls as app01_urls
from app02 import urls as app01_urls
urlpatterns = [
path('app01/', include(app01_urls)),
path('app01/', include(app02_urls)),
]
七、名称空间
1.不同的应用使用相同的别名,那么反向解析默认情况下是不会字典识别应用前缀
'''由路由分发场景下多个应用在涉及到反向解析别名冲突的时候无法正常解析'''
2.解决
方式一:名称空间,给总路由添加名称空间
总路由:
path('app01/', include(('app01.urls', 'app01'),namespace='app01')),
path('app02/', include(('app02.urls', 'app02'),namespace='app02'))
from django.urls import path,re_path
from app01 import views
urlpatterns = [
path('test1/', views.test1, name='test1_view'),
]
from django.urls import path,re_path
from app02 import views
urlpatterns = [
path('test1/', views.test1, name='test1_view'),
]
方式二:只需要确保反向解析的别名在整个项目中不重复即可
可以在别名前面加上应用名的前缀
总路由:
path('app01/', include('app01.urls')),
path('app02/', include('app02.urls')),
from django.urls import path,re_path
from app01 import views
urlpatterns = [
path('test1/', views.test1, name='app01_test1_view'),
]
from django.urls import path,re_path
from app02 import views
urlpatterns = [
path('test1/', views.test1, name='app02_test1_view'),
]

浙公网安备 33010602011771号