【2022-09-02】Django框架(四)
作者:互联网
Django框架(四)
Django框架之伪静态
概念
静态文件:数据是写死,永远不会修改
伪静态:将一个动态页面伪装成静态页面
# 为什么要伪装?
伪装的目的在于增大本网站的seo查询力度
并且增加搜索引擎收藏本网站的概率:如果搜索引擎发现是一个静态网页,说明这个页面不会再修改了,那么搜索引擎就会把这个网站收录起来,如果有用户搜索该网页相关的信息,那么搜索引擎就会优先把这个网页展示给用户。(这样就大大增加了网站的点击率,得到更多的流量)
# 搜索引擎本质上就是一个巨大的爬虫程序
如:
百度:他在互联网爬取到用户搜索的所有相关信息展示给用户
总结:无论怎么优化,怎么处理,还是干不过RMB玩家。(付费做广告的永远会放在搜索引擎的最前面)
实现
只需要在urls.py配置路由时加一个.html后缀即可以
path('index.html',view.index)
视图层
视图函数返回值
视图函数的返回值问题
视图函数必须返回一个HttpResponse对象
注意HttpResonse其实是一个类
class HttpResponse(HttpResponseBase):
pass
def render():
return HttpResponse(...)
def redirect(to, *args, permanent=False, **kwargs):
redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
return redirect_class(resolve_url(to, *args, **kwargs))
JsonResponse对象
作用:序列化成json格式的数据
json格式的数据有什么作用:
前后端数据交互需要使用用到json作为过渡,实现跨语言传输数据
# 补充:
前端序列化:
JSON.stringify()
JSON.parse()
后端序列化:
json.dumps()
json.loads()
# 使用json模块序列化
import json
def ab_json(request):
user_dict = {'username':'gary我是张三','password':'123','hobby':'girl美女'}
# 将字典序列化为json格式的字符串
json_str = json.dumps(user_dict)
# 将序列化后的字符串返回
return HttpResponse(json_str)
# 解决上述问题:
import json
def ab_json(request):
user_dict = {'username':'gary我是张三','password':'123','hobby':'girl美女'}
# 将字典序列化为json格式的字符串
json_str = json.dumps(user_dict,ensure_ascii=False) # 将内置编码修改
# 将序列化后的字符串返回
return HttpResponse(json_str)
# django提供的模块
from django.http import JsonResponse
def ab_json(request):
user_dict = {'username':'gary我是张三','password':'123','hobby':'girl美女'}
return JsonResponse(user_dict)
# 解决上述问题
from django.http import JsonResponse
def ab_json(request):
user_dict = {'username':'gary我是张三','password':'123','hobby':'girl美女'}
return JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})
# 研究其他形式是否可以序列化:
from django.http import JsonResponse
def ab_json(request):
l = [11,22,33,44,55]
return JsonResponse(l)
from django.http import JsonResponse
def ab_json(request):
l = [11,22,33,44,55]
return JsonResponse(l,safe=False)
# 默认只能序列化字典 序列化其他需要加safe参数
request对象方法
request.method # 获取请求方式
request.POST # 获取普通键值对形式的普通文件
request.GET # 获取GET请求数据
request.FILES # 获取文件数据
request.path # 只能获取路由
request.get_full_path() # 不但获取到路由还能获取到路由后面的参数
request.body # 原生浏览器发过来的二进制数据
form表单提交文件类型数据
# form表单上传文件类型的数据注意事项:
method必须指定成post
enctype必须换为:multipart/form-data
urls.py
urlpatterns = [
url(r'^ab_file/',views.ab_file)
]
form.html
<form action="" method="post" enctype="multipart/form-data">
<p>test:<input type="text" name="test"></p>
<p>file:<input type="file" name="file"></p>
<input type="submit">
</form>
views.py
def ab_file(request):
if request.method == 'POST':
print(request.POST) # 著获取普通的键值对数据 文件不行
print(request.FILES) # 获取文件数据
file_obj = request.FILES.get('file') # 获取文件对象
print(file_obj.name) # 拿到文件名字
with open(file_obj.name,'wb') as f:
for line in file_obj:
f.write(line)
return render(request,'form.html')
FBV与CBV
FBV与CBV
FBV:基于函数的视图
def index(request):
return HttpResponse()
path('index/', views.index)
CBV:基于类的视图
from django import views
class MyView(views.View):
def get(self, request):
return HttpResponse('我是CBV里面的get方法')
def post(self, request):
return HttpResponse('我是CBV里面的post方法')
path('func/', views.MyView.as_view())
"""
CBV会自动根据请求方式的不同匹配类中定义的方法并自动执行
"""
CBV源码剖析
准备工作:做一个简单的CBV来研究路由层(urls.py)到底是怎么触发视图层(views.py)的类方法的。
views.py
from django.views import View
class MyLogin(View):
def get(self,request):
return render(request,'login.html') # 收到get请求走这个方法返回一个页面
def post(self,request):
return HttpResponse('post请求') # 收到post请求走这个返回一个'字符串'
login.html
<form action="" method="post">
<input type="submit"> <!--点击提交按钮触发post请求-->
</form>
urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'login/',views.MyLogin.as_view())
]
那么我们如何去研究呢?
# 我们先来探讨url
url(r'login/',views.MyLogin.as_view())
# 我们可以看到他与FBV的区别就在于后面所对应的 类名.as_view()
我们知道在python中一切皆'对象',那么类使用(.)的方式时,类(.)的是肯定是一个属性或者一个方法
那么说明as_view()要么是一个属性要么是一个方法。
但是他加了一个(), 我们可猜测他是一个方法,他就相当于'函数名+()'
# 补充: 函数名/方法名 加括号()执行优先级最高,
这就就相当于在用户访问路由后缀为login/时,就会立刻执行views下的MyLogin类下的as_view()
# 那么我们就来研究这个as_view()到底是什么样的逻辑呢。
猜测:as_view() # 要么是被@staticmethod修饰的静态方法
as_view() # 要么是被@classmethod修饰的类方法
# 通过ctrl+左键的方式点进去看一下他的源码
@classonlymethod
def as_view(cls, **initkwargs):
# 通过源码我们可以看到他确实是一个绑定给类的方法,类来调用,就把类当作第一个参数传入
我们来研究一个这个函数:
函数的返回值:view # 为内部函数的函数名
# 那么在启动django的时候就会立刻执行as_view方法,as_view的返回值为view
那么就相当于:
url(r'login/',views.MyLogin.as_view())
同等于:
url(r'^login/,views.view') # 那么这个结果是不是和FBV模式一摸一样
# 通过这一点:CBV与FBV在路由匹配上本质是一样的。都为(路由,views.函数内存地址)
那么在用户输入路由后缀为login/的时候就会自动触发view方法。
研究view方法
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls为我们自己写的类(MyLogin) 加括号后产生对象
# 相当于:self = MyLogin(**initkwargs) # 类加()就产生一个我们自己写的类的对象self
if hasattr(self, 'get') and not hasattr(self, 'head'): # 反射
self.head = self.get
self.request = request # 这里是给对象赋值一些属性
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
# 对象.一个属性 :查找属性的顺序:先去对象自己找,再去产生对象的类里面找,之后再去父类里找
# 一定要明白当前的self是谁。(这里self为我们自己写的类产生的对象)
# 那么这里没有dispatch方法 ,我们自己写得类里面也没有,那么就去类继承得父类View查看有没有该方法
# 那么我们就去找一下这个dispatch属性/方法
研究:View: dispatch
# 很明显在上述的view方法里没有,我们自己写的MyLogin类里肯定也是没有的,那么我们就来研究视图层:我们所编写的类所继承的(View)
class MyLogin(View): # 同样通过ctrl+鼠标左键的方式点进去看一下
# 我们来研究一下这个代码
def dispatch(self, request, *args, **kwargs):
# 获取当前请求方式并转为小写 然后比对当前比对是否合法(这里就以'GET'请求为例)
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
# getattr反射:通过字符串来操作对象的属性或者方法(那么第二个参数位置即为'get')如果'get'方法有值就返回get方法得返回值,我们在定义类得时候,定义了get方法的,所有就会返回get方法得返回值
# 括号内参数解释:(self:自己写的类产生的对象,'get',当找不到get方法的时候就会找第三个参数)
# 那么现在就相当于:
# handler = getattr(我们自己写的类产生的对象,'get','当找不到get属性或者方法就会用第三个参数返回该参数对应方法得返回值')
# get如果存在:hanler就相当于:handler = 我们自己写的get方法
else:
handler = self.http_method_not_allowed # 找不到get方法走的方法(抛出异常)
return handler(request, *args, **kwargs) # 自动调用我们自己写的get方法
# 那么post方法同理
这样就疏通了CBV的执行流程。其实内部还是FBV
模板层
templates模板语法的传值
# 模板语法的格式:
{{ }} :跟变量相关的时候使用
{% %} :跟逻辑相关的时候使用
基本语法传值研究:
研究函数:
def func():
print('无参函数')
return '无参函数返回值'
def func1(xx):
print('有参函数')
return '有参函数返回值'
# 特点:传递函数名会自动加括号调用,但是模板语法不支持给函数传额外的参数
研究类:
class Myclass():
def get_self(self):
return 'self'
@staticmethod # 转换为普通函数
def get_func():
return 'func'
@classmethod # 绑定给类的方法
def get_class(cls):
return 'cls'
# 对象被展示到html页面上,也相当于执行了打印操作也会触发__str__方法a
def __str__(self):
return '是否加载呢'
obj = Myclass() # 类名加括号实例化产生一个对象
验证:模板语法的取值方式:
总结:
# django模版语法的取值 是固定的格式 只能采用“句点符” .
# 即可以点键也可以点索引 还可以两者混用
模板语法传值范围
模板语法传值的范围
基本数据类型直接传递使用
函数名的传递会自动加括号执行并将返回值展示到页面上
注意函数如果有参数则不会执行也不会展示 模板语法不支持有参函数
类名的传递也会自动加括号产生对象并展示到页面上
对象的传递则直接使用即可
ps:模板语法会判断每一个名字是否可调用 如果可以则调用!!!
"""django的模板语法在操作容器类型的时候只允许使用句点符"""
模板语法过滤器
模板语法过滤器(类似于python内置函数)
<p>统计长度:{{ s|length }}</p>
<p>加法运算:{{ i|add:123 }}、加法运算:{{ s|add:'heiheihei' }}</p>
<p>日期转换:{{ s|date:'Y-m-d H:i:s' }}</p>
<p>文件大小:{{ file_size|filesizeformat }}</p>
<p>数据切片:{{ l|slice:'0:10' }}</p>
<p>字符截取(三个点算一个):{{ s1|truncatechars:6 }}</p>
<p>单词截取(空格):{{ s1|truncatewords:6 }}</p>
<p>语法转义:{{ script_tag|safe }}</p>
<p>语法转义:{{ script_tag1|safe }}</p>
from django.utils.safestring import mark_safe
script_tag1 = '<script>alert(666)</script>'
res = mark_safe(script_tag1)
ps:有时候html页面上的数据不一定非要在html页面上编写了 也可以后端写好传入
'''django模板语法中的符号就两个 一个{{}} 一个{%%}
需要使用数据的时候 {{}}
需要使用方法的时候 {%%}
'''
模板语法之标签的使用(if,for..)
if判断
语法结构:
{% if b %} # 判断b是否为True
<p>if</p> # 条件成立执行
{% elif s %} # 上述条件为False判断elif条件
<h1>elif</h1> # elif条件成立执行
{% else %} # 上述都为False
<p>else</p>
{% endif %} # 结束语法
# 可直接输入if按Tab键补全语法结构
for循环
语法结构:
{% for 变量名 in 待循环集 %}
循环体代码
{% endfor %}
# 可直接输入for按Tab键补全for循环的语法结构
eg:
{% for foo in l %}
{{ foo }}
{% endfor %}
关键字:forloop
# forloop关键字可标识数据的状态
first:标识for循环是第一次
last :标识for循环时最后一次
counter0 : 索引
counter : 计数
revcounter :倒序计数
revcounter0:倒序索引
for与if混合使用
{% for foo in lll %}
{% if forloop.first %}
<p>这是我的第一次</p>
{% elif forloop.last %}
<p>这是最后一次啊</p>
{% else %}
<p>{{ foo }}</p>
{% endif %}
{% empty %}
<p>for循环的可迭代对象内部没有元素 根本没法循环</p>
{% endfor %}
处理字典的其他方法
# 处理字典其他方法
{% for foo in d.keys %} # keys
<p>{{ foo }}</p>
{% endfor %}
{% for foo in d.values %} # values
<p>{{ foo }}</p>
{% endfor %}
{% for foo in d.items %} # items
<p>{{ foo }}</p>
{% endfor %}
with起别名
{% with dd.hobby.2.info as nb %}
<p>{{ nb }}</p>
# 在with语法内就可以通过as后面的别名快速的使用到前面非常复杂获取数据的方式
<p>{{ dd.hobby.2.info }}</p> # 也可以使用之前的
{% endwith %}
自定义过滤器、标签、inclusion_tag
自定义前注意事项:
1. 在应用下创建一个名字'必须'为templatetags文件夹
2. 在该文件夹内创建'任意'名称的py文件 比如:mytag.py
3. 在该py文件内'必须'编写下面两句话
from django import template
register = templante.Library()
# 注:变量名也不能改变
自定义过滤器:
# 关键字:@register.filter(name='自定义名字')
eg:
# 自定义过滤器:
from django import template
register = template.Library()
@register.filter(name='mysum')
def my_sum(v1,v2):
return v1+v2
# 使用
{% load mytag %} # 导入文件
<p>{{ n|mysum:s }}</p> # 字符串拼接
<p>{{ i|mysum:222 }}</p> # 数字相加
自定义标签:
# 自定义标签
@register.simple_tag(name='plus')
def index(a,b,c,d):
return '%s-%s-%s-%s'%(a,b,c,d)
# 具体使用
{% load mytag %}
<p>{% plus 'gary' 28 'age' 20 %}</p>
自定义inclusion_tag
# 内部原理
先定义一个方法
在页面上调用该方法 并且可以传值
该方法会生成一些数据然后传递给一个html页面
之后将渲染好的结果放到调用的位置
# 自定义inclusion_tag
@register.inclusion_tag('left_menu.html')
def left(n):
data = ['标签{}'.format(i) for i in range(n)] # 列表生成式
# 将data传递给'left_menu.html'
# 第一种方式:
# return {'data':data}
# 第二种方式:
return locals()
# left_menu.html
<ul>
{% for foo in data %} # for循环data列表
<li>{{ foo }}</li> # 添加到li标签内
{% endfor %}
</ul>
# 使用
{% load mytag %} # 导入文件
{% left 10 %} # 参数可指定li标签的个数
模板的继承与导入
模板继承
准备工作:
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-md-3">
<div class="list-group">
<a href="/home/" class="list-group-item active">
首页
</a>
<a href="/login/" class="list-group-item">登录</a>
<a href="/reg" class="list-group-item">注册</a>
</div>
</div>
<div class="col-md-9">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Panel title</h3>
</div>
<div class="panel-body">
<div class="jumbotron">
<h1>Hello, world!</h1>
<p>...</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
# 先搭一个简单的框架
# 需求:
点击登录/注册:只改变右侧的巨幕部分其他部分不改变
初始继承:
views.py
def home(request):
return render(request,'home.html')
def login(request):
return render(request,'login.html')
def reg(request):
return render(request,'reg.html')
# 继承关键字:extends
{% extends 'home.html' %} # 直接继承home.html
我们可以看到:只需要一个继承就可以完全继承home页面的所有内容
但是:这并不符合我们的要求 登录/注册页面肯定要有变化 那么我们怎么知道那一片区域进行修改呢
# 这里就需要在模板home.html提前定义好区域这里就要用到指定的模板语法
block模板语法
# 继承了之后子页面跟模版页面长的是一模一样的 你需要在模版页面上提前划定可以被修改的区域
# block会提前定义好区域
格式:
{% block content %}
标识代码块
{% endblock %}
login.html
{% extends 'home.html' %}
{% block content %}
<h1 class="text-center">登录页面</h1>
<form action="">
<p>username:<input type="text" name="username" class="form-control"></p>
<p>password:<input type="password" name="password" class="form-control"></p>
<input type="submit" class="btn btn-success">
</form>
{% endblock %}
reg.html
{% extends 'home.html' %}
{% block content %}
<h1 class="text-center">注册页面</h1>
<form action="">
<p>username:<input type="text" name="username" class="form-control"></p>
<p>password:<input type="password" name="password" class="form-control"></p>
<input type="submit" class="btn btn-danger">
</form>
{% endblock %}
# 一般情况下模版页面上应该至少有三块可以被修改的区域
1.css区域
2.html区域
3.js区域
# 每一个子页面就都可以有自己独有的css代码 html代码 js代码
"""
一般情况下 模版的页面上划定的区域越多 那么该模版的扩展性就越高
但是如果太多 那还不如自己直接写
"""
模板导入
# 将页面的某一个局部当成模块的形式,那个地方需要就可以导入使用即可
# 关键字:include
# 格式:
{% include 'html页面' %}
# eg:
标签:02,return,get,self,09,request,html,2022,def 来源: https://www.cnblogs.com/dy12138/p/16650966.html