Django之路由层及视图层

路由层

  • 只写字符串,不写正则表达式(test,testadd,xxxtestyyy)
  • 精准匹配,首页与尾页匹配(url(r'^$',view.home),url(r'',views.error))
  • 匹配固定数据([0-9]{4}),匹配任意数字(\d+)
urlpatterns = [
			url(r'^admin/', admin.site.urls),
			url(r'^$',views.home),
			url(r'^test/$',views.test),
			url(r'^testadd/$',views.testadd),
			url(r'',views.error)
		]
注意:第一个参数是正则表达式,匹配规则按照从上往下一次匹配,匹配到一个之后立即匹配,直接执行对应的视图函数

网站首页路由
url(r'^$',views.home)
网站不存在页面
url(r'',views.error)

无名分组(\d+),给视图函数传位置参数

有名分组(?P\d+)

无名分组(将加括号的正则表达式匹配到的内容当做位置参数自动传递给对应的视图函数)
	url(r'^test/(\d+)/',views.test),  # 匹配一个或多个数字
	
	def test(request,xxx):
		print(xxx)
		return HttpResponse('test')

有名分组(将加括号的正则表达式匹配到的内容当做关键字参数自动传递给对应的视图函数)
	url(r'^test/(?P<year>\d+)/',views.test),  # 匹配一个或多个数字
	
	def test(request,year):
		print(year)
		return HttpResponse('test')
  • 注意点:无名分组和有名分组不能同时使用
但是支持用一类型多个形式匹配
无名分组多个
	url(r'^test/(\d+)/(\d+)/',views.test),
有名分组多个
	url(r'^test/(?P<year>\d+)/(?P<xxx>\d+)/',views.test)

反向解析(根据名字动态获取到对应路径)

反向解析(根据名字动态获取到对应路径)
	from django.shortcuts import reverse
	
	url(r'^index6668888/$',views.index,name='index')
	# 可以给每一个路由与视图函数对应关系起一个名字
	# 这个名字能够唯一标识出对应的路径
	# 注意这个名字不能重复是唯一的
	
前端使用
	{% url 'index' %}
	{% url '你给路由与视图函数对应关系起的别名' %}

后端使用
	reverse('index')
	reverse('你给路由与视图函数对应关系起的别名')

无名分组反向解析
	url(r'^test/(\d+)/',views.test,name='list')
	
	后端使用
		print(reverse('list',args=(10,)))
	前端使用
		{% url 'list' 10 %}
	
	user_list = models.User.objects.all()
	url(r'^edit/(\d+)/',views.edit,name='edit')
	前端模板语法
		{%for user_obj in user_list%}
			<a href='edit/{{ user_obj.pk }}/'></a>
		{% endfor %}
	
	视图函数
	from django.shortcuts import reverse
	def edit(request,edit_id):
		url = reverse('edit',args=(edit_id,))
		
	模板
		{% url 'edit' edit_id %}

有名分组反向解析
	后端使用
		# 后端有名分组和无名分组都可以用这种形式
		print(reverse('list',args=(10,)))
		# 下面这个了解即可
		print(reverse('list',kwargs={'year':10}))
	前端使用
		# 前端有名分组和无名分组都可以用这种形式
		{% url 'list' 10 %}
		# 下面这个了解即可
		{% url 'list' year=10 %}
总结:针对有名分组与无名分组的反向解析统一采用一种格式即可
	后端
	reverse('list',args=(10,))  # 这里的数字通常都是数据的主键值
	前端
	{% url 'list' 10 %}
反向解析的本质:就是获取到一个能够访问名字所对应的视图函数

路由分发

  • django每一个app下面都可以有自己的urls.py路由层,templates文件夹,static文件夹(文件名可以叫任意名但通常建议urls)

  • include用法初识(分发到单个app下)

  • 多个app情况下,避免冲突不推荐使用的是重命名的方法

  • 项目名下urls.py(总路由)不再做路由与视图函数的匹配关系而是做路由的分发

    from app01 import urls as app01_urls  # 这里避免重名,但不推荐使用
    from app02 import urls	as app02_urls  # 这里避免重名 但不推荐使用
    from django.conf.urls import include
    
    # 路由分发  注意路由分发总路由千万不要$结尾
    url(r'^app01/',include(app01_urls)),
    url(r'^app02/',include(app02_urls))
    
    # 在应用下新建urls.py文件,在该文件内写路由与视图函数的对应关系即可
    from django.conf.urls import url
    from app01 import views
    urlpatterns = [
    	url(r'^index/',views.index)
    ]
    

