路由分发
1.django是专注于开发应用的,当一个django项目特别庞大的时候,所有的路由与视图函数映射关系全部写在项目名下urls.py(总路由),很明显太冗余也不便于管理,这个时候也可以利用路由分发来减轻总路由的压力。
2.django的每一个应用(app)都可以有自己独立的urls.py路由器,static文件夹,templates文件夹
3.基于上述特点,使用django做分组开发非常的简便。每个人只需要写自己的应用即可,互不干扰。最后由组长统一汇总到一个空的django项目中然后使用路由分发将多个应用关联在一起,即可完成大项目的拼接。
路由分发解决的就是项目的总路由匹配关系过多的情况
利用路由分发之后 总路由不再干路由与视图函数的直接的对应关系
总路由而是做一个分发处理(识别当前url是属于那个应用下的 直接分发对应的应用去处理)
请求来了之后 总路由不做对应关系 只询问你要访问哪个app的功能 然后将请求转发给对应的app去处理
提前创建好 应用app01 应用app02 然后记得注册
1.总路由配置
# 1.需要导入一个include路由分发模块
from django.conf.urls import url,include
# 2.导入子路由的uls(重名问题 起别名)
from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = [
path('admin/', admin.site.urls),
# 1.路由分发
path('app01/',include(app01_urls)), # 只要url前缀是app01开头 全部交给app01下的urls处理
path('app02/',include(app02_urls)) # 只要url前缀是app02开头 全部交给app02下的urls处理
]
名称空间
路由分发之后 针对相同的别名能否自动反向解析出不同的应用前缀
默认情况下是无法直接识别应用前缀的
解决方式一:名称空间
总路由增加一个名称空间
path('app01/',include(('app01.urls','app01'), namespace='app01')) # 创建一个名称空间app01
path('app02/',include(('app01.urls','app02'), namespace='app02')) # 创建一个名称空间app02
# 子路由
path('after/', views.after, name= 'after_view' )
# 后端
def reg(request):
print(reverse('app01:after_view'))
return HttpResponse('good afternoon from app01')
# 前端
{% url 'app01:after_view' %}
解决方式二:别名
# 子路由
path('after/',views.after, name='app01_after_view')
# 后端
def reg(request):
print(reverse('app01_after_view'))
return HttpResponse('good afternoon from app01')
# 前端
{% url 'app01:after_view' %}
1.其实只要保证名字不冲突 就没有必要使用名称空间
2.解决方法
一般情况下 有多个app的时候我们在起别名的时候会加上app的前缀
这样的话就能够确保多个app之间名字不冲突的问题
虚拟环境
实景应用:
项目1需要使用:django1.11 python38
项目2需要使用:django2.22 pymysql requests python38
项目3需要使用:django3.22 request_html flask urllib3 python38
实际开发项目中我们只会给项目配备所需的环境,不需要的一概不配!!!
定义:虚拟环境:能够针对相同版本的解释器创建多个分身 每个分身可以有自己独立的环境
pycharm创建虚拟环境:(没创建一个虚拟环境就相当于重新下载了一个全新的的解释器)
# 命令行创建
创建命令
python -m venv pyvenv38 --> 创建的虚拟的python名称
激活
activate
关闭
deactivate
注意:python命令不支持多版本共存的操作 会默认是环境变量中最上面的python解释器
下载django报错时
pip install --index-url http://mirrors.aliyun.com/pypi/simple/ django==1.11.11 --trusted-host mirrors.aliyun.com
视图层之必会三板斧
Djanggo视图层,视图就是Django项目下views.py文件,它的内部是一系列的函数或者是类,用来专门处理客户端访问请求后处理请求并且返回相应的数据,相当于一个中央情报处理系统。
三板斧
"""
HttpResponse
返回字符串类型
httpresponse() 括号内直接跟一个具体的字符串作为响应体,比较直接简单,所有这里主要介绍后面两种形式。
render
返回html页面 并且在返回给浏览器之前还可以给html文件传值
render(request, template_name[, context])结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
# 参数:
request: 用于生成响应的请求对象。
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。
redirect
传递要重定向的一个硬编码的URL
def my_view(request):
rerurn redirect('/some/url/')
也可以是一个完整的URL
def my_view(request):
...
return redirect('http://www.baidu.com/')
"""
2.三板斧的本质
视图app01没有返回一个HttpResponse对象,返回一个None代替了
Django视图层函数必须要返回一个HttpResponse对象
django视图层函数必须有一个返回值,并且返回值的数据类型必须是HttpResponse对象
研究底层源码
1.def index(request):
return HttpResponse()
"""
按住ctrl点击进入HttpResponse:
发现HttpResponse其实是一个类,那么类名加()实例化产生一个对象
class HttpResponse(HttpResponseBase):
pass
"""
2.def index(request):
return render()
"""
按住ctrl点击进入render:
def render(request, template_name, context=None, content_type=None, status=None, using=None):
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)
先执行的是render函数,然后render函数返回的是HttpResponse(content, content_type, status)
"""
3.def index(request):
return redirect()
"""
按住ctrl点击进入redirect:
def redirect(to, *args, permanent=False, **kwargs):
redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
return redirect_class(resolve_url(to, *args, **kwargs))
按住ctrl点击进入HttpResponsePermanentRedirect:
class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
pass
按住ctrl点击进入HttpResponseRedirectBase:
class HttpResponseRedirectBase(HttpResponse):
pass
会发现它继承的也是HttpResponse
按住ctrl点击进入HttpResponseRedirect:
class HttpResponseRedirect(HttpResponseRedirectBase):
pass
按住ctrl点击进入HttpResponseRedirectBase:
class HttpResponseRedirectBase(HttpResponse):
pass
会发现它继承的也是HttpResponse
"""
'''综上研究发现:Django视图层函数必须要返回一个HttpResponse对象'''
3.视图函数返回json格式数据
def index(request):
# 将后端字典序列化发送到前端
def index(request):
# 将后端字典序列化发送到前端
user_dict = {'name': 'jason', 'pwd': 123, 'hobby': ['read', 'run', 'music']}
# 先转成json格式字符串
json_str = json.dumps(user_dict)
# 将该字段返回
return HttpResponse(json_str)
解决这个中文输入的问题我们之前是加了ensure_ascill=False,ensure_ascii 内部默认True自动转码,改为False不转码,只生成json格式,双引号
以上实现返沪json格式数据比较麻烦,又得转换json格式又得编码设置,那么Django的出现就在这方面做出了改变
JsonResponse对象
# 导入JsonResponse模块
from django.http import JsonResponse
def index(request):
user_dict = {'name': 'jason老师', 'pwd': 123, 'hobby': ['read', 'run', 'music']}
# json_str = json.dumps(user_dict, ensure_ascii=False)
return JsonResponse(user_dict, json_dumps_params={'ensure_ascii':False}) # 直接写入JsonResponse,不需要去做json序列化操作
# user_dict,json_dumps_params={'ensure_ascii':False} :解决前端中文转码问题
JsonResponse底层
# 继承了HttpResponse,返回的还是HttpResponse对象
class JsonResponse(HttpResponse):
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
json_dumps_params=None, **kwargs):
# json_dumps_params=None,注意这个参数,是一个默认参数
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
if json_dumps_params is None:
# 对应开始json_dumps_params=None,
json_dumps_params = {}
# 如果我们在这里传入了一个值,那么这里的json_dumps_params = {'XXX':'XXX'},然后在下面的data里面转换为的是关键字参数,XXX=XX
kwargs.setdefault('content_type', 'application/json')
'''
将data数据进行了json序列化,然后做了一个返回
**json_dumps_param,**加在字典前面,现在是在调用一个函数,那么它在这里做实参,**在是实参中将字典打散为关键字参数形式,就是什么等于什么
'''
data = json.dumps(data, cls=encoder, **json_dumps_params)
super().__init__(content=data, **kwargs)
JsonResponse序列化(列表注意事项)
# 导入JsonResponse模块
from django.http import JsonResponse
def ab_json(request):
l = [111,222,333,444,555]
# 默认只能序列化字典 序列化其他需要加safe参数 safe=False
return JsonResponse(l,safe=False)
json与pickle区别
import json
'''支持的数据类型:str,list, tuple, dict, set'''
# 序列化出来的数据是可以看得懂的,就是一个字符串
dumps 将Python对象转换成json字符串
loads
dump 将Python对象写入json文件
load
import pickle
'''支持的数据类型:python中的所有数据类型'''
# 序列化出来的结果看不懂,因为结果是一个二进制
# pickle序列化出的来的数据只能在python中使用
dumps
loads
dump
load
视图层之request对象获取文件
form表单携带文件类型的数据需要做到以下几点
1.method必须是post
2.enctype必须是multipart/form-data
后端获取文件数据的操作,request对象方法
1.获取请求方式POST/GET
request.method
一个字符串,表示请求使用的HTTP 方法。必须使用大写。
2.request.POST
获取POST请求提交普通的键值对数据 一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成
3.获取GET请求
request.GET
获取GET请求 一个类似于字典的对象,包含 HTTP GET 的所有参数
4.获取文件
request.FILES
一个类似于字典的对象,包含所有的上传文件信息。
FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。
注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会包含数据。否则,FILES 将为一个空的类似于字典的对象。
5.原生的浏览器发过来的二进制数据
request.body
一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,
例如:二进制图片、XML,Json等。
6.拿到路由
request.path
print(request.path) # /app01/index/
个字符串,表示请求的路径组件(不含域名)
7.拿到路由
request.path_info
print(request.path_info) # /app01/index/
8.能过获取完整的url及问号后面的参数
request.get_full_path()
print(request.get_full_path()) # /app01/index/?username=jason
import json
from django.shortcuts import render, HttpResponse, redirect
from django.http import JsonResponse
# Create your views here.
def index(request):
# user_dict = {'name': 'jason老师', 'pwd': 123, 'hobby': ['read', 'run', 'music']}
# # json_str = json.dumps(user_dict, ensure_ascii=False)
# return JsonResponse(user_dict, json_dumps_params={'ensure_ascii':False}) # 直接写入JsonResponse,不需要去做json序列化操作
#
# # user_dict,json_dumps_params={'ensure_ascii':False} :解决前端中文转码问题
if request.method == 'POST':
# 普通的键值对形式的数据
print(request.POST)
# 专门用来携带过来的文件数据,流式的文件数据
print(request.FILES) # <MultiValueDict: {'file': [<InMemoryUploadedFile: 今日内容.md (application/octet-stream)>]}>
# 也可以取到这个文件,注意它也是一个Dict,可以通过点的方式得到,'file'由前端的name标签决定
file_obj = request.FILES.get('file')
# 可以使用这个对象去点出一些方法
print(file_obj.name)
# 也可以将这个文件保存下来,这个文件可以是文本文件,也可以是音频文件,只不过pycharm不能拿播放音频文件
with open(file_obj.name, 'wb') as f:
for line in file_obj:
f.write(line)
return render(request, 'index.html')
FBV与CBV
1.FBV与CBV区别
FBV基于函数的视图(Function base view) 我们前面写的视图函数都是FBV
CBV基于类的视图(Class base view)
视图文件中除了用一系列的函数来对应处理客户端请求的数据逻辑外,还可以通过定义类来处理相应的逻辑。
# 视图函数既可以是函数也可以是类
def index(request):
return HttpRerponse('index')
2.CBV
从index页面,后台修改action的参数,朝func发送post请求
从index页面,后台修改action的参数,朝func发送post请求
CBV比起FBV来说的话,不需要通过request.method来判断当前是什么请求,CBV会自动判断,是哪种请求,就去触发哪种方法(写在类里面里的函数叫做方法)
CBV底层源码
源码分析入口
path('func/', views.MyView.as_view())
"""
# 面向对象属性方法查找顺序
1.先从对象自己名称空间找
2.在去产生类对象的类里面找
3.在去父类里面找
"""
1.绑定给类的as_view方法(它是我们自己写的类里面继承的类)
class View:
@classonlymethod
def as_view(...):
绑定给类的,类调用会自动将类当作第一个参数传入
def view(...):
pass
return view
2.CBV路由匹配本质:跟FBV是一致的
path('func/', views.view)
3.访问func触发view执行
def view(...):
obj = cls() # 我们自己写的类加括号产生的对象
return obj.dispatch()
'''涉及到对象点名字 一定要确定对象是谁 再确定查找顺序'''
4.研究dispatch方法
def dispatch(...):
判断 request.method将当前请求方式转成小写 在不在 self内 self==MyLogin
"http_method_names 内有八个请求方式 合法"
['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
if request.method.lower() in self.http_method_names:
# getattr 反射: 通过字符串来操作对象的属性或者方法
func_name = getattr(obj,request.method.lower())
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
用到了一个反射的知识,从obj这个对象里面,找一个request.method.lower()这个的函数
模板层
1.模板语法的传值
方式一:指名道姓的传
urls代码:
path('modal/', views.modal)
views代码:
def modal(request):
name = 'jason'
return render(request, 'modal.html', {'name':name})
指名道姓传参 不浪费资源
方式二:关键字locals()
html代码:
<body>
{{ name }}
{{ age }}
{{ gender }}
</body>
urls代码:
path('modal/', views.modal)
views代码:
def modal(request):
name = 'jason'
age = 18
gender = 'female'
return render(request, 'modal.html', locals()) # 将函数体里局部空间里所有的名字全部传入给前面的那个页面
将整个局部名称空间中的名字去全部传入简单快捷
2.模板语法传值的范围
基本数据类型直接传递使用
views代码
def modal(request):
i = 123
f = 13.4
s = 'study work hard'
d = {'name': 'Tony', 'pwd': 123}
t = (11, 22, 33)
se = {11, 22, 33}
b = True
return render(request, 'modal.html', locals())
html代码
<body>
<p>{{ i }}</p>
<p>{{ f }}</p>
<p>{{ s }}</p>
<p>{{ l }}</p>
<p>{{ d }}</p>
<p>{{ t }}</p>
<p>{{ se }}</p>
<p>{{ b }}</p>
</body>
函数名的传递会自动加括号执行并将返回值展示到页面上,注意函数如果有参数则不会执行也不会展示 模板语法不支持有参函数
views代码
def modal(request):
def func1():
print('函数')
return '这里才能传过去值哦'
return render(request, 'modal.html', locals())
html代码
<body>
{#传递函数名会自动加括号调用 但是模板语法不支持给函数传额外的参数#}
<p>{{ func1 }}</p>
</body>
类名的传递也会自动加括号产生对象并展示到页面上,对象的传递则直接使用即可,对象默认情况下不可以加括号了,除非自己写__call__方法,模板语法会判断每一个名字是否可调用 如果可以则调用!!!
views代码
class MyClass(object):
def get_obj(self):
return 'obj'
@classmethod
def get_cls(cls):
return 'cls'
@staticmethod
def get_func():
return 'func'
obj = MyClass()'
return render(request, 'modal.html', locals())
html代码
<body>
{#传类名的时候也会自动加括号调用(实列化) 生成对象#}
<p>{{ MyClass }}</p>
{#内部能够自动判断出当前的变量名是否可以加括号调用 如果可以就会自动执行 针对的是函数名和类名#}
<p>{{ obj }}</p>
{#模板语法支持对象调用类方法#}
<p>{{ obj.get_self }}</p>
<p>{{ obj.get_func }}</p>
<p>{{ obj.get_class }}</p>
</body>
3.模板语法之过滤器(类似于python内置函数)
1.模板语法过滤器
过滤器就类似于是模板语法内置的 内置方法
模板语法提供了一些内置方法,以助于快速的处理数据(过滤器最多只能有两个参数)
2.基本语法
{{ 变量|过滤器:参数}}
过滤器:会自动将竖杠左侧的变量当做第一个参数交给过滤器处理,右侧的当做第二个参数。
3.过滤器的内置方法
<p>统计长度:{{ s|length }}</p>
# 统计长度:15
<p>加法运算:{{ i|add:123 }}、加法运算:{{ s|add:'heiheihei' }}</p>
# 加法运算:246、字符串加法运算:study work hard +every day
<p>日期转换:{{ s|date:'Y-m-d H:i:s' }}</p>
<p>文件大小:{{ file_size|filesizeformat }}</p>
# 文件大小:31.1 KB
<p>数据切片:{{ l|slice:'0:10' }}</p>
# 切片操作(支持步长)
<p>字符截取(三个点算一个):{{ s1|truncatechars:6 }}</p>
# 字符截取(包含三个点):模板语法提供...
<p>单词截取(空格):{{ s1|truncatewords:6 }}</p>
# 单词截取(不包含三个点 按照空格切):my name is objk my age is 18 and ...
<p>语法转义:{{ script_tag }}</p>
# <h1>今晚晚饭又有好多好吃的了</h1>(是不会识别前端标签的)
<p>语法转义:{{ script_tag|safe }}</p>
# 今晚晚饭又有好多好吃的了
# 如果不转移的话,那么无法识别前端标签,那么以下这种功能的情况就没有办法实现
<p>语法转义:{{ script_tag1|safe }}</p>
script_tag1 = '<script>alert(666)</script>'
# 也可以使用后端转
from django.utils.safestring import mark_safe
script_tag2 = '<script>alert(666)</script>'
res = mark_safe(script_tag2)
ps:有时候html页面上的数据不一定非要在html页面上编写了 也可以后端写好传入
'''django模板语法中的符号就两个 一个{{}} 一个{%%}
需要使用数据的时候 {{}}
需要使用方法的时候 {%%}
'''
标签:return,Django4,request,json,app01,HttpResponse,def
From: https://www.cnblogs.com/zjl248/p/16980549.html