3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

メモ〜djangoでのself.objectの注意点

Posted at

今回のお題

今回はdjangoアプリのクラスベースビューの中でself.objectを使う際の注意点を書きます。

ほとんど自分用のメモです。

発生した問題

class ShopDetailView(UserPassesTestMixin, DetailView):
  model = Shop
  def test_func(self):
    return self.request.user.shop.pk == self.object.pk
  
  get_success_url(self):
    return reverse("shop:detal", kwargs = { "pk": self.object.pk })

としたときにエラーになった(selfにはobjectという属性値はないと言われた)。

このエラーはtest_funcをコメントアウトすると解消された。

結論

UserPassesMixinなどのAccessMininを継承しているMixinの中では

self.object

は使えない。

self.kwargs["pk"]

pkを拾って参照中のオブジェクトにアクセスする。

理由

まず汎用ビューでself.objectとすると参照中のオブジェクトが取得できる理由について。

例えばDetailViewであれば継承のルートはSingleObjectMixin > BaseDetailVeiw > DetailView

そしてself.objectの取得はBaseDeatilViewgetメソッドの中で定義されており、そこで使われているget_objectメソッドは大元のSingleObjectMixinで定義されている。

この流れはCreateViewUpdateViewでも同様で、SingleObjectMixin > ModelFormMixin > BaseCreateView, BaseUpdateView > CreateView, UpdateViewという順序で継承されている(objectについてはself.object = form.save()としてModelFormMixinの中で取得している)。

django.views.generic.detail.py
class SingleObjectMixin(ContextMixin):
    """
    Provide the ability to retrieve a single object for further manipulation.
    """
    model = None
    queryset = None
    slug_field = 'slug'
    context_object_name = None
    slug_url_kwarg = 'slug'
    pk_url_kwarg = 'pk'
    query_pk_and_slug = False

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

        Require `self.queryset` and a `pk` or `slug` argument in the URLconf.
        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()

        # Next, try looking up by primary key.
        pk = self.kwargs.get(self.pk_url_kwarg)
        slug = self.kwargs.get(self.slug_url_kwarg)
        if pk is not None:
            queryset = queryset.filter(pk=pk)

        # 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()
            queryset = queryset.filter(**{slug_field: slug})

        # If none of those are defined, it's an error.
        if pk is None and slug is None:
            raise AttributeError(
                "Generic detail view %s must be called with either an object "
                "pk or a slug in the URLconf." % self.__class__.__name__
            )

        try:
            # Get the single item from the filtered queryset
            obj = queryset.get()
        except queryset.model.DoesNotExist:
            raise Http404(_("No %(verbose_name)s found matching the query") %
                          {'verbose_name': queryset.model._meta.verbose_name})
        return obj
django.views.generic.detail.py
class BaseDetailView(SingleObjectMixin, View):
    """A base view for displaying a single object."""
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        context = self.get_context_data(object=self.object)
        return self.render_to_response(context)

class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView):
    """
    Render a "detail" view of an object.

    By default this is a model instance looked up from `self.queryset`, but the
    view will support display of *any* object by overriding `self.get_object()`.
    """

一方でAccessMixin由来のMixinではobjectという属性値が定義されていない。

このため、実装するテンプレートビューが他のテンプレートビューとの多重継承であったとしても、test_funcなどの上記Mixin由来のものの中ではself.objectを使うことはできない。

対処法

``python
self.kwargs["pk"]

を用いて参照中のオブジェクトにアクセスする。

なお、以下のように記述するのは問題ない(すなわち`self.kwargs`はMixinでもTemplateViewでも使える)。

```python
class ShopDetailView(UserPassesTestMixin, DetailView):
  model = Shop
  
  get_success_url(self):
    return reverse("shop:detal", kwargs = { "pk": self.kwargs["pk"] })
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?