web-dev-qa-db-ja.com

Django:ミックスインとディスパッチメソッドを備えたクラスベースのビュー

通常、クラスベースビューのdispatchメソッドを使用して、初期変数を設定したり、ユーザーの権限に基づいてロジックを追加したりします。

例えば、

from Django.views.generic import FormView
from braces.views import LoginRequiredMixin

class GenerateReportView(LoginRequiredMixin, FormView):
    template_name = 'reporting/reporting_form.html'
    form_class = ReportForm

    def get_form(self, form_class):
        form = form_class(**self.get_form_kwargs())
        if not self.request.user.is_superuser:
            form.fields['report_type'].choices = [
                choice for choice in form.fields['report_type'].choices
                if choice[0] != INVOICE_REPORT
            ]
        return form

期待どおりに動作します。匿名ユーザーがページにアクセスすると、 LoginRequiredMixindispatchメソッドが呼び出され、ユーザーがログインページ。

しかし、このビューにいくつかの権限を追加したり、いくつかの初期変数を設定したりする場合は、たとえば、

class GenerateReportView(LoginRequiredMixin, FormView):

    def dispatch(self, *args, **kwargs):
        if not (
            self.request.user.is_superuser or
            self.request.user.is_manager
        ):
            raise Http404
        return super(GenerateReportView, self).dispatch(*args, **kwargs)

ビューが継承するミックスインのdispatchメソッドがまだ呼び出されていないため、機能しない場合があります。したがって、たとえば、ユーザーの権限を要求できるようにするには、LoginRequiredMixinから検証を繰り返す必要があります。

class GenerateReportView(LoginRequiredMixin, FormView):

    def dispatch(self, *args, **kwargs):
        if self.request.user.is_authenticated() and not (
            self.request.user.is_superuser or
            self.request.user.is_manager
        ):
            raise Http404
        return super(GenerateReportView, self).dispatch(*args, **kwargs)

この例は単純ですが、ミックスインにはさらに複雑なロジックが含まれている場合があります。これは、権限を確認し、いくつかの計算を行い、それをクラス属性に格納するなどです。

今のところ、(上記の例のように)ミックスインからコードをコピーするか、ビューのdispatchメソッドから別のミックスインにコードをコピーし、最初のコードの後に​​継承して実行することで解決します順序(この新しいミックスインは1つのビューでのみ使用されるため、それほどきれいではありません)。

適切な方法があるので、このような問題を解決しますか?

23
vero4ka

すべての権限をチェックするカスタムクラスを作成します

from Django.views.generic import FormView
from braces.views import AccessMixin

class SuperOrManagerPermissionsMixin(AccessMixin):
    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_authenticated():
            return self.handle_no_permission(request)
        if self.user_has_permissions(request):
            return super(SuperOrManagerPermissionsMixin, self).dispatch(
                request, *args, **kwargs)
        raise Http404 #or return self.handle_no_permission

    def user_has_permissions(self, request):
        return self.request.user.is_superuser or self.request.user.is_manager

# a bit simplyfied, but with the same redirect for anonymous and logged users
# without permissions


class SuperOrManagerPermissionsMixin(AccessMixin):
    def dispatch(self, request, *args, **kwargs):
        if self.user_has_permissions(request):
            return super(SuperOrManagerPermissionsMixin, self).dispatch(
                request, *args, **kwargs)
        else:
            return self.handle_no_permission(request)

    def user_has_permissions(self, request):
        return request.user.is_authenticated() and (self.request.user.is_superuser
                                                    or self.request.user.is_manager)


class GenerateReportView(SuperOrManagerPermissionsMixin, FormView):
#Remove next two lines, don't need it
    def dispatch(self, *args, **kwargs):
        #or put some logic here
        return super(GenerateReportView, self).dispatch(*args, **kwargs)

また、GenerateReportView(SuperOrManagerPermissionsMixin、FormView)クラスの実装では、ディスパッチメソッドをオーバーライドする必要はありません。

複数の継承を使用していて、親クラスの1つに多少の改善が必要な場合は、最初に改善することをお勧めします。コードをよりクリーンに保ちます。

5
KePe

これは古い投稿ですが、他の人が出くわす可能性があるので、ここに私の提案する解決策を示します。

あなたが言う時

「[...]このビューにいくつかの権限を追加したいたとえば、いくつかの初期変数を設定する、たとえば[...]」

ビューのディスパッチメソッドでこれらの初期変数を設定する代わりに、それらの変数を設定するための個別のメソッドを記述し、そのメソッドをget(および必要に応じてpost)メソッドで呼び出すことができます。これらはディスパッチ後に呼び出されるため、初期変数の設定はミックスインでのディスパッチと競合しません。したがって、メソッドをオーバーライドします

def set_initial_variables():
    self.hey = something
    return 

def get(blablabla):
    self.set_initial_variables()
    return super(blabla, self).get(blabla)

これはおそらく、ビューのディスパッチでミックスインのコードをコピーして貼り付けるよりもクリーンです。

3
andyk

あなたが挙げた例では、私は Django-bracesUserPassesTestMixinを使用します。

class GenerateReportView(UserPassesTestMixin, FormView):
    def test_func(self, user):
        return user.is_superuser or user.is_manager

それがより複雑なロジックに適さない場合、複雑なロジックをうまくカプセル化するため、別のミックスインを作成するとOKアプローチのように聞こえます。

[〜#〜]編集[〜#〜]
Django 1.9以降、UserPassesTestMixinがDjangoに含まれるようになりました。 https://docs.djangoproject.com/en/1.11/topics/auth/default /#Django.contrib.auth.mixins.UserPassesTestMixin

2
Alasdair

Django serPassesTestMixin mixinまたは @ user_passes_test デコレータで実行できます。

UserPassesTestMixinの例

from Django.contrib.auth.mixins import UserPassesTestMixin


class SuperUserOrManagerRequiredMixin(UserPassesTestMixin):
    def test_func(self):
        if self.request.user.is_superuser or self.request.user.is_manager:
            return True

        return False


class MyView(LoginRequiredMixin, SuperUserOrManagerRequiredMixin, View):
    ...
0
Yuriy Kots