路由层
有名无名分组反向解析
给视图函数的其别名的时候,前端或后端进行反向解析的时候必须要括号内分组的路由给定一个参数,一个数字,可以理解为给分组的路由指定一个匹配结果,让它知道具体要匹配什么内容。
无名分组反向解析
# 无名分组反向解析url路由
url(r'^index/(\d+)/',views.index,name='xxx')
# 前端
{% url 'xxx' 111 %}
# 后端
reverse('xxx',args=(1,))
这个数字一般情况是放数据的主键值,前端通过GET请求传给后端,后端拿到数据的主键值,通过主键值查找到对应的数据进行操作(数据的查找、编辑和删除)。
如:
# 路由url
url(r'^edit/(\d+)/',views.edit,name='xxx')
# 前端
<a href="{% url 'xxx' user_obj.id %}">编辑</a> # 存放当前操作数据的id值
# 后端
def edit(request,edit_id):
reverse('xxx',args=(edit_id,))
edit_obj = models.User.objects.filter(id=edit_id).first()
...
有名分组反向解析
# 路由url
url(r'^func/(?P<year>\d+)/',views.func,name='ooo')
# 前端
# 写法1
<a href="{% url 'aaa' named=111 %}"
# 写法2
<a href="{% url 'aaa' 111 %}" # 跟无名分组写法相同,便于记忆
# 后端
# 写法1
reverse('ooo',kwargs={'named':111})
# 写法2
reverse('ooo',args=(111,)) # 跟无名分组写法相同,便于记忆
路由分发
django的每一个应用都可以有自己的templates文件夹、urls.py文件夹、static文件夹,这就意味着用django框架写项目的时候,可以将项目拆分成一个个小功能(应用)将各个应用分给各个小组进行开发,达到很好的分组开发的效果。
应用场景:
- 作为组长只需要将手下书写的app全部拷贝到一个新的django项目中 然后在配置文件里面注册所有的app再利用路由分发的特点将所有的app整合起来
- 当一个django项目中的url特别多的时候 总路由urls.py代码非常冗余不好维护
这个时候也可以利用路由分发来减轻总路由的压力
利用路由分发之后 ,总路由就不再需要书写路由与视图函数的直接对应关系了,而是总体做分配工作。
代码实现
首先给每个应用都创建一个路由层urls.py文件和一个视图层view.py文件
总路由:
-
写法1
from django.conf.urls import url,include # 需要导入include from app01 import urls as app01_urls from app02 import urls as app02_urls # 将各应用的路由层文件导入到总路由中,注意需要给文件起别名防止重名。 urlpatterns = [ url(r'^app01/',include(app01_urls)) # 只要url前缀是app01开头 全部交给app01处理 url(r'^app02/',include(app02_urls)) # 只要url前缀是app02开头 全部交给app02处理 ]
-
写法2
无需导入应用的路由文件,这种简易写法推荐使用
# 总路由 from django.conf.urls import url,include urlpatterns = [ url(r'^app01/',include('app01.urls')), url(r'^app02/',include('app02.urls')) ]
注意:总路由里面的url千万不能加$结尾
子路由
# app01 urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^reg/',views.reg)
]
# app02 urls.py
from django.conf.urls import url
from app03 import views
urlpatterns = [
]
名称空间
分组开发,在给视图函数起别名的时候难免会用到相同的名字,导致重名,如何才能避免这种重名的现象
防止重名的方法一:
总路由中给app指定名称空间
# 当多个应用出现了相同的别名 我们研究反向解析会不会自动识别应用前缀
"""
正常情况下的反向解析是没有办法自动识别前缀的
"""
# 名称空间
# 总路由
url(r'^app01/',include('app01.urls',namespace='app01')),
url(r'^app02/',include('app02.urls',namespace='app02'))
# 解析的时候
# app01
urlpatterns = [
url(r'^reg/',views.reg,name='reg')
]
# app02
urlpatterns = [
url(r'^reg/',views.reg,name='reg')
]
# 前端
{% url 'app01:reg' %}
{% url 'app02:reg' %}
# 后端
reverse('app01:reg')
reverse('app02:reg')
其实只要保证名字不冲突 就没有必要使用名称空间,浪费资源
防止重名的方法二:
有多个app我们在起别名的时候会加上app的前缀,这样的话就能够确保多个app之间名字不发生冲突。
# app01
urlpatterns = [
url(r'^reg/',views.reg,name='app01_reg')
]
# app02
urlpatterns = [
url(r'^reg/',views.reg,name='app02_reg')
]
伪静态
将一个动态网页伪装成静态网页,而静态网页是网址写好了不会变化的页面
伪装的目的
增大网站的SEO(Search Engine Optimization)查询力度,并且增加搜索引擎收藏本网上的概率
urlpatterns = [
url(r'^reg.html',views.reg,name='app02_reg')
]
ps:搜索引擎本质就是一个巨大的爬虫程序,其实无论你怎么优化、怎么处理,别人花钱了的还是会优先展示,比如百度的竞价排名。
虚拟环境
通俗的来讲,虚拟环境就是借助虚拟机docker来把一部分内容独立出来,我们把这部分独立出来的东西称作“容器”,在这个容器中,我们可以只安装我们需要的依赖包,各个容器之间互相隔离,互不影响,每次创建一个虚拟环境就类似于重新下载一个纯净的python解释器,但是虚拟环节不要创建太多,它是会消耗硬盘空间的。
虚拟环节的应用场景
linux中,缺什么才装什么,在我们正常的开发中,我们也是秉承这种思想,我们会给每一个项目配备一个独有的解释器环境,该环境内只装项目需要的模块,其他的用不到的模块一概不装,提升加载速度。
扩展
开放当中我们会给每一个项目配备一个requirements.tex
文件,里面书写了该项目所需要的所有模块和对应的版本,只需要直接输入一条命令就可以安装所有项目必需的模块。
django版本区别
(主要是对比1.x与2.x、3.x的区别,2.x和3.x基本相同)
-
1.x路由层使用的是url方法,而在2.x和3.x中路由层使用的是path方法,
url()
第一个参数支持正则,是正则匹配path()
第一个参数不支持正则,写什么就只能匹配到什么。如果是习惯使用url那么在2.x和3.x还提供了另一种方法:
# 2.x和3.x里面的re_path就等价于1.x里面的url from django.urls import path,re_path re_path(r'^index/',index), # 还路由导入url,直接使用url方法,但是2.x和3.x中不推荐这样使用 from django.conf.urls import url url(r'^login/',login)
就目前来看path方法好像没有url方法好用,其实不然,path方法内部支持五种转换器。
-
2.x和3.xpath方法内部的五种转换器
- int,匹配正整数,包含0。
path('index/<int:id>/',index) # 将括号内第二个路由的内容转成整型然后再以关键字实参的形式传递给后面的视图函数。 # 浏览器请求: "http://127.0.0.1:8000/index/111/" # 后端: def index (request,id): print(id,type(id)) # 111 <class 'int'>
- str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
- slug,匹配字母、数字以及横杠、下划线组成的字符串。
- uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
- path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
-
自定义转换器
from app01.path_converts import MonthConverter class MonthConverter: regex='\d{2}' # 属性名必须为regex def to_python(self, value): return int(value) def to_url(self, value): return value # 匹配的regex是两个数字,返回的结果也必须是两个数字 # 使用前需要先注册转换器 from django.urls import path,register_converter from app01.path_converts import MonthConverter register_converter(MonthConverter,'mon') urlpatterns = [ path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'), ]
-
模型层里面1.x外键默认都是级联更新删除的,但是到了2.x和3.x中需要你自己手动配置参数
models.ForeignKey(to='Publish',on_delete=models.CASCADE...)
视图层
三板斧
-
HttpResponse
:返回字符串 -
render
:返回html页面,并且在返回给浏览器之前可以给html文件传值 -
redirect
:重定向
视图函数必须返回一个HttpRespnse对象,查看三板斧的源码即可得出结论,都继承了HttpRespnse类,视图函数什么都不返回的时候回报以下错误:
"""
The view app01.views.index didn't return an HttpResponse object. It returned None instead."""
render简单内部原理
def index(request):
from django.template import Template,Context
res = Template('<h1>{{ user }}</h1>')
con = Context({'user':{'username':'jason','password':111}})
ret = res.render(con)
print(ret)
return HttpResponse(ret)
JsonResponse对象
json格式的数据有什么用?
前后端数据交互需要使用到json作为过渡 实现跨语言传输数据
"""
前端序列化与反序列化:
JSON.stringify() json.dumps()
JSON.parse() json.loads()
""""""
用一个需求引出JsonResponse对象
需求一:在后端给html页面传一个json格式的字典
-
基于json模块和三板斧之一HttpResponse来实现
# urls.py url(r'^json_str/', views.json_str), # views.py def json_str(request): import json user_dict = {'username':'程咬金','password':111} # 先将字段转成json格式的字符串 json_dict = json.dumps(user_dict) # 将json格式的字符串返回 return HttpResponse(json_dict) # 浏览器请求: """http://127.0.0.1:8000/json_str/""" # 页面显示: """ {"username": "\u7a0b\u54ac\u91d1", "password": 111} 通过字典key的双引号我们可以确定html页面确实收到的是json格式的字典, 但是用户名乱码了,是因为dumps的默认转码的原因:ensure_ascii=True,需将其设为False""" json_dict = json.dumps(user_dict,ensure_ascii=False) """{"username": "程咬金", "password": 111}"""
-
用JsonResponse实现
def json_str(request): from django.http import JsonResponse """ 读源码掌握用法,JsonResponse内部也是使用的json, data = json.dumps(data, cls=encoder, **json_dumps_params), 间接将ensure_ascii=False""" return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False}) # 序列化后直接传给页面 # 页面显示 """{"username": "程咬金", "password": 111}"""
需求二:用JsonResponse给页面传一个json格式的列表
def json_str(request):
from django.http import JsonResponse
lll = [111,222,333,444]
return JsonResponse(lll)
# 浏览器请求数据保错
"""
In order to allow non-dict objects to be serialized set the safe parameter to False.
通过读报错信息我们知道需要将sfae参数设为False"""
return JsonResponse(lll,safe=False)
# 页面展示
"""[111, 222, 333, 444]"""
form表单上传文件前端及后端如何操作
form表单上传文件注意事项:
- method必须指定成post
- enctype必须换成formdata
- 获取数据的input标签必须有name
前端页面
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit">
</form>
后端
# urls.py
url(r'^get_file/', views.get_file),
# views.py
def get_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.chunks(): # 推荐加上chunks方法 其实跟不加是一样的都是一行行的读取
f.write(line)
return render(request,'get_file.html')
request对象方法
"""
request.method
request.POST
request.GET
request.FILES
request.body # 原生的浏览器发过来的二进制数据
request.path
request.path_info
request.get_full_path() 能过获取完整的url及问号后面的参数
"""
print(request.path) # /app01/ab_file/
print(request.path_info) # /app01/ab_file/
print(request.get_full_path()) # /app01/ab_file/?username=jason
FBV与CBV
视图函数既可以是函数也可以是类
FBV(function base views)
就是在视图里使用函数处理请求。
def index(request):
return HttpResponse('index')
CBV(class base views)
就是在视图里使用类处理请求。
# CBV路由
url(r'^login/',views.MyLogin.as_view()) # 前一个参数跟FBV书写相同,后一个参数是类.as_view()
# views.py
from django.views import View
class MyLoign(View):
def get(self,request):
...
def post(self,request):
...
CBV特点
能够直接根据请求方式的不同直接匹配到对应的方法执行
ps:FBV和CBV各自有自己的应用场景