美文网首页
django通用视图之ListView和DetailView原理

django通用视图之ListView和DetailView原理

作者: 前端无聊 | 来源:发表于2019-04-06 03:23 被阅读0次

通看了ListView和DetailView源码,记录下在这2类的子类中需要注意的重写的方法和属性(或者说坑),并对2类进行了对比分析。

  • ListView 和 DetailView的子类一般可以重写的属性和方法:
    1. model或者queryset或者get_queryset()(关联模型数据查询,设置这三者的功效一样的)
    2. template_name(模版命名)
    3. get_context_data()(response回去之前对返回数据的修改)
  • DetailView的子类一般还可以重写下面特有的区别于ListView 的方法:
    1. get_object() (获得某单个模型实例对象,也就是数据库中某条数据)

注意:本文都是围绕这上面的东西进行论述,如果想更清楚理解这几个方法, 请看官方文档或者最好自己去看源码去。虽然我已经看过源码,但是系统的讨论会 话费大量的时间和精力,本文已经力求准确和系统的论述上面的知识点。

app下的urls.py

from django.urls import path,re_path
from tyViews import views

urlpatterns = [
    re_path('^PublisherBookList/([\w-]+)/$',views.PublisherBookList.as_view()),
    re_path(r'^AuthorDetailView/(?P<pk>[0-9]+)/$', views.AuthorDetailView.as_view()),
]

views.py

from django.shortcuts import render,get_object_or_404

from django.views.generic import ListView,DetailView
from tyViews.models import Publisher,Book,Author
from django.utils import timezone
from django.db.models import Q


# class PublisherList(ListView):
class PublisherDetail(DetailView):
    # queryset = Publisher.objects.all()
    queryset = Publisher.objects.order_by('-publication_date')
    template_name = 'tyViews/publisher.html'

    # 指定model属性值或者queryset = Publisher.objects.xxxx()功效上都是一样的
    # model = Publisher
    # context_object_name='list'#增加一个传递到模板中context字典的键,非更改

    def get_context_data(self, **kwargs):
        # 先调用原来的实现,获取上下文
        context = super(PublisherDetail, self).get_context_data(**kwargs)
        # 把所有图书构成的查询集合添加到上下文中
        context['book_list'] = Book.objects.all()
        return context

class PublisherBookList(ListView):
    
    #可以再url中指定属性值,但是不建议,因为如果在url中指定,
    #那么将对url下的所有视图函数都可以访问更改这个值(如果 值可变的话),
    #会造成不安全的数据发生,除非你能很明确这个值不会被更改,这样才能
    #达到安全机制。
    template_name = 'tyViews/pb.html'

    #其实是给context字典添加一个键,他的值是等于键object_list的值
    #所以在模板中使用context_object_name定义的键或者‘object_list’都可以的
    context_object_name = 'publisher_book_list'

    #注意这里并没有queryset和指定model,而是重写了get_queryset()方法,但是理论上是等效的,
    #也就是:model属性  =  queryset属性  =  get_queryset()方法
    #这个方法是查询数据库
    def get_queryset(self):
        #self.args[0]是获取到url参数中的第一个值
        self.publisher = get_object_or_404(Publisher, name=self.args[0])

        #  单纯的filter表示下面的|,也就是2个条件都同时查询出来的数据集
        # return Book.objects.filter(title__contains='python' ,publisher__exact= self.publisher )

        # | 表示2个条件都查询出来合并起来成为查询集合
        # &  表示先查询第一个条件然后再在第一个条件的基础上进行第二个条件的刷选
        return Book.objects.filter(Q(title__contains='python') & Q(publisher__exact= self.publisher))
        
        #  在Q前面加上~表示非
        # return Book.objects.filter(Q(title__contains='python') & ~Q(publisher__exact= self.publisher))

        # return Book.objects.filter(publisher__book__title__contains='python')


        # 扩展:
        #  Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。
        # 正确:
        # Poll.objects.get(
        #     Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        #     question__startswith='Who')
        #
        # 错误:
        # Poll.objects.get(
        #     question__startswith='Who',
        #     Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))


    #这个方法是在get_queryset()方法执行的基础上再执行对数据进行更改
    def get_context_data(self, **kwargs):
        # 先调用原来的实现,获取上下文
        context = super(PublisherBookList, self).get_context_data(**kwargs)
        # 添加出版社对象,拼接context字典
        context['publisher'] = self.publisher
        #print('-'*40)
        #print(context)
        #print('-' * 40)
        return context  ## 执行额外的操作,返回字典给模板呈现出来

class AuthorDetailView(DetailView):
    #在DetailView中没有返回queryset集合,而是返回了一个模型对象实例,
    #所以不存在像ListView中这样的object_list键,同时继承自DetailView的
    #子类都要在url中传入pk或者slug的参数值(如url中的 (?P<pk>[0-9]+) ),否则报错。
    
    #template_name 属性值并不是必须的,如果不设置的话,默认是:模型名小写_detail,
    #如果是在ListView的子类中则默认是:模型名小写_list  。
    template_name = 'tyViews/books_by_publisher.html'
    # queryset = Author.objects.all()


    #需要注意的是:我强烈不建议在重写了get_object后还重写get_queryset
    #因为get_objecth会在get_queryset的基础上filter(pk=pk),而pk
    #是你url中输入的,除非你想优化数据库的查询(比如部分查询),在源码注释中
    # 说重写了get_object,就不会执行get_queryset,但是是会执行的!这是注
    # 释的bug。

    #下面的功效和上面的queryset = Author.objects.all()是一样的
    #总之要3选1来设置,否则会报错(model,queryset,get_queryset())
    def get_queryset(self):
        # self.author = get_object_or_404(Author, pk=self.args[0])
        # return Author.objects.filter(name=self.author.name)
        #print('get_queryset....'*3)
        return Author.objects.filter(Q(name__contains='1111'))

    #这个方法主要是用来进行在访问数据库获取数据实例的时候进行的一些额外的操作
    #必须返回单个queryset模型实例对象
    #在模板中使用object来进行访问这个实例对象,get_object是DetailView中特有而
    #ListView中没有的方法,功效上相当于ListView中的get_queryset
    #返回一个模型对象实例,而不是返回一个queryset集合(模型对象实例的集合)。
    #这个方法是从get_queryset中获取一个符合条件的(url中输入的pk条件)
    def get_object(self):
        # 调用超类中的同名方法
        object = super(AuthorDetailView, self).get_object()
        # 记录最后访问日期
        object.last_accessed = timezone.now()
        object.save()
        # 返回对象
        return object


pb.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>publisher_book_list</h1>
    {{ publisher }}
        <hr>
{#{% for publisher_book in publisher_book_list %}#}
{% for publisher_book in object_list %}{# object_list和 publisher_book_list一样的数据#}
    <ul>
        <li>{{ publisher_book }}</li>
    </ul>

{% endfor %}

</body>
</html>

books_by_publisher.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Publisher</title>
</head>
<body>
    <h2>Publisher_6666</h2>
    <hr>

    <ul>
{#        {{ publisher }}#}

{#        {% for author in object %}#}
            <li>{{ object.name }}</li>
        <li>{{ object.last_accessed }}</li>
{#        {% endfor %}#}
    </ul>
</body>
</html>

数据库:


3.png

启动服务器访问后结果如图:

1.png 2.png

相关文章

网友评论

      本文标题:django通用视图之ListView和DetailView原理

      本文链接:https://www.haomeiwen.com/subject/xzuliqtx.html