跳转至

Views

在 Django 中,视图 views.py 是用于处理用户请求并返回响应的函数或类。

视图的主要任务是接收 HTTP 请求、通过调用模型处理数据、并返回适当的 HTTP 响应,可能是 HTML、重定向、JSON 数据等任何内容。

  • 普通视图(Function-Based Views, FBVs)使用函数来定义,更灵活
  • 通用视图(Class-Based Views, CBVs)使用类来定义,适用于许多常见的 CRUD 操作和标准用例。

实际项目中,通常可以同时使用这两种类型的视图,以满足不同的需求

请求

https://docs.djangoproject.com/zh-hans/5.1/ref/request-response/

当一个页面被请求时,Django 会创建一个 HttpRequest 对象,这个对象包含了请求的元数据,然后 Django 加载相应的视图,将 HttpRequest 作为视图函数的第一个参数,通常名为 request

每个视图都要负责实例化、填充和返回一个 HttpResponse 对象

from django.shortcuts import render  # 引入快捷函数
from .models import Question  # 引入模型

# 视图函数的名称没有要求,可随意
def index(request):
    try:
        xxx = request.POST["key_name"]
    except (KeyError, Choice.DoesNotExist):
        pass
    else:
        pass
    return render(request, 'hello_world.html', context)

HttpRequest

  • request.scheme 请求协议,比如 httphttps
  • request.path 完整路径的字符串,比如 "/music/bands/the_beatles/"
  • request.method 请求方法比如 GETPOST
  • request.content_type 请求头 CONTENT_TYPE 对应的 MIME 类型的字符串
  • request.body 获取非常规表单数据,比如二进制图像,XML 有效负载等
  • request.headers
  • request.META
  • request.COOKIES 一个包含所有 cookies 的字典,键和值是字符串
  • request.GET 类似字典的对象,包含所有给定的 HTTP GET 参数
  • request.POST 类似字典的对象,包含所有给定的 HTTP POST 参数,取到的值永远是字符串,取不到值会报 KeyError
  • request.FILES 类似字典的对象,包含所有上传的文件
  • request.user 代表当前登录用户,request.user.is_authenticated
  • request.is_secure() 如果请求是通过 HTTPS 发出的,则返回 True

视图装饰器

from django.views.decorators.http import require_http_methods

# 要求视图只接受特定的请求方法,请求方法需要大写
@require_http_methods(["GET", "POST"])
def my_view(request):
    pass

# 其它
@require_GET()  # 要求视图只接受 GET 方法
@require_POST()  # 要求视图只接受 POST 方法
@require_safe()  # 要求视图只接收 GET 和 HEAD 方法

QueryDict

在一个 HttpRequest 对象中, GET 和 POST 属性是 django.http.QueryDict 的实例,QueryDict 是字典的一个子类

from django.http import QueryDict

q = QueryDict("a=1&a=2&c=3")  # <QueryDict: {'a': ['1', '2'], 'c': ['3']}>
q.lists()  # [('a', ['1', '2'])]

返回

HttpResponse

response = HttpResponse()

属性

  • response.status_code 状态码
  • response.cookies
  • response.headers 响应头

子类

  • 200 HttpResponse
  • 301 HttpResponseRedirect
  • 403 HttpResponseForbidden
  • 404 HttpResponseNotFound
  • 500 HttpResponseServerError

没有子类的状态码可以这样返回:HttpResponse(status=201)

  • 设置头字段
response.headers["Age"] = 120  # 方式1
response["Age"] = 120  # 方式2

# 删除
del response.headers["Age"]
del response["Age"]  # 不存在不会引发 KeyError
  • 返回文本
from django.http import HttpResponse

def index(request):
    x = "Hello, World"
    return HttpResponse(x)
  • 将响应视为文件附件
# 返回Excel表
response = HttpResponse(
    my_data,
    headers={
        "Content-Type": "application/vnd.ms-excel",
        "Content-Disposition": 'attachment; filename="foo.xls"',
    },
)

返回模板

  • loader
from django.http import HttpResponse
from django.template import loader

def index(request):
    # 上下文的value将被渲染到模版中对应的key:{{ key }}
    context = {"key": "Hello, World"}

    template = loader.get_template('hello_world.html')
    return HttpResponse(template.render(context, request))
  • 便捷函数 render

https://docs.djangoproject.com/zh-hans/5.1/topics/http/shortcuts/#render

将给定的模板与上下文字典组合在一起,并以渲染的文本返回一个 HttpResponse 对象

