Django 路由与视图

2022-07-08  本文已影响0人  李霖弢

路由配置

urls.py中,通过pathre_pathurl配置路由
其中kwargs入参可传入额外信息,并在视图中被**kwargs捕获

from django.urls import include, path, re_path
path(route, view, kwargs=None, name=None, Pattern=None):
固定路由

FBV直接引入即可
CBV需要调用as_view方法(继承自View类)
此外还支持include另一个路由文件作为二级路由

from django.urls import include, path
import blog.views

urlpatterns = [
    path('', blog.views.index), # FBV
    path('restful/', blog.views.RestfulView.as_view()), # CBV
    path('blog/', include('blog.urls')),  # 作为blog下的二级路由
]
动态路由

使用<type:name>形式匹配参数,其中type可以为 int、str、path 等,也可以通过register_converter注册自定义类型

# urls.py
urlpatterns = [
    path('blog/<int:id>/', views.db_get),
    path('blog/<int:id>/<str:name>/', views.month_archive)
]

# views.py
def db_get(request, id):
    vv = Blog.objects.get(id=id)
    return JsonResponse(model_to_dict(vv), json_dumps_params={'ensure_ascii': False})
正则表达式路由

通过re_path方法,用(?P<name>pattern)进行正则匹配
也可以用老版本中的url语法,等同于re_path

from django.urls import re_path
from django.conf.urls import url
from . import views
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}),
    re_path('articles/(?P<year>[0-9]{4})/(?P<month>0[1-9]|1[0-2])/', views.month_archive)
]

视图函数 function base views

比较简单,需自行区分请求的method并做出响应

@csrf_exempt
def hello_world(request, *args, **kwargs):
    if request.method == 'GET':
        return HttpResponse('Hello, get request', content_type="text/plain")
    elif request.method == 'POST':
        return HttpResponse('Hello, post request', content_type="text/plain")
    return HttpResponse("Hello, world.", content_type="text/plain")

# urls.py    
urlpatterns = [
    path('hello/', hello_world),
]

视图类 class base views

推荐,符合 restful 规范, 且支持继承
视图类可继承View类自行实现逻辑,也可以继承其他类以简化,称为 mixins:

from django.http import HttpResponse
from django.views import View

class HelloView(View):
    
    def get(self, request, *args, **kwargs):  
        return HttpResponse('getn')
      
    def post(self, request, *args, **kwargs):  
        return HttpResponse('postn')
    
    def put(self, request, *args, **kwargs):  
        return HttpResponse('putn')
    
    def delete(self, request, *args, **kwargs):  
        return HttpResponse('deleten')
    
    # 请求会先进入 dispatch 方法找到对应函数,然后再执行
    # @csrf_exempt
    # def dispatch(self, request, *args, **kwargs):
        # return super(HelloView, self).dispatch(request, *args, **kwargs)

# urls.py    
urlpatterns = [
    path('hello/', HelloView.as_view()),
]
method_decorator

来自django.utils.decorators模块的类装饰器,用于将函数装饰器应用到视图类中的方法
name 属性用于指定添加装饰器的方法名,若全都需要则添加给dispatch

# 定义函数装饰器
def wrapper(f):
    def innser(*args, **kwargs):
        print('before')
        ret = f(*args, **kwargs)
        print('after')
        return ret
    return innser

@method_decorator(wrapper, name='get')
class HelloView(View):
    ...

视图方法入参

接受 request 参数(继承自 HttpRequest 的子类 WSGIRequest ),用于存放浏览器传递过来的所有数据

HttpRequest

来自django.http模块,常用属性如下:

request.META.get('HTTP_APP_CLIENT', None)
# ?a=1&a=2
request.GET.get("a") # 2
request.GET.getlist("a") # [1, 2]
import json
request.body # b'{"a":100}'
request.body.decode() # '{"a":100}'
json.loads(request.body.decode())  # {"a":100}
WSGIRequest

视图方法返回值

HttpResponse

来自django.http模块,用于直接返回字符串

return HttpResponse("created VV3 with no hair")
JsonResponse

来自django.http模块,用于直接返回JSON
注意配置json_dumps_params以支持中文

data = {'code': 0, "content": "返回中文字符串", "err_msg": ""}
return JsonResponse(data, json_dumps_params={'ensure_ascii': False})
render

来自django.shortcuts模块,从当前app下的templates目录逐级向上查找并返回HTML 或者模板文件
模板引擎可在settings.pyTEMPLATES下配置,默认引擎为Django Template Language (DTL),也可以修改为 Mako 或 Jinja2
需配合settings.pyTEMPLATES配置使用