名称空间

  • 结合反向解析和路由分发,来想一个问题,如果不同app下路由与视图函数取的名字一样,那么在反向解析的时候,它能不能自动识别是哪个app下的呢?不能!

  • 两个app中,取两个相同的名字,分别在对应的视图函数用反向解析打印查看结果

    # app01路由
    url(r'^test/',views.test,name='test')
    # app02路由
    url(r'^test/',views.test,name='test')
    
    # 方向解析的时候并不会自动找到对应的应用拼接上
    # app01中
    _url = reverse('test')  # app01/test/
    # app02中
    _url = reverse('test')  # app01/test/
    
  • 上面这种情况可以提供名称空间的方式解决

    # 这个时候就可以考虑用名称空间的概念
    url(r'^app01/',include('app01.urls',namespace='app01')),  
    url(r'^app02/',include('app02.urls',namespace='app02'))
    
    _url = reverse('app01:test')
    {% url 'app01:test'%}
    
    _url = reverse('app02:test')
    {% url 'app02:test'%}
    
  • 建议:一般不建议使用名称空间,只需要在给路由与视图函数起名字的时候加上所在app名就可以解决(在演示这个的时候,记得把命名空间去掉,不然会报解析值错)

伪静态

  • 先看一眼博客园某篇文章的url发现有后缀.html,百度是个大爬虫,后缀是.html结尾会认为你网站数据一尘不变提高对你这个网站的seo收录概率

    url(r'^index.html',views.index)
    url(r'^test/(\d+.html)',views.index)
    url(r'^demo/(?P<year>\d+.html)')
    url(r'^index.html',views.index,name='app01_index')
    

虚拟环境

  • 虚拟环境创建(先创建一个python项目,选择虚拟项目记得勾选能够被应用于其他项目,下次创建项目时即可选择改虚拟环境作为开发环境)

  • Django2.0版本与1.0区别

    """
    1.0版本的url和2.0版本的re_path分组出来的数据都是字符串类型
    默认有五个转换器,感兴趣的自己可以课下去试一下
    str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
    int,匹配正整数,包含0。
    slug,匹配字母、数字以及横杠、下划线组成的字符串。
    uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
    path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
    
    自定义转换器 需要写三部分	1.正则表达式	2.to_python(类型转换str>>>int)	3.to_url(反向解析)
    # 自定义转换器     
    class FourDigitYearConverter:  
        regex = '[0-9]{4}'  
        def to_python(self, value):  
            return int(value)  
        def to_url(self, value):  
            return '%04d' % value  占四位,不够用0填满,超了则就按超了的位数来!
    register_converter(FourDigitYearConverter, 'yyyy')  
    
    urlpatterns = [  
        path('articles/2003/', views.special_case_2003),  
        path('articles/<yyyy:year>/', views.year_archive),  
        ...  
    ]  
    """
    

视图层

当你的视图函数特别多,并且业务有很多分支的时候,是可以在你的app里面建一个views文件夹,里面更具业务逻辑的不同划分不同的视图函数文件

django三板斧

1.HttpResponse

2.render
# render内部原理
from django.template import Template,Context
def test(request):
  tmp = Template("<h1>{{ user }}</h1>")
  con = Context({'user':'jason'})
  res = tmp.render(con)
  print(res)
  return HttpResponse(res)

3.redirectt

JsonResponse对象

# 前后端真正能够分离的关键
from django.http import JsonResponse
def index(request):
  dic = {'name':'jason','age':18}  # 等价于用json转json.dumps(),再发送!
  # import json
  # return HttpResponse(json.dumps(dic))
  
  # 如果不是字典类型,会报错,看源码需要指定safe=False
  # l1 = [1,2,3]
  # return JsonResponse(l1,safe=False)
  return JsonResponse(dic)

# 编码问题  jason在序列话数据的时候默认以ascill码编码中文数据
# res = {'name':'元少'}
# ret = json.dumps(res)		中文被编码
# ret = json.dumps(res,ensure_ascill=False)
return JsonResponse(dic,json_dumps_params={'ensure_ascill':False})

CBV和FBV

FBV:基于函数的视图
CBV:基于类的视图

CBV:
	url(r'^mycls/',views.MyCls.as_view())

	class MyCls(View):
		def get(self,request):
			return render(request,'index.html')
		def post(self,request):
			return HttpResponse('post')

无论是FBV还是CBV路由层都是路由对应视图函数内存地址
urlpatterns = [
	# url(r'^mycls/',views.view)
	url(r'^mycls/',views.MyCls.as_view())
]

class MyCls(View):
	def get(self,request):
		return render(request,'index.html')
	def post(self,request):
		return HttpResponse('post')

# CBV源码解析

文件上传

# 不指定编码情况下,用debug模式查看request中的POST和FILES数据
# 指定编码,打印文件对象类型
# form表单上传文件的注意事项,enctype编码指定为formdata
def uploadfile(request):
  if request.method == 'POST':
    # print(request.FILES)
    # print(request.FILES.get('myfile'))
    file_obj = request.FILES.get('myfile')
    with open(file_obj.name,'wb') as f:
      for line in file_obj.chunks():
      # 或者直接对文件对象for循环for line in file_obj
        f.write(line)
    return HttpResponse("OK!")
  return render(request,'index.html')

# 注意点前后端数据(不是文件)传输编码格式request.POST中只能获取到formdata和urlencoded两种编码方式其他的不行

# 如果文件名重复不想被覆盖改如何实现

request对象常见方法

request.GET
request.POST
request.method
request.body
request.path
request.get_full_path()
request.META
request.FILES
posted @ 2019-06-10 19:17  fuwei8086  阅读(207)  评论(0)    收藏  举报