Django进阶

请求处理流程

客户端(浏览器发送请求)—》路由Route(根据请求进行转发给视图)—》View(接受请求、处理请求)—》Model(数据库交互、查询修改等)

—》View(根据数据库返回的数据进行反馈,给Template)—-》Template渲染相应的内容—-》通过View将显示内容返回给客户端

配置ALLOWED_HOSTS

要想本机被其他主机访问,需要配置允许访问的域名

默认127.0.0.1如果要局域网内访问,就要加入局域网IP,公网访问,则公网IPALLOWED_HOSTS = [“192.168.1.4”,]

路由重定向

在路由urls中,无论是path还是re_path都有name参数,用来动态获取path中urlpattern的具体内容

比如在子应用的urls中,使用了name为home,我们可以通过home获取到管理页面视图的urlpatternurlpatterns = [
  re_path(r’^index/$’,index,name=”index”)
]

当我们在其他视图需要获取管理页面视图的url时,直接通过获取,而不需要把url写死from django.urls import reverse
def index(req):
  path = reverse('home')
  print(path)
  return render(req,'index.html')

使用Postwoman发送请求

image-20220408121532240

打印的路径如下

image-20220408121549642

使用reverse就可以直接获取到对应name视图urlpattern,而不需要访问urls中的index/

当urls中的urlpattern改变为home时,只要name不变,仍然是index,就一样可以获取到管理页面视图的路由urlpattern

命名空间

为了防止不同应用中的name重复,通常以子应用名作为include的第二个参数(命名空间)

include的第一个参数是一个元组include((‘子应用名.urls’))

工程urls如下urlpatterns = [
  path(‘admin/’,admin.site.urls),
  path(”,include((‘login.urls’,’login’))),
  path(”,include((‘myapp.urls’,’myapp’))),
]

login.urlsurlpatterns = [
  re_path(r’^index/$’,index,name=”index”)
]

myapp.urlsurlpatterns=[
  path(‘index/’,index,name=’index’)
]

这个时候reverse需要使用namespace:name来获取视图对应的urlpatternpath = reverse(‘login:index’)

HttpRequest对象

HTTP请求数据传递参数的四种方式

  • URL的特定部分,如/weather/bejing/2018,可以在服务器端的路由中用正则表达式截取;
  • 查询字符串(query string):形如key=1&value=2
  • 请求体:表单、json、XML
  • 请求头

正则提取请求URL的参数

  • 进行正则分组的参数会传递给视图
  • 需要在view中定义变量来接收参数