def db_retrieve(request):
    arr = []
    vvs = Blog.objects.all()
    for vv in vvs:
        arr.append(model_to_dict(vv))
    content = json.dumps(arr)
    return render(request, "index.html", {"title":"首页", "content": content })

# template/index.html
<h1>{{ title }}</h1>
<script> console.log("{{ content | safe }}"); </script>
redirect

来自django.shortcuts模块,用于重定向

return redirect("http://www.baidu.com")

数据校验

Django本身支持通过form模块实现数据校验,但仅适用于后端渲染,并不方便

from django import forms
class LoginForm(forms.Form):
    name = forms.CharField(
        label="账号",
        min_length=4,
        required=True,
        error_messages={'required': '账号不能为空', "min_length": "账号名最短4位"},
        widget=forms.TextInput(attrs={'class': "input-text",
                                      'placeholder': '请输入登录账号'})
    )

静态目录

通常通过STATICFILES_DIRS指定通用目录,且每个app拥有自己的static目录(不强求,因访问时并不会指定从哪个app下获得)
不同目录下的静态文件需注意不可 同子目录且同名,其本质上最终都在一个空间内

# settings.py
STATIC_URL = '/static/'  # 用于指定静态目录对应的网址URL映射,默认值'/static/'
STATICFILES_DIRS = ( # 各个app通用的静态文件目录
    os.path.join(BASE_DIR, 'common_static'),
)
# 执行 python manage.py collectstatic 后所有app下的static目录,和STATICFILES_DIRS下的静态文件都会打包到此目录
STATIC_ROOT = os.path.join(BASE_DIR, "static")

# urls.py
from django.urls import re_path
from django.conf import settings
from django.views.static import serve

urlpatterns = [
    re_path(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT})
]

模板文件中可通过static标签加载静态资源,如settings.py中已配置'builtins':['django.templatetags.static']则可无需通过load引入static

 {% load static %}
  <link rel="stylesheet" href="{% static 'style.css' %}">

文件上传

通过request.FILES获取上传的文件

自行处理
# urls.py
path('file_upload/', views.file_upload)

# views.py
class FileUploadForm(forms.Form):
    file = forms.FileField(label="文件上传")

def handle_uploaded_file(f):
    save_path = os.path.join(settings.MEDIA_ROOT, f.name)
    with open(save_path, 'wb+') as fp:
        for chunk in f.chunks():
            fp.write(chunk)

@csrf_exempt
def file_upload(request, *args, **kwargs):
    error_msg = ""
    if request.method == 'POST':
        forms = FileUploadForm(request.POST, request.FILES)
        if forms.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return HttpResponse('上传成功')
        error_msg = "异常"
    else:
        forms = FileUploadForm()
    return render(request, 'file_upload.html', {'forms': forms, "error_msg": error_msg})

# file_upload.html
<form method="post" action="/blog/file_upload/" enctype="multipart/form-data">
    {% csrf_token %}
    {{ forms }}<br>
    <input type="submit" value="提交">
</form>
通过ORM自动处理

在模型中定义models.FileField类型字段,该字段在数据库里只是个字符串,但在view中,其不但可以接收普通的字符串,还可以接收文件实例,此时其会自动将该文件存入MEDIA_ROOT / upload_to路径

# settings.py
# 需配合 django.template.context_processors.media 使用,支持在模板中通过`{{MEDIA_URL}}`引用该路径
MEDIA_URL = '/media/'
# 配合 models.FileField 上传文件
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# models.py
def upload_to(instance, filename):
    return "/".join([MEDIA_ROOT, "upload", filename])

class Blog(models.Model):
    ...
    # upload_file = models.FileField(upload_to="upload/") #指向 MEDIA_ROOT/upload
    # upload_file = models.FileField(upload_to="upload/%Y/%m/%d")
    upload_file = models.FileField(upload_to=upload_to, default="")

# urls.py
path('file_upload2/', views.file_upload2)

# views.py
def file_upload2(request, *args, **kwargs):
    if request.method == 'POST':
        upload_file = request.FILES['file']
        # upload_files = request.FILES.getlist('files') # 如有多个文件
        Blog.objects.create(name=upload_file.name, age=18, has_hair=False,
                            upload_file=upload_file)
        return HttpResponse('上传成功')
    return render(request, 'file_upload2.html', {})

# file_upload2.html
<form method="post" action="/blog/file_upload2/" enctype="multipart/form-data"> 
    {% csrf_token %}
    <label>选择上传文件:</label><input type="file" name="file">
    <div><input type="submit" value="提交" style="margin-top:10px"></div>
</form>

上一篇 下一篇

猜你喜欢

热点阅读