August Rush

一个还在努力成长的小火汁!

游龙当归海,海不迎我自来也。

We create our own demons.

You can reach me at augustrush0923@gmail.com
Django通用视图的get_queryset, get_context_data和get_object等方法
发布:2020年05月24日 | 作者:augustrush | 阅读量: 1581

Django提供了很多通用的基于类的视图(Class Based View),可以帮我们简化执行以下操作的代码。这些基于类的视图还提供了get_queryset, get_context_data和get_object等方法以便我们更灵活地使用它们。我们今天就来看下我们何时需要使用这些方法以及如何使用。

  • ListView - 展示对象列表(比如所有用户,所有文章)
  • DetailView - 展示某个对象的详细信息(比如用户资料,比如文章详情)
  • CreateView - 通过表单创建某个对象(比如创建用户,新建文章
  • UpdateView - 通过表单更新某个对象信息(比如修改密码,修改文字内容
  • FormView - 用户填写表单后转到某个完成页面
  • DeleteView - 删除某个对象
  • RedirectView - 用来进行跳转, 默认是永久重定向(301),可以直接在urls.py中使用

get_queryset()方法

get_queryset()源码:

def get_queryset(self):
    """
    Return the `QuerySet` that will be used to look up the object.

    Note that this method is called by the default implementation of
    `get_object` and may not be called if `get_object` is overridden.
    """
    if self.queryset is None:  # 如果当前对象的queryset值为空
        if self.model:  # 如果当前对象的model值为true
            return self.model._default_manager.all()  # 返回默认manager的值
        else:  # 如果上述条件不满足 则报错ImproperlyConfigured
            raise ImproperlyConfigured(
                "%(cls)s is missing a QuerySet. Define "
                "%(cls)s.model, %(cls)s.queryset, or override "
                "%(cls)s.get_queryset()." % {
                    'cls': self.__class__.__name__
                }
            )
    return self.queryset.all()  # 默认返回queryset的所有值

正如其名,该方法可以返回一个量身定制的对象列表。当我们使用Django自带的ListView展示所有对象列表时,ListView默认会返回Model.objects.all()。

# Create your views here.
from django.views.generic import ListView
from .models import Article

class IndexView(ListView):
    model = Article

然而这可能不是我们所需要的。当我们希望只展示作者自己发表的文章列表且按文章发布时间逆序排列时,我们就可以通过更具体的get_queryset方法来返回一个我们想要显示的对象列表。

# Create your views here.
from django.views.generic import ListView
from .models import Article
from django.utils import timezone

class IndexView(ListView):
    template_name = 'blog/article_list.html'
    context_object_name = 'latest_articles'

    def get_queryset(self):
        return Article.objects.filter(author = self.request.user).order_by('-pub_date')

上述代码等同于:

# Create your views here.
from django.views.generic import ListView
from .models import Article
from django.utils import timezone

class IndexView(ListView):
    model = Article
    template_name = 'blog/article_list.html'
    context_object_name = 'latest_articles'

    def get_queryset(self):
        # 调用父类方法
        qs = super().get_queryset()
        return qs.filter(author = self.request.user).order_by('-pub_date')

我们也可以在DetailView和EditView中定义get_queryset(),一旦定义了该方法那么DetailView返回的一个具体对象只会从queryset里查找。

get_context_data()方法

get_context_data()源码:

def get_context_data(self, **kwargs):
    """
    Insert the single object into the context dict.
    """
    context = {}  # 定义一个空字典context
    if self.object:
        context['object'] = self.object
        context_object_name = self.get_context_object_name(self.object)
        if context_object_name:
            context[context_object_name] = self.object
    context.update(kwargs)  # 组装字典
    return super(SingleObjectMixin, self).get_context_data(**context)

get_context_data可以用于给模板传递模型以外的内容或参数,非常有用。例如现在的时间并不属于Article模型。如果你想把现在的时间传递给模板,你还可以通过重写get_context_data方法(如下图所示)。因为调用了父类的方法,

# Create your views here.
from django.views.generic import ListView
from .models import Article
from django.utils import timezone

class IndexView(ListView):
    queryset = Article.objects.all().order_by("-pub_date")
    template_name = 'blog/article_list.html'
    context_object_name = 'latest_articles'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['now'] = timezone.now() #只有这行代码有用
        return context

get_object()方法

get_object()源码:

    def get_object(self, queryset=None):
        """
        Returns the object the view is displaying.

        By default this requires `self.queryset` and a `pk` or `slug` argument
        in the URLconf, but subclasses can override this to return any object.
        """
        # Use a custom queryset if provided; this is required for subclasses
        # like DateDetailView
        if queryset is None:  # 获取查询集,可以在子类覆盖重写查询集
            queryset = self.get_queryset()  # 获取父类get_queryset()方法

        # Next, try looking up by primary key.
        pk = self.kwargs.get(self.pk_url_kwarg)  # 获取当前对象pk_url_kwarg的值
        slug = self.kwargs.get(self.slug_url_kwarg)  # 获取当前对象slug_url_kwarg的值
        if pk is not None:  # 如果pk有值
            queryset = queryset.filter(pk=pk)  # 根据pk值获取single object。

        # Next, try looking up by slug.
        if slug is not None and (pk is None or self.query_pk_and_slug):
            slug_field = self.get_slug_field()  # 获取slug_field的name值
            queryset = queryset.filter(**{slug_field: slug})

        # If none of those are defined, it's an error.
        if pk is None and slug is None:  # 如果pk和slug都为空,则抛AttributeError
            raise AttributeError("Generic detail view %s must be called with "
                                 "either an object pk or a slug."
                                 % self.__class__.__name__)

        try:
            # Get the single item from the filtered queryset
            obj = queryset.get()  # 获取单个queryset值
        except queryset.model.DoesNotExist:
            raise Http404(_("No %(verbose_name)s found matching the query") %
                          {'verbose_name': queryset.model._meta.verbose_name})
        return obj

DetailView和EditView都是从URL根据pk或其它参数调取一个对象来进行后续操作。下面代码通过DetailView展示一篇文章的详细信息。

# Create your views here.
from django.views.generic import DetailView
from django.http import Http404
from .models import Article
from django.utils import timezone

class ArticleDetailView(DetailView):
    queryset = Article.objects.all().order_by("-pub_date")  #等同于model = Article
    template_name = 'blog/article_detail.html'
    context_object_name = 'article'

然而上述代码可能满足不了你的需求。比如你希望一个用户只能查看或编辑自己发表的文章对象。当用户查看别人的对象时,返回http 404错误。这时候你可以通过更具体的get_object()方法来返回一个更具体的对象。代码如下:

from django.views.generic import DetailView
from django.http import Http404
from .models import Article
from django.utils import timezone

class ArticleDetailView(DetailView):
    queryset = Article.objects.all().order_by("-pub_date")
    template_name = 'blog/article_detail.html'
    context_object_name = 'article'

    def get_object(self, queryset=None):
        obj = super().get_object(queryset=queryset)
        if obj.author != self.request.user:
            raise Http404()
        return obj


  • 标签云

  • 支付宝扫码支持一下

  • 微信扫码支持一下



基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建

京ICP备20007446号-1 & 豫公网安备 41100202000460号

网站地图 & RSS | Feed