from django.shortcuts import render

def index(request):
    context = {"key": "Hello, World"}
    return render(request, 'hello_world.html', context)

重定向

  • HttpResponseRedirect
from django.http import HttpResponseRedirect

def my_view(request):
    ...
    return HttpResponseRedirect('/some/url/')
  • 便捷函数 redirect

返回临时重定向,加 permanent=True 则永久重定向

from django.shortcuts import redirect

def my_view(request):
    ...
    # 参数可以是多种形式
    return redirect("https://example.com/")  # 完整URL
    return redirect("/some/url/")  # 硬编码URL
    return redirect("some-view-name", foo="bar")  # 视图函数和其参数,会被 reverse() 反向解析
    return redirect(MyModel.objects.get(...))  # 模型,将调用模型的 get_absolute_url() 函数

reverse('one_app:results', args=(question.id,))

参数视图函数中避免URL硬编码:reverse(视图名字,args=(视图所需参数的元组,))

返回 404 页面

  • HttpResponseNotFound

需要定义错误页面的 HTML,可以创建名为 404.html 的 HTML 模板,并放置在模版路径顶层

return HttpResponseNotFound("<h1>Page not found</h1>")
  • Http404

返回标准的错误页面

from django.http import Http404

def my_view(request):
    try:
        obj = MyModel.objects.get(pk=1)
    except MyModel.DoesNotExist:
        raise Http404("No MyModel matches the given query.")
  • get_object_or_404

在给定的模型管理器上调用 get(),会引发 Http404

from django.shortcuts import get_object_or_404

def my_view(request):
    # 等价上面 Http404 的写法
    obj = get_object_or_404(MyModel, pk=1)  # 传递 Model

    # 也可以传递 QuerySet
    """
    queryset = Book.objects.filter(title__startswith="M")
    get_object_or_404(queryset, pk=1)
    """
  • get_list_or_404()
from django.shortcuts import get_list_or_404

def my_view(request):
    my_objects = get_list_or_404(MyModel, published=True)

# 等价于
from django.http import Http404
def my_view(request):
    # 将模型管理器.filter()转换为列表,如果为空,则引发 Http404
    my_objects = list(MyModel.objects.filter(published=True))
    if not my_objects:
        raise Http404("No MyModel matches the given query.")

通用视图

https://docs.djangoproject.com/zh-hans/5.1/topics/class-based-views/generic-display/

基于类的通用视图,是为了缓解单调重复的视图层工作

通用视图均继承自 View 类,它处理视图链接到 URLs,HTTP 方法调度和其他简单功能

from django.http import HttpResponseRedirect
from django.shortcuts import render

from django.views import View
# from django.views.generic import TemplateView, RedirectView

from .forms import MyForm

# 基于函数的视图,使用条件分支来响应不同的请求方法
def myview(request):
    if request.method == "POST":
        form = MyForm(request.POST)
        if form.is_valid():
            # <process form cleaned data>
            return HttpResponseRedirect("/success/")
    else:
        form = MyForm(initial={"key": "value"})

    return render(request, "form_template.html", {"form": form})


# 基于类的视图,可以使用不同的类实例方法响应不同 HTTP 请求方法
class MyFormView(View):
    form_class = MyForm
    initial = {"key": "value"}
    template_name = "form_template.html"

    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {"form": form})

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            # <process form cleaned data>
            return HttpResponseRedirect("/success/")
        return render(request, self.template_name, {"form": form})

扩展通用视图,即覆盖类的属性或方法

# 父类
class GreetingView(View):
    greeting = "Good Day"
    def get(self, request):
        return HttpResponse(self.greeting)

# 子类可以覆盖父类的属性和方法
class MorningGreetingView(GreetingView):
    greeting = "Morning to ya"

# URLconf 通过给 as_view 传递参数也可以覆盖类属性
urlpatterns = [
    path("about/", GreetingView.as_view(greeting="G'day")),
]

内置通用视图

# one_app/views.py
from django.views.generic import ListView, DetailView
from .models import Question


# 通用视图 ListView 会显示一个特定类型对象的列表页面
class IndexView(ListView):  # 不再需要传递 request
    model = Question  # 关联模型
    template_name = 'one_app/index.html'  # 关联模版
    context_object_name = 'latest_question_list'  # 上下文变量,默认为object_list


# 通用视图 DetailView 会显示一个特定类型对象的详细信息页面
class ResultsView(DetailView):
    model = Question
    template_name = 'one_app/results.html'