Django之视图层
路由分发
当一个django项目特别庞大时,所有路由与视图函数映射关系全部写在项目名下urls.py(总路由层),这时可利用路由分发来减轻总路由的压力。
django里面的app可以有自己的static文件,templates文件夹,urls.py(***)。
路由分发:项目名下面的urls.py不再做路由与视图函数对应关系,而做一个中转站,仅负责将请求分发不同的app中,然后在app的urls.py完成路由与视图函数的对应关系。
路由分发之前,总路由直接干路由与视图函数的匹配
path('index/',index)
路由分发之后,总路由只按照应用名分配匹配方向
path('app01/',include('app01.urls'))
名称空间
即便不同的app使用相同的URL名称,URL的命名空间模式也可以让你唯一反转命名的URL。有路由分发场景下多个应用在涉及到反向解析别名冲突时,无法正常解析。
如果想要正常识别区分有两种方式:
方式1:名称空间
#总路由增加一个名称空间
path('app01/', include(('app01.urls', 'app01'), namespace='app01')),# 创建名称空间app01
path('app02/', include(('app02.urls', 'app02'), namespace='app02')), # 创建名称空间app02
# 子路由app01
urlpatterns = [
path('after/',views.after, name='after_view')
]
# 后端
def reg(request):
print(reverse('app01:after_view'))
return HttpResponse('下午好 from app01')
# 前端
{% url 'app01:after_view' %}
方式2:起别名不冲突即可
多个应用别名不冲突可以用应用名作为别名的前缀
# 子路由app01
urlpatterns = [
path('after/',views.after, name='app01_after_view')
]
# 后端
def reg(request):
print(reverse('app01_after_view'))
return HttpResponse('嘿嘿嘿 from app01')
# 前端
{% url 'app01:after_view' %}
# 只要保证名字不冲突,就没有必要使用名称空间;
# 多个app时,起别名加上app的名字作为别名前缀,也能解决多个app之间名字冲突的问题
虚拟环境
虚拟环境可以看做是原生python的副本,但因为标准库都一样,所以每次都复制是不合算的,直接调用原来的标准库就行。
同时解释器也存到Scripts这个目录下,path环境变量只需要增加一个即可。
方法一:
使用venv创建虚拟环境及其目录结构;
在cmd中使用python -m venv 虚拟环境名称 创建
方法二:
pycharm依次点击file-sittings-project-python interpreter-Add python interpreter-virtualenv environment-New environment
location一般为当前工程目录
Base interpreter为自己创建的虚拟环境
# 虚拟环境的激活与关闭
激活 activate
关闭 deactivate


视图层之必会三板斧
# HttpResponse、render、Redirect
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/')
jsonresponse对象
# json序列化形式
def index(request):
user_dict = {'name': 'jason', 'age': 123}
data = json.dumps(user_dict)
return HttpResponse(data)
"""
若字典数据中存在文字,输出的结果会进行编码,所以我们可以加参数ensure_ascii,这个参数默认为Ture,我们让这个参数等于False即可
"""
# JsonResponse序列化形式
def index(request): # 视图函数返回json格式数据
user_dict = {'name': 'jason', 'age': 123}
return JsonResponse(user_dict)
如果字典数据中存在中文字,输出的结果会进行编码。
查看JsonResponse源码可发现,如果json_dumps_params如果为None的话,就是一个空字典;然后又根据关键字形参的知识我们可以加json_dumps_params={'ensure_ascii': False}
"""
JsonResponse主要序列化字典,针对非字典的其他可以被序列化的数据需要修改safe参数为False
"""
视图层之request对象获取文件
# form表单携带文件类型的数据需要做到以下几点
enctype属性由默认的urlencoded变成form/data(enctype=''multipart/form-data'')
method属性由默认的get变成post
# 提交post请求需要将配置文件中的csrf中间件注释
form表单上传文件,后端需要在request.FILES获取文件数据,而不再是POST里面
# 代码示例:
views.py
def file(request): # 文件数据的读取
if request.method == 'POST':
file_info = request.FILES.get('file')
with open(file_info.name, 'wb') as f:
for line in file_info:
f.write(line)
return render(request, 'json.html')
print(request.POST) # <QueryDict: {'username': ['jason']}>
print(request.FILES) # <MultiValueDict: {'file': [<InMemoryUploadedFile: 今日考题.md (application/octet-stream)>]}>
print(file_info.name) # 今日内容.md
HTML
<form action="" method="post" enctype="multipart/form-data">
<p>username:<input type="text" name="username"></p>
<p>文件:<input type="file" name="file"></p>
<input type="submit" name="">
</form>
视图层之FBV与CBV
FBV(Function Based View):基于函数的视图
CBV(Class Based View):基于类的视图
视图文件中除了用一系列的函数来对应处理客户端请求的数据逻辑外,还可以通过定义类来处理相应的逻辑。
# 视图函数既可以是函数也可以是类
def index(request):
return HttpResponse('index')
CBV源码剖析
示例:
class MyView(views.View):
def get(self, request):
return HttpResponse('我是get')
def post(self, request):
return HttpResponse('我是post')
运行后会发现,类里面的函数自动调用了。
"""
如果请求方式是GET 则会自动执行类里面的get方法
如果请求方式是POST 则会自动执行类里面的post方法
"""
学会查看源码解决问题,查看CBV源码:
1.CBV路由层
path('view/',views.MyView.as_view())
# 在这必须调用自己写的MyReg类中的as_view方法
# as_view的源码
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls就是MyView类
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)
# 对象在查找属性或方法的时候,顺序是什么?先从自己找,再从产生对象的类中找,再去类的父类中找...
"""查看源码的时候,要牢记上面的话"""
return view
2.视图层views.py中写上继承view的类
class MyView(views.View):
def get(self, request):
return HttpResponse('我是get')
def post(self, request):
return HttpResponse('我是post')
3.在前端发送请求的时候会进到CBV源码的dispatch方法,判断请求方式在不在默认的八个请求方式中
"""CBV源码最精髓的部分"""
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
# 判断当前请求方式在不在默认的八个请求方式中
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
# handler = getattr(自己写的类产生的对象,'小写的请求方法(get\post)','获取不到对应的方法就报错')
# handler就是我们自己定义的跟请求方法相对应的方法的函数内存地址
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # 再调用获取到的方法
"""
八个请求方式:
http_method_names = [‘get’, ‘post’, ‘put’, ‘patch’, ‘delete’, ‘head’, ‘options’, ‘trace’]
"""