Django学习21-基于类的视图1

基于类的视图

  视图是可调用的,它接收请求并返回响应。这可能不仅仅是一个函数,Django提供了一些可用作视图的类的示例。这些允许您通过利用继承和mixin来构建视图并重用代码。
  基于类(class-based)的视图提供了另一种方法,将视图实现为Python对象而不是函数。 它们不会替换基于函数(function-based)的视图,但与基于函数的视图相比具有一定的差异和优势:

  • 与特定HTTP方法(GET,POST等)相关的代码组织可以通过单独的方法而不是条件分支来解决。
  • 诸如mixins(多重继承)之类的面向对象技术可用于将代码分解为可重用组件。

使用基于类的视图

  一般而言,如果要对不同的HTTP请求做出不同的相应的话,function-based views会在单一的函数中采用判断分支的方法,比如:

from django.http import HttpResponse

def index(request):
    if request.method == 'GET':
        # <view logic>
        return HttpResponse('result')
     if request.method == 'POST':
        # <view logic>
        return HttpResponse('result')

  如果使用基于类的视图:

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

class IndexView(View):
    def get(self, request):
        return HttpResponse('get result')

    def post(self, request):
        return HttpResponse('post result')

  这里IndexView继承了class django.views.generic.base.View类,它的属性和方法:

  • http_method_names:视图支持的HTTP请求方法,默认['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

  • as_view(**initkwargs):返回一个可调用对象,参数为request返回值是响应:

    response = MyView.as_view()(request)
    
  • dispatch(request, *args, **kwargs):检查HTTP方法并尝试委托给与HTTP方法匹配的方法; 一个GET将被委托给get(),一个POST到post(),依此类推。

  • http_method_not_allowed(request, *args, **kwargs):视图接收到不支持的HTTP请求方法时返回的错误,默认HttpResponseNotAllowed

  • options(request, *args, **kwargs):处理HTTP的OPTION请求,返回响应中的ALLOW首部会包含视图支持的请求方法;

  django的URL解析器需要将request和相应的参数传递给一个可调用的函数,而不是一个类。所以class-based view提供一个类方法:as_view()来解决这个问题,as_view()方法让你可以把类当做函数来调用。as_view创建一个类实例,然后调用它的dispatch方法,dispatch分析出request是GET、POST或者其他,然后将request匹配给相应的函数,比如将POST请求匹配给post()函数,如果给函数没有定义的话,将引发HttpResponseNotAllowed错误。

# urls.py
from django.urls import path
from myapp.views import MyView

urlpatterns = [
    # path('', views.index, name='index'),
    path('', views.IndexView.as_view()),
]

  虽然小型的class-based view并不需要依靠类属性来完成它的工作,但是类属性在很多的基于类的设计中都很有用。设置类属性有两个方法。第一个方法是标准的python方法:在子类中重写类的属性和方法,比如在父类中有greeting属性,在子类中就可以重写:

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

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")),
]

使用多重继承(mixins)

  Mixins是多重继承(multiple inheritance)的一种形式,其中可以组合多个父类的行为和属性。例如,在基于类的通用视图中,有一个名为TemplateResponseMixin的mixin,其主要目的是定义render_to_response()方法。当与View基类的行为结合使用时,结果是一个TemplateView类:它拥有分析request并作出相应匹配的方法(原本定义在View中的行为),也拥有一个接受一个template_name并返回一个TempalteReponse对象的render_to_response()方法(原本定义在 TemplateResponseMixin中的行为)
  Mixins是在多个类中重用代码的绝佳方法,但它们需要一些代价。代码就是使用多重继承的次数越多,读取子类就越难以知道它正在做什么,并且如果你继承了一个具有一个类的东西,那么知道哪些方法可以覆盖哪些方法就更难了另请注意,您只能从一个通用视图继承 - 也就是说,只有一个父类可以从View继承,其余的(如果有的话)应该是mixins。尝试从多个继承自View的类继承 - 例如,尝试在列表顶部使用表单并组合ProcessFormView和ListView - 将无法按预期工作。
  使用基于类的视图处理表单, 原视图:

@login_required
def new_topic(request):
    """添加新的主题"""
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = TopicForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # 添加数据到数据库
            topic = Topic(text=request.POST.get('text'), owner=request.user)

            topic.save()
            # process the data in form.cleaned_data as required
            # ...
            # redirect to a new URL:
            return redirect('learning_logs:my_topics')
            # return HttpResponseRedirect(reverse('learning_logs:my_topics'))

    # if a GET (or any other method) we'll create a blank form
    else:
        form = TopicForm()

    return render(request, 'new_topic.html', {'form': form})

  使用基于类的视图:

# new_topic 的 基于类的视图
# 装饰一个类
decorators = [never_cache, login_required]
# @method_decorator(login_required, name='dispatch')
# @method_decorator(never_cache, name='dispatch')


@method_decorator(decorators, name='dispatch')
class NewtopicView(View):
    form_class = TopicForm
    initial = {}
    template_name = 'new_topic.html'

    # 装饰类的每一个实例
    # @method_decorator(login_required)
    # def dispatch(self, *args, **kwargs):
    #     return super().dispatch(*args, **kwargs)

    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():
            topic = Topic(text=request.POST.get('text'), owner=request.user)
            topic.save()
            return redirect('learning_logs:my_topics')
        return render(request, self.template_name, {'form': form})

