Добавление разрешений на объект для администратора django - PullRequest
14 голосов
/ 09 сентября 2010

Фон

Я разрабатываю приложение django для сайта по аренде жилья. Он будет иметь два типа пользователей, арендаторов и управляющих недвижимостью.

Я бы хотел, чтобы управляющие недвижимостью могли управлять своей арендной собственностью в админке django. Однако они должны иметь возможность управлять только своими собственными свойствами.

Я понимаю, что администратор 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 модели.

Это звучит пока осуществимо, или я должен идти в другом направлении?


Осложнения

Теперь вот сложная часть; Я не уверен, как справиться с этим. Допустим, у Rental может быть много «RentalPhotos». RentalPhoto имеет от ForeignKey до Rental. Пользователи должны иметь возможность добавлять фотографии в свою аренду. Тем не менее, на фотографиях нет пользователя ForeignKey, поэтому невозможно напрямую узнать, кому принадлежит фотография.

Может ли это быть решено с помощью некоторой хитрости в рамках, следуя ForeignKey с, пока объект не будет найден с ForeignKey для пользователя? Или я должен выбрать легкий путь и дать RentalPhoto (и все остальное, «принадлежащее» Rental) свое собственное ForeignKey соответствующему auth.User? Второй подход вызвал бы ненужную избыточность, первый, вероятно, потребовал бы ненужных накладных расходов на обработку ...

Если я полностью сбился с пути, пожалуйста, не стесняйтесь указывать мне в правильном направлении. Заранее благодарен за любую помощь.

Ответы [ 4 ]

22 голосов
/ 11 сентября 2010

Я бы просто добавил метод к каждой модели 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, но только ему разрешено редактировать все

. Используя это, мы можем расширить аутентификацию, добавив пользовательский бэкэнд, например:

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 вместо выполнения поиска по грубой строке, но это также должно работать, если у вас есть стандартные имена

1 голос
/ 13 сентября 2011

Если вы не хотите реализовывать свой собственный бэкэнд разрешений, я рекомендую вам использовать https://github.com/chrisglass/django-rulez Вы будете делать то, что хотите, намного проще.

1 голос
/ 09 сентября 2010

Может ли это быть решено обманом в рамки, следующие ForeignKeys пока объект не найден с ForeignKey к пользователю?

Я не вижу, где есть хитрость, необходимая: RentalPhoto -> Rental -> Пользователь Таким образом, чтобы получить пользователя для конкретного RentalPhoto, вы должны вызвать что-то вроде этого в следующем случае:

photo.rental.user

Следование нескольким отношениям за один шаг можно считать нехитростью.

0 голосов
/ 25 апреля 2017

Это в 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...