首页 > 技术文章 > django路由层

huangxuanya 2019-06-10 19:52 原文

上传文件

注意的点:

print('path:',request.path)
   print('full_path:',request.get_full_path())

path: /upload_file/
full_path: /upload_file/?name=jason



简单的文件上传
前端需要注意的点:
   1.method需要指定成post
   2.enctype需要改为formdata格式

后端暂时需要注意的是
   1.配置文件中注释掉csrfmiddleware中间件
   2.通过request.FILES获取用户上传的post文件数据
   
file_obj = request.FILES.get('my_file')
       print(file_obj.name)
       with open(file_obj.name,'wb') as f:
           for line in file_obj.chunks():
               f.write(line)
先在day56项目文件的urls里进行测试

from django.contrib import admin
from django.urls import path,re_path
from django.conf.urls import url,include
from django.shortcuts import HttpResponse,render
import json
from django.http import JsonResponse
from app01 import views



def upload_file(request):
    if request.method == 'POST':
        print(request.FILES)  #通过request.FILES获取用户上传的post文件数据,
        #<MultiValueDict:{'my_file':[<InMemoryUploadedFile: 01 笔记.txt (text/plain)>]}>
        #即使value看起来是个数组,实际上就是文件对象
        print('path',request.path)  #只是获取到路径的后缀path: /upload_file/
        print('full_path',request.get_full_path()) #获取到路径的后缀并且携带着参数full_path: /upload_file/  这里没有携带参数所有没有后缀
        file_obj = request.FILES.get('my_file') #file_obj内存文件对象,可以通过点的方式获取属性(01 笔记.txt 虽然跟file_obj.name显示一样,不过类型不一样 file_obj 是InMemoryUploadedFile)
        print(file_obj.name)   #01 笔记.txt 字符串类型
        with open(file_obj.name,'wb')as f:
            for line in file_obj.chunks(): #file_obj.chunks() generator对象,相当于read()
                f.write(line)
        return HttpResponse('OK')
    return render(request, 'index.html')


urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'upload_file/',upload_file),
    ]

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">
    <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<h1>index</h1>
<form action="" method="post" enctype="multipart/form-data">
    <input type="file" name="my_file">
    <input type="submit">
</form>
</body>
</html>

Jason模块的使用

import json
print(json.dumps({'name':'jason大帅比','password':'123'},ensure_ascii=False)) #,ensure_ascii=False不转换成二进制,可以显示中文在屏幕上

在url种做测试
import json
from django.http import JsonResponse
def index(request):
    # res = {'name':'Jason大帅比','password':18}
    # return HttpResponse(json.dumps(res))  会显示二进制,不能显示中文
    return JsonResponse({'name':'Jason大帅比','password':'1888888'},json_dumps_params={'ensure_ascii':False})  加了ensure_ascii':False,可以返回有中文的字典

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'upload_file/',upload_file),
    re_path(r'^index/',index),
    ]

FBV(function base view) 与CBV(class base view)

测试
class Demo(object):
    def __init__(self):
        pass

    def func(self):
        pass

    @classmethod
    def bar(cls):
        pass

    @staticmethod
    def index():
        pass

    @property
    def name(self):
        return 'hahah'
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')

app01下创建一个urls.py

form django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'mycls', views.MyCls.as_view())
]

app01下的views

from django.shortcuts import render, HttpResponse

from django.views import View


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

day56项目文件中总的路由

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/',include('app01.urls')),  #路由分发
    url(r'^app02/',include('app02.urls')),  #路由分发
    ]

浏览器访问的时候要127.0.0.1:8000/app01/mycls #加前缀app01

as_views推理过程(**********)

urlpatterns = [
    url(r'mycls', views.MyCls.as_view()) 
]

views.MyCls.as_view(),不可能是绑定的给对象的方法,要么是类方法,要么就是staticmathod,
在类点一个属性和方法的时候,查找顺序是先从自身去找,找不到再到父类中去找MyCls(View),去View中去找 

urlpatterns = [
    url(r'mycls', views.MyCls.as_view()) #as_view() 函数名加括号,所以写完函数就立马执行,就会得到返回值view
]
可以写成
urlpatterns = [
    url(r'mycls', views.MyCls.view) #相当于浏览器访问我mycls时,我就调用我这个view函数,views函数就会执行
]

 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] 八种请求方式

虚拟环境

不同的项目配置不同的python解释器
django1.0与django2.0之间的区别
django2.0里面的path第一个参数不支持正则,你写什么就匹配,100%精准匹配
django2.0里面的re_path对应着django1.0里面的url
虽然django2.0里面的path不支持正则表达式,但是它提供五个默认的转换器

      str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
      int,匹配正整数,包含0。
      slug,匹配字母、数字以及横杠、下划线组成的字符串。
      uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
      path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)

   自定义转换器
      1.正则表达式
      2.类
      3.注册

   # 自定义转换器
   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')
   PS:路由匹配到的数据默认都是字符串形式