使用装饰器装饰类视图

  使用装饰器装饰类视图的方法和装饰函数的方法不一样,需要method_decorator来将其进行转化:

  • 在URLconf中使用装饰器:path('new_topic/', login_required(views.NewtopicView.as_view()), name='new_topic'),
  • 使用method_decorator装饰类的dispatch方法;
  • 装饰整个类:可以使用decorators装饰器列表,选择多个装饰器,在调用时会按顺序执行装饰;

基于类的通用视图

在Web应用程序开发过程中可能遇到一些重复的模式。Django的通用视图(generic views)正是为了解决这些枯燥的工作而被开发的。它们采用视图开发中的某些常用习语和模式并对其进行抽象,以便能快速编写数据的公共视图,而无需编写太多代码。
  Django附带通用视图来执行以下操作:

  • 显示单个对象的列表和详细信息页面。如果我们要创建一个管理会议的应用程序,那么TalkListViewRegisteredUserListView就是列表视图的示例。单个谈话页面就是我们称之为“细节”视图的一个例子。
  • 在年/月/日归档页面,相关详细信息和“最新”页面中显示基于日期的对象。
  • 允许用户创建,更新和删除对象 - 无论是否授权。

  总之,这些视图提供了简单的界面来执行开发人员遇到的最常见任务。

TemplateView

  django.views.generic.base.TemplateView呈现给定模板,其中包含在URL中捕获的参数的上下文。它继承了这3个类:

  • django.views.generic.base.TemplateResponseMixin
  • django.views.generic.base.ContextMixin
  • django.views.generic.base.View:就是上文提到的View类

  它的方法get_context_data()会返回表示模板上下文的字典。 提供的关键字参数将构成返回的上下文。使用方法:

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['number'] = random.randrange(1, 100)
    return context

对象的通用视图

  TemplateView当然很有用,但Django的通用视图在呈现数据库内容的视图时确实很有用。 因为它是如此常见的任务,Django附带了一些内置的通用视图,这些视图使得生成对象的列表和详细视图非常容易。

ListView

例如,现在需要一个页面显示所有topics:

from django.views.generic import ListView

class Topics(ListView):
    template_name = 'topic_list.html'
    model = Topic
  

  template_name指定使用的模板,model指定使用的模型,在URLconf设置使用基于类的通用视图:

urlpatterns = [
	# ...
    # path('topics/', views.topics, name='topics'),
    path('topics/', views.Topics.as_view(), name='topics'),
]

  这就是所有需要Python代码,而在模板中要使用object_list来指明包含topics的列表:

<ol>
{% for topic in object_list %}
  <li>
    <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic.text }}</a><br/>
  </li>
{% endfor %}
</ol>

  这就是它的全部内容。通用视图的所有很酷的功能都来自更改通用视图上设置的属性。
  若是要进行分页显示,需要添加参数:

class Topics(ListView):
    # 指定模板
    template_name = 'topic_list.html'
    
    model = Topic
    # 每页显示数量
    paginate_by = 10
    # 最后一页最多显示
    paginate_orphans = 15
    # 分页排序的标准
    ordering = 'date_added'
    # 修改默认的object_list名称
    context_object_name = 'topic_list'

  而且使用Django自带的分页使用的URL模式为:path('topics/page<int:page>/', views.Topics.as_view(), name='topics'),,或者还是用path('topics/', views.Topics.as_view(), name='topics'),,用/objects/?page=3的方式进行查找。

  其传递给模板的页面对象为page_obj 具体查看ListView继承的MultipleObjectMixin
  修改模板

{% block content %}

<ol>
{% for topic in topic_list %}
  <li>
    <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic.text }}</a><br/>
  </li>
{% endfor %}
</ol>
{#   使用django-bootstrap #}
  <div>
    {% bootstrap_pagination page_obj url="?page=1" size="small"%}
  </div>



{% endblock %}

  这就是使用基于类的通用视图完成多个模型对象的显示。
在这里插入图片描述

  指定model的值model = Topic,实质上是为了获取queryset查询的结果集,等价于queryset = Topic.objects.all()可以直接指定结果集:

class Topics(ListView):
    # 指定模板
    template_name = 'topic_list.html'
    queryset = Topic.objects.order_by('-date_added')

  可以继承这个类来构建其它视图:

class MyTopics(Topics):
    """基于类的视图,继承Topics显示自己的topics"""

    def get_queryset(self):
        """筛选 作者为当前用户 返回queryset"""
        return Topic.objects.filter(owner=self.request.user).order_by('-date_added')

  只需要设置queryset就能显示‘自己’的topics。

DetailView

  DetailView提供检索单个对象以进行进一步操作的功能。渲染对象的“细节”视图。默认情况下,这是从self.queryset查找的模型实例,但是view将通过覆盖self.get_object(self, queryset=None):来支持显示任意对象。
  如现在Author有一个last_accessed字段来记录最后一次访问的时间:

# models.py
from django.db import models

class Author(models.Model):
    salutation = models.CharField(max_length=10)
    name = models.CharField(max_length=200)
    email = models.EmailField()
    headshot = models.ImageField(upload_to='author_headshots')
    last_accessed = models.DateTimeField()

  那么在定义好URLconf为基于类的视图后调用get_object就可对对象进行修改并返回:

from django.utils import timezone
from django.views.generic import DetailView
from books.models import Author

class AuthorDetailView(DetailView):

    queryset = Author.objects.all()

    def get_object(self):
        obj = super().get_object()
        # Record the last accessed date
        obj.last_accessed = timezone.now()
        obj.save()
        return obj

猜你喜欢

转载自blog.csdn.net/qq_19268039/article/details/84253199