如何将“所有者”与默认用户模型相关联?

时间:2018-12-28 22:20:26

标签: django authentication permissions django-rest-framework

我正在尝试自学Django和Django Rest Framework,但是我很难理解对象所有权的定义方式。

我想将默认用户模型应用于DRF文档中提供的自定义“ IsOwnerOrReadOnly”权限。我的目标是:用户将能够使用自己的帐户来PUT / PATCH / DELETE信息,但无法更改其他任何用户的信息。

我的用户序列化器:

class UserSerializer(serializers.ModelSerializer):

    rides = serializers.PrimaryKeyRelatedField(many = True, queryset = Ride.objects.all())

    class Meta:
        model = User
         fields = [
            'pk',
            'username',
            'first_name',
            'last_name',
            'email',
            'password',
            'rides'
        ]
        write_only_fields = ['password']
        read_only_fields = ['pk', 'username']

IsOwnerOrReadOnly代码:

class IsOwnerOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.owner == request.user

问题出在行return obj.owner == request.user上。它总是返回AttributeError: 'User' object has no attribute 'owner’。为什么用户的“所有者”不是用户本身?我该怎么说“ owner = this object”,以便我的权限类起作用?

后续问题:我最终希望扩展默认的用户模型以添加更多字段。如果我通过继承AbstractBaseUser来创建新的用户模型,我的权限是否仍然有效? (我刚开始学习Django,所以我不完全确定创建新用户模型的过程是什么。但是我认为最好提一下,以防改变我定义权限的方式。)

2 个答案:

答案 0 :(得分:0)

您会收到错误消息,因为没有所有模型的所有者这样的抽象概念。您将必须在模型中定义对象与用户之间的关系,并将其应用于每个模型的权限中。

在这种情况下,您要确保用户正在尝试修改自己的用户对象,因此您可以执行以下操作:

#include <memory>
#include <map>

enum COMPONENT_TAG {
    COMP_BASE,
    COMP_ITEM,
    COMP_ITEM_EQUIP
};

struct VirtualBaseComponent { // component

    COMPONENT_TAG tag;
    virtual ~VirtualBaseComponent() = 0;
};
VirtualBaseComponent::~VirtualBaseComponent() {};

struct ItemComponent : public VirtualBaseComponent {
    COMPONENT_TAG tag = COMP_ITEM;
    ~ItemComponent() {};
};

struct EquipmentComponent : public ItemComponent {
    COMPONENT_TAG tag = COMP_ITEM_EQUIP;
    ~EquipmentComponent() {};
};

struct Entity {
    std::map<COMPONENT_TAG, std::unique_ptr<VirtualBaseComponent>> components;

    template <typename T>
    T* get_component(COMPONENT_TAG tag) {

        auto found = components.find(tag);
        VirtualBaseComponent* baseptr;
        if (found == components.end()) {
            baseptr = nullptr;
        } else {
            baseptr = found->second.get();
        }
        return dynamic_cast<T*>(baseptr);
    }
};

int main() {
    {
    //set up a random item (a box)
    Entity box;
    box.components.insert(std::make_pair(COMP_ITEM, std::make_unique<ItemComponent>()));

    //fetching a base component: ok, this works fine
    ItemComponent *boxptr = box.get_component<ItemComponent>(COMP_ITEM);
    //do_stuff(boxptr->item_property);
    }


    {// set up a sword
    Entity sword;
    sword.components.insert(std::make_pair(COMP_ITEM_EQUIP, std::make_unique<EquipmentComponent>()));

    //but here, if I actually want to use some "item property" of the sword not related to its equipment-ness...
    //oops! that retrieves a nullptr, because sword does not have a base item tag
    ItemComponent *swordptr = sword.get_component<ItemComponent>(COMP_ITEM);

    ItemComponent a = *swordptr; //dereferencing it crashes the program
    }


}

因此,从本质上讲,这仅适用于User模型,您必须根据模型与用户之间的关系对其进行扩展以适用于其他模型。

您可能想要为每个资源定义不同的权限类别,而不是尝试将所有逻辑放在同一权限类别中。

关于扩展用户模型的问题。文档中的page解释了扩展现有用户模型的不同方法,这些方法基本上是通过扩展class IsOwnerOrReadOnly(permissions.BasePermission): def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True return obj == request.user AbstractUser来实现的。 Django权限框架仍然可以使用,因为它们针对这些基类实现了。

但是请记住,DRF权限类与Django权限不同。您可以使用Django权限来实现权限类中的逻辑,但是可以在没有权限的情况下实现它们。

从评论中编辑

如果您有一个拥有者的AbstractbaseUser模型,则可以将其合并。

Ride

答案 1 :(得分:0)

所有权

说有一个Model A。要定义所有权,您需要在Model A中创建一个字段,该字段将指向User模型。您可以轻松做到这一点。确保您使用django.contrib.auth.get_user_model

从API获取拥有对象的列表

要获取经过身份验证的用户(request.user)拥有的对象的列表,您正在寻找创建过滤器。为此,有两种方法:

  • 创建一个过滤器类并在其中使用generic views。这是更可扩展的。我在drfaddons.filters.IsOwnerFilterBackend中也做过。这是source code
  • 在每个API类中覆盖def filter_queryset(self, queryset)

确保检索/更新/删除

这一次您要确保正在应用权限检查。在这里,您将要从has_object_permission实现has_permissionpermissions.BasePermission

通常,您会同时使用filteringpermission检查。

在整个项目中实施

要在整个项目中实施此操作,需要在filter的{​​{1}}配置中将permissionsettings.py设置为默认值。您还需要确保每个模型中都存在一个owner字段(最好使用相同的名称)。 (检查REST_FRAMEWORK模型)。

我在所有项目中都做过同样的事情,因此,我创建了一个相同的程序包。选中DRF Addons。您也可以通过pip Abstract安装它。它将完成上述所有任务。