伪静态

伪静态网页
搜索优化seo
2.0版本
 path('index.html',views.index,name='app01_index'),
 1.11版本
 url(r'index.html',views.index,name='app01_index')

路由分发

路由分发
django每一个app下面都可以有自己的urls.py 路由层,templates文件夹,static 文件夹
总路由项目名下的url.py(总路由)不再做路由与视图函数的匹配关系而是做路由的分发
    注意路由分发总路由千万不要$结尾
    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下的文件夹名字一样
# from app01 import urls as app01_urls
# from app02 import urls as app02_urls
# 起别名的方式避免冲突
# url(r'^app01/',include(app01_urls)),
# url(r'^app02/',include(app02_urls)) ,



 #名称空间 第二种方式:(了解)
#路由分发  注意路由分发走路也前往不要$结尾
# path('app01/',include(app01_urls,namespace=app01)),
# path('app02/',include(app02_urls,namespace=app02)),
app01.urls.py
		from django.conf.urls import url
		from app01 import views
		urlpatterns = [
			url(r'^index/',views.index,name='index')
		]

	app02.urls.py
		from django.conf.urls import url
		from app02 import views
		urlpatterns = [
			url(r'^index/',views.index,name='index')
		]

	app01.views.py
	reverse('app01:index')

	app02.views.py
	reverse('app02:index')

#第三种方式:
# path('app01/',include(app01.urls)),
# path('app02/',include(app02.urls))

反向解析

反向解析(根据名字获动态获取对应的路径) 本质:就是获取到访问名字所对应的视图函数
from django.shortcuts import reverse

url(r '^index6668888/$',views.index,name='index')
可以给每一个路由与视图函数对应关系起一个名字
这个名字能够唯一标识出对应的路径
注意这个名字不能重复是唯一的

前端使用
<a href="{% url 'index' %}"></a>
<a href="{% url '你给路由与视图函数对应关系起的别名' %}"></a>

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

无名分组反向解析
   url(r'^test/(\d+)/',views.test,name='list')

后端使用
def index(request):
    print(reverse('list',args=(10,)))  加逗号,才是一个元组,这里的数字通常都是数据的主键值
    return render(request,'index.html')
前端使用
<a href="{% url 'list' 10 %}"></a>

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 %}
视图函数
      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 %}
   反向解析的本质:就是获取到一个能够访问名字所对应的视图函数

路由层

注意:第一个参数是正则表达式,匹配规则是按照从上往下依次匹配,匹配到一个之后立即匹配,直接走视图函数,不再往下个路由匹配

网站首页路由

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

无名分组

无名分组(将加括号的正则表达式匹配到的内容当做位置参数自动传递给对应的视图函数)
   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+)/(?P<year>\d+)/',views.test)


但是支持用一类型多个形式匹配
无名分组多个
   url(r'^test/(\d+)/(\d+)/',views.test),
   有名分组多个
   url(r'^test/(?P<year>\d+)/(?P<xxx>\d+)/',views.test),
# 第一种方式
#起别名,当两个app下的文件夹名字一样
# from app01 import urls as app01_urls
# from app02 import urls as app02_urls
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^$',views.home),      #第一个参数时正则,按照正则的去匹配   如果是path('test',views.test),path('testadd',views.testadd)   会乱
    # path('^test/$', views.test),   #从上往下依次匹配,并且匹配到一个之后就会立即结束,不再继续往下匹配   127.0.0.1:8000/fdstest/loginddddddd也能匹配上
    # path('^test/[0-9][4]/',views.test),
    # path('^test/\d+/',views.test), #匹配一个或多个数字
    # path('test/(\d+)/',views.test), #变成传个两个参数 ,视图函数需要接受两个参数,不然会报错
    # path('test/(?P<year>\d+)/', views.test),

    #有名与无名结合使用
    # path('test/(\d+)/(?P<year>\d+)/',views.test),  #不能结合使用
    #无名分组多个
    # path('test/(\d+)/(\d+)/', views.test),
    #有名分组多个
    re_path('test/(?P<year>\d+)/(?P<xxx>\d+)',views.test),
    re_path('^testadd/$',views.testadd),
    # path('^index666/$',views.index,name='index'),
    # path('^index6668888/$',views.index,name='index'), #无论你的路径怎么变,根据你的别名来跳转,
    # 千个视图函数只需要写index跳转等 name顺便取,一个名字对应一个门牌号
    # path('^index1/$',views.testadd),

    #test/1/  test/2/  解释的是字符串,数字要有参数接受
    # path('test/(\d+)/', views.test,name='list'),

    #test/1/  test/123
    re_path('test/(?P<year>\d+)/', views.test,name='list'),
    re_path('^index/$',views.index,name='index'),
    re_path('',views.error),
    re_path(r'upload_file/',upload_file)
    #名称空间 第二种方式:
    #路由分发  注意路由分发走路也前往不要$结尾
    # path('app01/',include(app01_urls,namespace=app01)),
    # path('app02',include(app02_urls,namespace=app02)),
    #第三种方式:
    # path('app01/',include(app01.urls)),
    # path('app02',include(app02.urls))
    

