我正在尝试自学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,所以我不完全确定创建新用户模型的过程是什么。但是我认为最好提一下,以防改变我定义权限的方式。)
答案 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
。
要获取经过身份验证的用户(request.user
)拥有的对象的列表,您正在寻找创建过滤器。为此,有两种方法:
generic views
。这是更可扩展的。我在drfaddons.filters.IsOwnerFilterBackend
中也做过。这是source code。def filter_queryset(self, queryset)
。这一次您要确保正在应用权限检查。在这里,您将要从has_object_permission
实现has_permission
和permissions.BasePermission
。
通常,您会同时使用filtering
和permission
检查。
要在整个项目中实施此操作,需要在filter
的{{1}}配置中将permission
和settings.py
设置为默认值。您还需要确保每个模型中都存在一个owner字段(最好使用相同的名称)。 (检查REST_FRAMEWORK
模型)。
我在所有项目中都做过同样的事情,因此,我创建了一个相同的程序包。选中DRF Addons。您也可以通过pip Abstract
安装它。它将完成上述所有任务。