urlpatterns = [
re_path(r'(1)/(xiaozhi)’,detail)
]

比如当前url表示,id为1,用户为xiaozhi的用户

在view中定义如下,用变量来接收参数def detail(request,acc_id,acc_name):

return HttpResponse(“Hello” + acc_name)

image-20220407172233892

路由将会接收到参数,进行转发给View处理

如果需要匹配所有整数,就在urls中添加正则urlpatterns = [
path(‘admin/’, admin.site.urls,name=’home’),
re_path(r'(\d+)/(\s+)$’,detail)
]

关键字参数

利用正则来实现关键字参数urlpatterns = [
path(‘admin/’, admin.site.urls,name=’home’),
re_path(r'(?P<acc_id>\d+)/(?P<acc_name>[a-zA-Z]+)/$’,detail)
]

可以看到路由中定义acc_id在前,acc_name在后

所以请求的时候要注意顺序,这样定义的好处是,即使view中参数顺序与路由不一致,也不影响接收处理请求def detail(request,acc_name,acc_id):

return HttpResponse(acc_name + acc_id)

image-20220407190407231

查询字符串

在url中传递参数,格式如下http;//hostname:port/路由名/?key=value&key=value&…

使用查询字符串传递参数http://127.0.0.1:8000/index/?acc_id=1&acc_name=xiaozhi

View通过GET接收参数def index(req):
query_params = req.GET
print(query_params)

image-20220408134357710

得到的是一个字典,进一步获取内容def index(req):
query_params = req.GET
print(query_params.get(‘acc_id’))
print(query_params.get(‘acc_name’))

image-20220408134630191

但是在传递一个键多个值的时候,只能获取到最后一个http://127.0.0.1:8000/index/?acc_id=1&acc_name=xiaozhi&acc_id=233def index(req):
query_params = req.GET
print(query_params)
print(query_params.get(‘acc_id’))
print(query_params.get(‘acc_name’))
return render(req,’index.html’)

image-20220408135140961

所以要获取一个键,多个值,要使用getlistdef index(req):
query_params = req.GET
print(query_params)
print(query_params.getlist(‘acc_id’))
print(query_params.get(‘acc_name’))
return render(req,’index.html’)

image-20220408135235352

请求体

POST表单

首先在seetings里关闭CSRF跨域MIDDLEWARE = [
‘django.middleware.security.SecurityMiddleware’,
‘django.contrib.sessions.middleware.SessionMiddleware’,
‘django.middleware.common.CommonMiddleware’,
# ‘django.middleware.csrf.CsrfViewMiddleware’,
‘django.contrib.auth.middleware.AuthenticationMiddleware’,
‘django.contrib.messages.middleware.MessageMiddleware’,
‘django.middleware.clickjacking.XFrameOptionsMiddleware’,
]

注释掉就可以接收表单请求了

image-20220408141716220

在视图中获得请求数据def index(req):
data = req.POST
print(data)
return render(req,’index.html’)

image-20220408141855851

JSON

在body中添加数据,POST一下{
“acc_id”:”123″,
“acc_name”:”xiaozhi”
}

image-20220408142438992

由此可见,数据不在POST中,而在body中def index(req):
data = req.body
print(data)
return render(req,’index.html’)

image-20220408142555307

原始数据是byte字节类型,为了便于查看,解码一下def index(req):
data = req.body.decode()
print(data)
return render(req,’index.html’)

image-20220408142737570

字符串格式化为JSON数据def index(req):
data = json.loads(req.body.decode())
print(data)
return render(req,’index.html’)

image-20220408145053765

请求头

获取数据def index(req):
data = req.META

返回字典

请求数据

image-20220408145930045

def index(req):
data = req.META
print(data)
return HttpResponse(req,”index.html”)

image-20220408145905540

HttpResponse对象

包含参数

  • content:传递字符串,不可以是对象、字典等
  • status:状态码
  • content_type
    • 语法规则:大类/小类
      • text/html
      • text/css
      • text/javascript
      • application/json
      • image/png
    • 是一个MIME类型(告诉客户端是什么类型数据)

return HttpResponse(“Hello”,status=200)

JsonResponse

from django.http import JsonResponse
def index(req):
dict = {
“name”:”xiaozhi”
}
return JsonResponse(dict)

image-20220408151504773

页面跳转

from django.shortcuts import render, redirect
def index(req):
dict = {
“name”:”xiaozhi”
}
return redirect(“https://www.baidu.com/”)

跳转至对应路由中def index(req):
dict = {
“name”:”xiaozhi”
}
return redirect(“/admin/”)

状态保持

  • 浏览器请求服务器是无状态的。
  • 无状态:指一次用户请求时↑浏览器、服务器无法知道之前这个用户做过什么,每次请求都是- 次新的 请求。
  • 无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后, 会免闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象。
  • 有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
  • 实现状态保持主要有两种方式:
    • 在客户端存储信息使用Cookie
    • 在服务器端存储信息使用Session

cookie、session运作流程

  1. 浏览器首次访问站点,不携带cookie信息,浏览器会在响应中创建一个Cookie,包含SessionID返回给客户端浏览器,并保存到本地
  2. 第二次访问同样的站点,会自动带上SessionID、cookie信息,服务器检测到后会验证用户的身份,验证成功后调用Session存在内存中的用户信息

实现

设置Cookie

  1. 判断请求中有无Cookie
  2. 获取用户信息

如果没有,则

  1. 创建Cookie
  2. 返回Cookie

def index(req):
# 判断有无Cookie

# 获取用户信息
username = req.GET.get(‘acc_name’)
# 创建Cookie
response = HttpResponse(“set_cookie”)
response.set_cookie(“username”,username)
# 返回Cookie
return responsehttp://127.0.0.1:8000/index/?acc_id=233&acc_name=xiaozhi

image-20220408162919279
image-20220408163050833

获取Cookie

def get_cookie(req):
cookies = req.COOKIES
username = cookies.get(‘username’)
print(username)
return HttpResponse(“get_cookie”)http://127.0.0.1:8000/get_cookie/

image-20220408182235345
  • Cookie被包含在在请求头中
  • 第一次服务器会在响应头中返回Cookie,之后就不再返回Cookie了
  • Cookie是基于IP分配的

过期时间

def set_cookie(req):
# 判断有无Cookie

# 获取用户信息
username = req.GET.get(‘acc_name’)
# 创建Cookie
response = HttpResponse(“set_cookie”)
response.set_cookie(“username”,username,max_age=3600) # 单位是s
# 返回Cookie
return response

使用max_age,从浏览器请求服务器开始计时

删除Cookie

两种方式 response.delete_cookie(‘username’) #参数是key
response.set_cookie(“username”,username,max_age=0)

  • Session需要依赖于Session
  • 如果浏览器禁用cookie,则Session不能实现

实现

session的信息存在数据库表django_session中mysql> show tables;
+—————————-+
| Tables_in_test_django |
+—————————-+ | |
| django_session |
+—————————-+mysql> desc django_session
-> ;
+————–+————-+——+—–+———+——-+
| Field | Type | Null | Key | Default | Extra |
+————–+————-+——+—–+———+——-+
| session_key | varchar(40) | NO | PRI | NULL | |
| session_data | longtext | NO | | NULL | |
| expire_date | datetime(6) | NO | MUL | NULL | |
+————–+————-+——+—–+———+——-+

可以看到里面没有数据mysql> select * from django_session;
Empty set (0.00 sec)

创建session

def set_session(req):
print(req.COOKIES)
# 假设信息验证成功
username=’xiaozhi’
# 设置session
req.session[‘username’]=username
return HttpResponse(“set_session”)http://127.0.0.1:8000/set_session/?username=xiaozhi

可以看到请求后多了sessionid

数据库中多了记录mysql> select * from django_session;
+———————————-+———————————————————————————–+—————————-+
| session_key | session_data | expire_date |
+———————————-+———————————————————————————–+—————————-+
| q3ir5zkuukin23t5e3nn5cb7ijs0lxjm | eyJ1c2VybmFtZSI6InhpYW96aGkifQ:1ncnh6:maEMAkqj-BolYvCEKov1YyjJ26giv1mhZek3_ew0cCY | 2022-04-22 12:26:28.403729 |
+———————————-+———————————————————————————–+—————————-+
1 row in set (0.00 sec)

获取session

def get_session(req):
# 从浏览器的Cookie中获取sessionid
cookies = req.COOKIES
# 获取session对应的信息
username = req.session[‘username’]
return HttpResponse(“get_session”)

清除所有session

删除值部分req.session.clear()

清除session数据

在存储中删除session的整条数据。req.session.flush()

删除session中的指定键及值

在存储中只删除某个键及对应的值。del req.session[‘key’]

设置session的有效期

request.session.set_expiry(value) # 单位为s

  • 如果value是一个整数1 session将在value秒没有活动后过期。
  • 如果value为0 ,那么用户session的Cookie将在用户的浏览器免闭时过期。
  • 如果value为None,那么session有效期将采用系统默认值,
  • 默认为两周,可以通过在settings .py中设置SESSION_ COOKIE_ AGE来设置全局默认值。

Session保存

在settings中,可以设置session数据的存储方式,可以保存在数据库、本地缓存中

数据库保存

Django默认将session数据保存在数据库中,setting中可以写配置,也可以不写

配置SESSION_ENGINESESSION_ENGINE= ‘django.contrib.sessions.backends.db’

如果存储在数据库中,需要在INSTALLED_APPS中安装Session应用(默认配置)’django.contrib.sessions’,

本地缓存

存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。SESSION_ENGINE = ‘django.contrib.sessions.backends.cache’

混合存储

优先从本机内存中存取,如果没有则从数据库中存取。SESSION_ENGINE = ‘django.contrib.sessions.backends.cached.db’

Redis

在Redis中保存session,需要引入第三方拓展,可以使用django-redis解决

django-redis 中文文档 — Django-Redis 4.7.0 文档 (django-redis-chs.readthedocs.io)

安装拓展pip install django-redis

配置settings的cachesCACHES = {
“default”: {
“BACKEND”: “django_redis.cache.RedisCache”,
“LOCATION”: “redis://127.0.0.1:6379/1”,
“OPTIONS”: {
“CLIENT_CLASS”: “django_redis.client.DefaultClient”,
}
}
}

作为session backend使用SESSION_ENGINE = “django.contrib.sessions.backends.cache”
SESSION_CACHE_ALIAS = “default”

Redis获取Session与正常session一样request.session.get(‘name’)

删除sessiondel request.session[‘name’]

删除redis中session的所有value数据(不包括key)request.session.clear()

删除所有数据(包括key)r

类视图

面向对象的登录视图设计

GET请求渲染页面

POST请求验证登录并跳转def login(req):
if req.method==”GET”:
return render(req)
else:
# POST请求验证登录
return redirect(“首页”)

  • 类视图采用面向对象的设计方式
  • 继承自View
  • 包含get、post、put、delete等方法
  • 类试图的方法的第二个参数必须是请求实例对象
  • 类试图的方法必须有返回值返回值是HttpResopnse及其子类

class LoginView(View):
# 重写方法
def get(self,req):
return HttpResponse(“GET”)

def post(self,req):
return HttpResponse(“POST”)

在urls中需要as_view引入urlpatterns = [
re_path(r’^login/$’,LoginView.as_view())
]

类视图原理

首先在urls中调用类的as_view()方法def as_view(cls, **initkwargs):
“””Main entry point for a request-response process.”””
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(
‘The method name %s is not accepted as a keyword argument ‘
‘to %s().’ % (key, cls.__name__)
)
if not hasattr(cls, key):
raise TypeError(“%s() received an invalid keyword %r. as_view “
“only accepts arguments that are already “
“attributes of the class.” % (cls.__name__, key))

def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, ‘request’):
raise AttributeError(
“%s instance has no ‘request’ attribute. Did you override “
“setup() and forget to call super()?” % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs

# __name__ and __qualname__ are intentionally left unchanged as
# view_class should be used to robustly determine the name of the view
# instead.
view.__doc__ = cls.__doc__
view.__module__ = cls.__module__
view.__annotations__ = cls.dispatch.__annotations__
# Copy possible attributes set by decorators, e.g. @csrf_exempt, from
# the dispatch method.
view.__dict__.update(cls.dispatch.__dict__)

return view

会返回一个view,第22行会调用dispatch方法def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn’t exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn’t on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)

dispatch方法会对请求方法进行小写,在列表中进行验证http_method_names = [‘get’, ‘post’, ‘put’, ‘patch’, ‘delete’, ‘head’, ‘options’, ‘trace’]

如果不在列表中就会报http_method_not_allowed错误

最后handler会将请求转发至对应的重构函数

MRO的继承顺序

登陆验证

from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin # 验证是否登录
class LoginView(LoginRequiredMixin,View):
# 重写方法
def get(self,req):
return HttpResponse(“GET”)

def post(self,req):
return HttpResponse(“POST”)

  1. 首先会在LoginView中找dispatch
  2. 没有的话逐一找其父类:LoginRequiredMixin、View
  3. 在LoginRequiredMixin中class LoginRequiredMixin(AccessMixin):
    “””Verify that the current user is authenticated.”””
    def dispatch(self, request, *args, **kwargs):
    if not request.user.is_authenticated:
    return self.handle_no_permission()
    return super().dispatch(request, *args, **kwargs)从源码中可以看出,如果没有登陆,会返回:handle_no_permission()不允许访问,继续在View类中找dispatch,返回视图
  4. 如果反过来,先继承View,会直接返回视图,不会进行登录认证

中间件

Django中的中间件是一个轻量级、底层的插件系统,可以窗人Django的请求和响应处理过程,修改Django的输入或输出。

中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。

我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。

通俗来说,就是将一个请求进行一次或多次处理,而不是像视图那样,处理后就返回。只能做一件事

且所有的请求都会经过中间件处理MIDDLEWARE = [
‘django.middleware.security.SecurityMiddleware’,
‘django.contrib.sessions.middleware.SessionMiddleware’,
‘django.middleware.common.CommonMiddleware’,
# ‘django.middleware.csrf.CsrfViewMiddleware’,
‘django.contrib.auth.middleware.AuthenticationMiddleware’,
‘django.contrib.messages.middleware.MessageMiddleware’,
‘django.middleware.clickjacking.XFrameOptionsMiddleware’,
]

中间件定义

就是在视图函数的外面再定义一个函数,且这个函数必须要有一个参数def simple_middleware(get_response):
# One-time configuration and initialization.

def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
print(“before request”)
response = get_response(request)
print(“after request”)
# Code to be executed for each request/response after
# the view is called.

return response

return middleware

get_response是一个形参,也是一个函数,下面可以调用

中间件注册加载

MIDDLEWARE = [
‘django.middleware.security.SecurityMiddleware’,
‘django.contrib.sessions.middleware.SessionMiddleware’,
‘django.middleware.common.CommonMiddleware’,
# ‘django.middleware.csrf.CsrfViewMiddleware’,
‘django.contrib.auth.middleware.AuthenticationMiddleware’,
‘django.contrib.messages.middleware.MessageMiddleware’,
‘django.middleware.clickjacking.XFrameOptionsMiddleware’,
# 注册中间件
‘login.middleware.simple_middleware’,
]

image-20220413100149117

所有的请求,包括静态资源的请求,都会经过中间件

使用案例

判断每个请求中是否带有Cookie的一些信息def SimpleMiddleware(get_response):
def middleware(request):
username = request.COOKIES.get(“username”)
if username is None:
print(“username is NONE”)
else:
print(“username为:” + username )
response = get_response(request)
print(“after request”)
return response
return middleware

image-20220413101325179

中间件初始化

def simple_middleware(get_response):
# One-time configuration and initialization.

def middleware(request):

return response

return middleware

在中间件函数前面,这个部分就是中间件初始化的代码位置。

  • Debug=True时默认初始化两次,False则一次
  • 中间件的初始化只会在项目重新运行时进行一次,刷新页面不会导致中间件重新初始化

注意点

响应前,请求获取的顺序是按照中间件顺序,从上到下

而响应的顺序是按中间件顺序,从下到上。

先获取请求的后响应,后获取请求的先响应

Django分页

后端

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

一些属性和方法paginator = Paginator(book_list,3) # 先拿到分页器对象,第一个参数:对象列表,第二个参数:每页显示的条数

paginator.count # 总条数
paginator.num_pages # 总页数
paginator.page_range # 页码数列表
Paginator.page_range:这是从第一页到最后一页组成的一个列表,比如分页对象总共有10页,那么Paginator.page_range = [1,2,3,4,5,6,7,8,9,10]

current_page = paginator.page(5) # 取某一页,返回一个对象
current_page.object_list # 某一页里所有数据,例如:这是第5也所有数据
current_page.has_next() # 是否有下一页
current_page.has_previous() # 是否有上一页
current_page.next_page_number() # 下一页的页码数
current_page,previous_page_number() # 上一页的页码数from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
class getAllUserInfo(View):
def get(self,request):
”’
分页获取用户数据
:param request:
:return: 一个JSON对象{}
”’
allUser = account.objects.all()
# 创建分页
page = request.GET.get(“page”,default=1)
# 创建分页对象,每页10个数据
paginator = Paginator(allUser,10)
try:
content = paginator.page(page) # 获取对应页的数据
except EmptyPage:
content = paginator.page(paginator.num_pages) # 返回最后一页的数据
# content = content.values
content = serializers.serialize(“json”,content)
res_json = {
“status”:200,
“data”:content,# 实际数据
“count”:paginator.count ,# 数据总条数
“num_pages”:paginator.num_pages # 数据总页数
}
print(res_json)
# res_json = json.loads(str(res_json))
return JsonResponse(res_json,safe=False)

  • 从数据库中获取数据
  • 从前端获取需要查询的页码
  • 创建分类器,传入数据,并设置每页的数据个数
  • 获取对应页的数据(该页不存在则返回最后一页数据)
  • 对数据进行序列化,并返回相关数据
    • 该页的数据
    • 数据总条数
    • 数据总页数

前端

创建分页组件<div class=”example-pagination-block”>
<el-pagination
layout=”prev, pager, next”
@current-change=”handleCurrentChange”
@size-change=”handleSizeChange”
:current-page=”currentPage”
:page-size=”pageSize”
:total=”num_pages”
/>
</div>

传入相关参数

  • @current-change:当前页面发生变化(切换页面)时触发的函数
  • @size-change:当前页面数据条数发生变化时触发的函数
  • :current-page:当前页码
  • :page-size:当前页面数据条数
  • :total:所有数据的总页数

在data中定义相关变量 data() {
return {
userList: [],
search: ”,
currentPage: 1, //当前数据页数
pageSize:10, //每页数据条数
total:0 ,//总数据条数
num_pages:1
}
},

定义触发事件时的函数 handleSizeChange(val) {
console.log(`每页 ${val} 条`)
this.pageSize = val
},
handleCurrentChange(val) {
console.log(`当前页: ${val}`)
this.currentPage=val
this.getUserData()
},

请求数据的同时,给相关变量赋值getUserData() {
this.axios.get(“http://127.0.0.1:8000/getalluser/”, {
params: {
“page”:this.currentPage
}
}).then((res) => {
let datas = JSON.parse(res.data.data)
for (var i = 0; i < datas.length; i++) {
this.userList.push(datas[i].fields)
}
this.total = res.data.count // 数据总条数
this.num_pages = res.data.num_pages //总页面数
})

分析过程

第一次获取数据,会将所有的页数返回给前端变量

当点击页面切换时,调用事件函数,更新当前页码变量,并将页码作为参数,重新获取数据

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