Добавление разрешений для объекта для администратора django
Фон
Я разрабатываю приложение django для сайта аренды для отпуска. Он будет иметь двух типов пользователей, арендаторов и управляющих недвижимостью.
Я хочу, чтобы менеджеры по недвижимости могли управлять своими свойствами аренды в admin django. Однако они должны иметь возможность управлять своими собственными свойствами.
Я понимаю, что admin django по умолчанию не поддерживает это. Мне интересно, сколько проблем было бы добавить эту функциональность, и, если это возможно, лучший способ справиться с этим.
Цель
В идеале я рисую, что он работает примерно так:
auth
уже разрешает такие права:
vacation | rental | Can add rental
vacation | rental | Can change rental
vacation | rental | Can delete rental
Я хотел бы изменить это на что-то вроде:
vacation | rental | Can add any rental
vacation | rental | Can change any rental
vacation | rental | Can delete any rental
vacation | rental | Can add own rental
vacation | rental | Can change own rental
vacation | rental | Can delete own rental
Возможное решение
Как бы рамки решали, принадлежит ли аренда (или что-то еще) пользователю? Я думаю, что он проверяет класс vacation.Rental
, чтобы увидеть, имеет ли он от ForeignKey
до auth.User
(возможно, имеющее определенное имя, например "владелец" ).
-
При создании нового vacation.Rental
значение поля ForeignKey
будет принудительно привязано к текущему идентификатору пользователя. Поле ForeignKey
не будет отображаться в форме.
-
При перечислении арендной платы будет отображаться только аренда с ForeignKey
, соответствующая текущему пользователю.
-
При изменении арендной платы будет отображаться только аренда с ForeignKey
, совпадающая с текущим пользователем. Поле ForeignKey
не будет отображаться в форме.
Конечно, это должно работать для любой модели, имеющей соответствующее поле ForeignKey
, а не только нашу модель vacation.Rental
.
До сих пор это звучит, или я должен идти в другом направлении?
Осложнения
Теперь, здесь сложная часть; Я не уверен, как справиться с этим. Скажем, a Rental
может иметь много "RentalPhotos". RentalPhoto
имеет a ForeignKey
to Rental
. Пользователи должны иметь возможность добавлять фотографии в собственную аренду. Однако на фотографиях нет пользователя ForeignKey
, поэтому нет возможности напрямую узнать, кому принадлежит фотография.
Можно ли это решить с помощью некоторой обманчивости в рамках, следуя ForeignKey
, пока объект не будет найден с помощью ForeignKey
для пользователя? Или я должен сделать простой выход и дать RentalPhoto
(и все остальное "принадлежать" Rental
) своему собственному ForeignKey
к соответствующему auth.User
? Второй подход потребует ненужного избыточности, первый, вероятно, потребует ненужных накладных расходов...
Если я собираюсь сбиться с пути, пожалуйста, не стесняйтесь указывать мне в правильном направлении. Заранее благодарим за любую помощь.
Ответы
Ответ 1
Я бы просто добавил метод для каждой модели is_owned_by(user)
, и это зависит от модели, чтобы решить, принадлежит ли она этому пользователю или нет. В большинстве случаев is_owned_by
может быть общей функцией в базовом классе модели, и вы можете настроить его в особых случаях. например.
class RentalPhoto(BaseModel):
def is_owned_by(self, user):
return self.rental.is_owned_by(user)
Это достаточно общий и явный, вы будете иметь полный контроль над тем, как они себя ведут.
Чтобы добавить новое разрешение, вы можете добавить это к своим моделям, например.
class Rental(models.Model):
# ...
class Meta:
permissions = (
("can_edit_any", "Can edit any rentals"),
)
Я думаю, вместо добавления двух разрешений для any
и own
вы должны добавить только разрешение own
, поэтому у каждого объекта уже есть can_edit
, который вы можете рассматривать, поскольку пользователь может редактировать только свой объект, и если у пользователя есть разрешение can_edit_any, чем только ему разрешено редактировать все
Используя это, мы можем расширить auth, добавив пользовательский бэкэнд, например.
class PerObjectBackend(ModelBackend):
def has_perm(self, user_obj, perm, obj=None):
allowed = ModelBackend.has_perm(self, user_obj, perm)
if perm.find('any') >=0 :
return allowed
if perm.find('edit') >=0 or perm.find('delete') >=0:
if obj is None:
raise Exception("Perm '%s' needs an object"%perm)
if not obj.is_owned_by(user_obj):
return False
return allowed
Это очень быстрая реализация, на самом деле вы можете расширить объекты разрешений, чтобы проверить, нужны ли они и какие объекты или нет. permission.is_per_object
вместо того, чтобы выполнять грубый поиск строки, но это также должно работать, если у вас есть стандартные имена
Ответ 2
Можно ли это решить с помощью некоторых обманщиков в рамки, следуя за ForeignKeys пока объект не будет найден с ForeignKey для пользователя?
Я не вижу, где есть сложность:
RentalPhoto → Аренда → Пользователь
Таким образом, чтобы получить пользователя для конкретного RentalPhoto, вы вызываете что-то вроде этого в экземпляре:
photo.rental.user
После нескольких отношений за один шаг можно считать неинтеллектуальным.
Ответ 3
Если вы не хотите реализовывать свой собственный сервер разрешений, я рекомендую вам использовать https://github.com/chrisglass/django-rulez. Вы будете делать то, что хотите в намного проще.
Ответ 4
В Django docs.
В основном вы создаете собственный класс администратора для своей модели и определяете метод get_queryset
. В вашем случае это может быть что-то вроде ниже. Суперпользователь увидит все арендные платы, а владелец только его.
class RentalAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(RentalAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(owner=request.user)
Вот еще одно возможное руководство: https://code.djangoproject.com/wiki/RowLevelPermissions