Django-如何使用两个UserPassesTestMixin

时间:2017-03-17 18:53:51

标签: python django

使用多个UserPassesTestMixin适用于其他视图但不适用于ProfileCreateView

似乎存在一个我无法弄清楚的逻辑错误!

views.py

class CurrentUser(UserPassesTestMixin):
    def test_func(self):
        x = self.request.user.slug
        print (x)
        y = self.kwargs['slug']
        print (y)
        if x == y:
            return True
        else:
            if self.request.user.is_authenticated():
                def get_absolute_url(self):
                    return reverse('profile:view_profile', kwargs={ "slug": self.slug })


class ProfileCreate(CurrentUser, UserPassesTestMixin, LoginRequiredMixin, UpdateView):
    model = User
    form_class = ProfileForm
    template_name="profile/profile_new.html"

    def test_func(self):
        x = self.get_object().full_name
        print (x)
        y = ''
        if x == y:
            return True    
        else:
            def get_absolute_url(self):
                return reverse('profile:view_profile', kwargs={ "slug": self.slug })



class ProfileEdit(CurrentUser, UserPassesTestMixin, LoginRequiredMixin, UpdateView):
    model = User
    form_class = ProfileForm
    template_name="profile/profile_new.html"

    def test_func(self):
        x = self.get_object().full_name
        #print (x)
        y = ''
        if x != y:
            return True
        else:
            def get_absolute_url(self):
                return reverse('profile:profile_edit', kwargs={ "slug": self.slug })

班级CurrentUser检查用户是否具有编辑个人资料的权限。如果用户是个人资料的作者,则用户将只能编辑该个人资料。 x和y都是这个类打印不同的值 x打印登录用户的slug值 y打印来自url的slug 这怎么可能是真的?

test_func类中的ProfileCreate检查用户是否已创建配置文件,即如果full_name为空字符串,则允许用户创建配置文件。否则重定向到其他视图。

CurrentUser类适用于其他视图,例如ProfileEdit等,

摘要! - loggedin user- localhost:8000/username1

用户将能够访问localhost:8000/username1/edit/但不能访问localhost:8000/username2/edit/

只有当localhost:8000/username1/create/为空时,用户才能访问full_name。 如果用户以username1身份登录,则用户应该无法访问localhost:8000/username2/create/ ----这部分代码不起作用。代码中的逻辑错误是什么?

2 个答案:

答案 0 :(得分:1)

你不能有两个叫做同一个东西的方法。您在ProfileCreate中的test_func会覆盖CurrentUser中的{。}}。

答案 1 :(得分:0)

您可以通过对继承使用合成来实现:

class A(UserPassesTestMixin):
    def test_func(self):
        return True

class B(UserPassesTestMixin):
    def test_func(self):
        return True

class C(UserPassesTestMixin):
    def test_func(self):
        return B.test_func(self) and A.test_func(self) and True

如果像我一样,您不喜欢它,则可以进行装饰以处理连词:

def check_all_tests(cls):
    tests = []
    if issubclass(cls, UserPassesTestMixin):
        tests.append(cls.test_func)
    for parent in cls.__bases__:
        if issubclass(parent, UserPassesTestMixin):
            tests.append(parent.test_func)
    cls.test_func = lambda view: all(test(view) for test in tests)
    return cls

@check_all_tests
class C(A, B, UserPassesTestMixin):
    def test_func(self):
        return True

并且我喜欢一直进行装饰,所以我做了一个Decoration_builder:

def make_user_test(func):
    def wrapper(cls):
        if issubclass(cls, UserPassesTestMixin):
            old_func = cls.test_func
            cls.test_func = lambda view: old_func(view) and func(view)
            return cls
        else:
            new_cls = type(cls.__name__, (UserPassesTestMixin, *cls.__bases__), cls.__dict__.copy())
            new_cls.test_func = func
            return new_cls
    return wrapper


@make_user_test
def is_creator(view):
    return view.get_object().created_by == view.request.user


@is_creator
@make_user_test(lambda view: view.get_object() != view.request.user.profile.party)  # user cannot delete itself
class PartyDelete(DeleteView):
    model = Party
    success_url = reverse_lazy('party_list')

如果您想要一些元数据,我做了一个函数,该函数将测试列表(函数)作为参数,并返回一个类装饰,该类装饰通过继承包装了原始对象,并添加了所有测试的并集:

from inspect import isfunction, signature
from functools import wraps


def user_passes_tests(*args):
    """
    Creates a class decorator that makes decorated class implement UserPassesTestMixin and
    provides implementation using a logical conjunction with all provided functions

    Decorated class must NOT implement UserPassesTestMixin itself
    """
    assert len(args) > 0, 'Provide at least one argument'
    assert all(isfunction(arg) for arg in args), 'All arguments are supposed to be functions'
    assert all(len(signature(f).parameters) == 1 for f in args), 'Functions must expect one and only one argument'

    def decorator(cls):
        assert not issubclass(cls, UserPassesTestMixin), \
            "Do not apply this decorator on UserPassesTestMixin subclass"

        @wraps(cls, updated=())
        class WrapperClass(UserPassesTestMixin, cls):
            def test_func(self):
                return all(func(self) for func in args)

        return WrapperClass

    return decorator

@user_passes_tests(is_creator, is_not_party)
class PartyDelete(DeleteView):
    model = Party
    success_url = reverse_lazy('party_list')