小总结:
路由层
无名分组
有名分组
反向解析
路由分发
名称空间
伪静态概念
虚拟环境
django1.0与django2.0区别

视图层
	FBV与CBV
	JsonResponse
	简单的文件上传
	request对象的常用方法

	url('正则表达式','视图函数内存地址')
	
	url(r'^index.html',views.index)
	url(r'^index/\d+.html',views.index)
	
	url(r'^index/[0-9]{4}/')

无名分组:将括号内正则表达式匹配到的内容当作位置参数自动传递给对应视图函数
	url(r'^index/([0-9]{4})/')

有名分组:将括号内正则表达式匹配到的内容当作关键字参数自动传递给对应视图函数
	url(r'^index/(?P<year>\d+)')

	注意:
		1、无名分组有名分组不支持混合使用
			url(r'^index/(\d+)/(?P<year>\d+)',views.index)
		2、虽然不能混合使用,但是可以重复使用相同类型的多个

反向解析:根据名字动态获取改名字所对应的能够匹配上url的一个结果
	url(r'^index/',views.index,name='index')
	前端
		{% url 'index' %}
	后端
		from django.shortcuts import reverse
		url = reverse('index')
	
	无名分组的反向解析
	url(r'^index/(\d+)/',views.index,name='list')
	# index/1/   index/13213/   index/12/
	前端
		{% url 'index' user_obj.pk %}
	后端
		from django.shortcuts import reverse
		url = reverse('index',args=(1,))
	
	有名分组的方向解析
	url(r'^index/(?P<xxx>\d+)/',views.index,name='edit')
	前端
		{% url 'index' user_obj.pk %}
		{% url 'index' xxx=user_obj.pk %}
	后端
		from django.shortcuts import reverse
		url = reverse('index',args=(1,))
		url = reverse('index',kwargs={'xxx':1})
		
路由分发:项目总路由不再干路由与视图函数的匹配的工作,而是做一个中转
	ps:django里面每一个app都可以有自己的urls.py,static静态文件夹,templates模板文件夹
	from  django.urls import include
	
	# 路由分发的时候千万不要加$
	url('^app01/',include('app01.urls'))
	
名称空间:当多个app中给路由与视图函数起别名重名的情况,可以考虑使用名称空间避免反向解析不准的情况
	# 在总路由中 给每一个分发的任务中添加namespace
	url('^app01/',include('app01.urls',namespace='app01'))
	
	from django.shortcuts import reverse
	url = reverse('app01:list')
	
	ps:名称空间这个对象完全没必要使用,你可以在起别名的时候加上一些具有唯一标识的前缀(比如你的应用名)
	
伪静态网页:
	url(r'^index.html',views.index)
	url(r'^index/\d+.html',views.index)

虚拟环境:每一个项目都有属于自己的python环境,避免导入多余的模块造成资源浪费

django版本对比
	django2.0与django1.0
	path:第一个参数不支持正则表达式,写什么就匹配什么,100%精准匹配
	re_path:就是django1.0里面的url

	虽然path不支持正则表达式,但是它提供给你五个自定义转换器
	int
	str
	uuid
	...
	
	自定义转换器:
		1.正则表达式
		2.写两个方法一个叫to_python,一个叫to_url
		3.注册(起一个名字 比如叫xxx)
	url(r'^index/<str:id>',views.index)
	url(r'^index/<xxx:id>',views.index)		

FBV与CBV
	
	CBV
	视图层代码
	from django.views import View
	from django.shortcuts import HttpResponse
	class MyLogin(View):
		def get(self,request):
			return HttpResponse('get')
		
		def post(self,request):
			return HttpResponse('post')
	
	路由层代码
	from app01 import views
	url(r'^login/',views.MyLogin.as_view())
	# url(r'^login/',views.view)
	
	源码部分自己截图写注释保存到手机 
	
JsonResponse:返回json的字符串
	import json
	json.dumps(res,ensure_ascii=False)
	
	JsonResponse(res,json_dumps_params={"ensure_ascii":False})

简单的文件上传
	前端:
		1.method由默认的get改为post
		2.enctype由默认的urlencoded改为formdata
	后端:
		1.暂时先去settings.py中将csrfmiddleware中间件注释掉
		2.从request.FILES中获取文件数据


request.method
request.GET
request.POST
request.FILES
request.path
request.get_full_path()
request.body

推荐阅读