Python学习笔记Day19 - Django进阶
目录
路由系统URL
a. url路由(旧版)
-
单个参数(\d+)
url(r'^detail-(\d+).html', views.detail)
-
多个参数按顺序赋给形参
url(r'^detail-(\d+)-(\d+).html', views.detail) def func(request, *args, **kwargs): args = (2,9)
-
多个参数指定形参 (最常用)
url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail) def func(request, *args, **kwargs): kwargs = {'nid': 1, 'uid': 3}
新版使用path代替
a. path路由
-
path参数
path('detail/<int:p>/', views.detail), path('articles/2003/', views.special_case_2003), path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), def func(request, *args, **kwargs): kwargs = {'year': 1, 'month': 3} # 参数 int -> 匹配0和正整数 str -> 匹配任何非空字符串但不包括/,不写默认匹配str slug -> 可理解为注释,匹配任何ascii码,包括连字符-和下划线_ uuid -> 匹配一个uuid对象(该对象必须包括破折号—,所有字母必须小写) path -> 匹配所有的字符串 包括/(意思就是path前边和后边的所有) path里不再需要用r,^,$符号 默认采用截止符方式,需要后加参数用?
-
re_path()
(?P<var>...)
...中直接写re规则re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail),
两种嵌套参数
re_path(r'^blog/(page-(\d+)/)?$', blog_articles), re_path(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments), # 推荐 # (?:...)为非捕获参数
b. name命名
对URL进行命名,方便更改、调用和reverse (以后可以根据此名称生成自己想要的URL)
-
url里指定name
path('asdfasdfasdf/', views.index, name='i1'), path('yug/(\d+)/(\d+)/', views.index, name='i2'), path('buy/<int:pid>/<int:nid>/', views.index, name='i3'),
-
html里通过name生成url跳转
./xxx.html action="{% url "i1" %}" # asdfasdfasdf/ action="{% url "i2" 1 2 %}" # yug/1/2/ action="{% url "i3" pid=1 nid=9 %}" # buy/1/9/ {{request.path_info}} # 跳转到当前的URL
-
反转name生成url
./views.py def func(request, *args, **kwargs): from django.urls import reverse url1 = reverse('i1') # asdfasdfasdf/ url2 = reverse('i2', args=(1,2,)) # yug/1/2/ url3 = reverse('i3', kwargs={'pid': 1, "nid": 9}) # buy/1/9/
c. 多级路由分发
-
分发至app
./project/urls.py from django.conf.urls import url,include urlpatterns = [ path('cmdb/', include("app01.urls")), path('monitor/', include("app02.urls")), ] ./app01/urls.py from app01 import views urlpatterns = [ path('login/', views.login), ] ./app02/urls.py from app02 import views urlpatterns = [ path('login/', views.login), ] # 被包含的URLconf 会收到来自父URLconf 捕获的任何参数 path('<username>/blog/', include('foo.urls.blog')),
-
同名前缀分组
from apps.main import views as main_views from credit import views as credit_views extra_patterns = [ path('reports/', credit_views.report), path('reports/<int:id>/', credit_views.report), path('charge/', credit_views.charge), ] urlpatterns = [ path('', main_views.homepage), path('help/', include('apps.help.urls')), path('credit/', include(extra_patterns)), ]
d. 命名空间
./project/urls.py
path('admin/', include('app01.urls',namespace='m1'))
path('crm/', include('app02.urls',namespace='m2'))
./app01.urls
app_name = 'n1'
./views.py
reverse('m1:n1', kwargs={'key': 'value'})
# 命名空间可以组合嵌套
e. 默认值
urlpatterns = [
path('blog/', views.page), # 不指定num参数时,默认num=1
path('blog/page<int:num>/', views.page),
]
def page(request, num=1):
...
f. 添加额外参数给views (钩子)
path('index/', views.index, {'name': 'alex'}), # 传入额外值name = 'alex'
def index(request,name):
print(name)
return HttpResponse('OK')
path('blog/', include('inner'), {'blog_id': 3}), # 为include的所有url传递额外值
模板文件分路由配置
`'DIRS': [os.path.join(BASE_DIR, 'templates')]` 是指到 BASE_DIR/templates文件夹中去取模板
`'DIRS': [os.path.join(BASE_DIR, 'app1/templates')]` 是指导 BASE_DIR/app1/templates文件夹中去取模板
一般来说,应该设置'DIRS': [os.path.join(BASE_DIR, 'templates')]
,公用的templates需要指定。
app1专用的templates,放在app1/templates下,可以不需指定。
因为在app1.views中若要指定一个专用模板,只要直接写‘app1_index.html’,Django服务器会在views文件所在的当前层(/app1)中找到templates,从而找到模板'app1_index.html'.
指定公用的templates路径,所有apps都可以调用,方便快捷。
app专用的templates不需要指定,这样当要复用这个app的时候,不需要考虑templates路径问题。
视图views.py
a. 获取用户请求数据
request.GET
request.POST
request.FILES
request.path_info # 当前URL
request.XXX
- request.body # 用户数据放在请求body里打包发送
request.GET.get()
request.POST
request.FILES
- request.Meta(...)
request.method
request.path_info
request.COOKIES
request....
- request.method(PUT,DELETE)
request.GET/POST会将请求body打包,其他不会
取body用request.body
- 请求的其他信息
from django.core.handlers.wsgi import WSGIRequest
request.environ
request.environ['HTTP_USER_AGENT']
b. checkbox等多选的内容
request.POST.getlist()
c. 上传文件
上传文件时,form标签要做特殊设置
<form ... enctype="multipart/form-data">
request.POST.get('1.jpg') # 获取上传文件的文件名
request.FILES.get('1.jpg') # 获取上传的文件对象obj
obj -> 默认显示name,(内部__repr__函数)
obj.name
obj.size
obj.read() -> 读取整个上传文件的数据,文件较大时慎用。
obj.chunks() -> 一点一点获取数据块
obj.multiple_chunks(chunk_size=None) -> 判断文件是否足够大,默认2.5M,大于返回True
# 可用来判断使用read还是chunks
obj.content_type -> 上传文件时的content_type报头,例如(e.g. text/plain or application/pdf).
obj.charset -> 编码
- 获取文件:
f = open(obj.name,'wb')
for i in obj.chunks():
f.write(i)
f.close()
d. FBV & CBV 两种函数定义方式
-
FBV: function base view 一般函数
path('index/', views.index), ./view.py def index(request): ...
-
CBV: class base view 类里的函数
path('home/', views.Home.as_view()), # 类名.as_view() ./view.py from django.views import View class Home(View): # 继承View def get(self,request) # 请求GET时执行 def post(self,request) # 请求POST时执行 # 其他函数 def dispatch(self,request): # View父类里负责判断method的函数,自动继承,可重定义 def as_view(self,request): # 从url转到类中首先执行的方法,默认
两者没有明显优劣,建议:两者都用
e. 装饰器
def auth(func):
def inner(reqeust,*args,**kwargs):
v = reqeust.COOKIES.get('username111') # 验证登录状态
if not v:
return redirect('/login/')
return func(reqeust, *args,**kwargs)
return inner
-
FBV装饰器:
@auth def index(request): ...
-
CBV装饰器:
from django import views from django.utils.decorators import method_decorator @method_decorator(auth,name='dispatch') # 装饰所有请求装饰,dispatch,与在dispatch函数前加装饰效果一致 class Order(views.View): # @method_decorator(auth) # 仅装饰get def get(self,reqeust): v = reqeust.COOKIES.get('username111') return render(reqeust,'index.html',{'current_user': v}) def post(self,reqeust): v = reqeust.COOKIES.get('username111') return render(reqeust,'index.html',{'current_user': v})
模板html
a.母版
访问子板时引用母版,并替换母版的同名block块,然后渲染
-
母板:
{% block title %} {% endblock %} # 可放置在任意位置
-
子板:
{% extends "base.html" %} # 开头声明母版路径 {% block title %} ... {% endblock %}
写自己的css和js时,也要封装成分别的css和js的block块
b.小模块导入
将重复的标签块封装成小模块再导入
{% include 'tag.html' %} # 可重复导入
c.模板函数 |
{{ item.event_start|date:"Y-m-d H:i:s"}}
{{ bio|truncatewords:"30" }} # 变量前30个字符,用于中文不行
{{ content |slice:"30" }} # 变量前30个字符,可用于中文
{{ my_list|first|upper }}
{{ name|lower }}
d.自定义函数
-
simple_tag
<1> app下创建templatetags目录 # 不可改名 <2> 创建任意xxxx.py文件 <3> 在.py文件内创建template对象 register # 不可改名 from django import template register = template.Library() <4> 用simple_tag装饰.py内的函数 @register.simple_tag def func1(a1,a2,a3....) # 可传任意参数 return "asdfasd" <5> 在settings中注册APP <6> 在html顶部引用函数(文件名) {% load xxoo %} <7> 使用函数 {% func1 arg1 arg2 %} # 空格不影响,用{% %} - 缺点: 不能作为if条件 - 优点: 参数任意
-
filter
<1><2><3><5><6>同上 <4> 用filter装饰.py内的函数 @register.filter def func2(a1,a2) # 只能最多两个参数 return "asdfasd" <7> 使用函数 {{ 'arg1'|func2:'arg2' }} # 中间不能加空格,用{{|}} - 缺点: 最多两个参数,不能加空格 要使用多个参数需要将参数传入同一个形参,在函数内部实现分离 - 优点: 能作为if条件 {% if arg1|func2:arg2 %} {% end if %}