какой тип дизайна модели я должен использовать для добавления ролей Пользователям таким образом, чтобы он работал в Django и в администраторе Django - PullRequest
0 голосов
/ 27 ноября 2010

Я использую систему аутентификации Djangos по умолчанию (django.contrib.auth) и хочу добавить «роли» своим пользователям таким образом, чтобы Django Admin мог с ней работать.

Пример:

  • Пользователь может быть сотрудником , учителем , учеником и / или родителем
  • Если пользователю назначена роль, он получит разрешения (например, staffmembers может войти в систему администратора Django)
  • Некоторые роли могут иметь дополнительные поля (например, parent имеет отношение по крайней мере с одним учеником , и у каждого ученика есть поле со своей группой классов
  • Не каждая роль имеет дополнительные поля
  • A родитель может быть учителем или сотрудником и наоборот
  • A студент не может иметь другую роль

Существуют всевозможные (обычные) способы достижения вышеуказанного в модели. Django поддерживает многие из них, но администратор Django - нет. Администратор Django обладает множеством полезных функций, поэтому я хотел бы использовать его (но я все больше и больше боюсь, что это будет невозможно).

Сначала я подумал о следующей модели:

class ExtendedUser(contrib.auth.models.User):
    """
       For the ease of use I inherit from User. I might
       want to add methods later
    """
    pass

class StaffMember(models.Model):
    """
       A staffmember is a co-worker of the organisation
       and has permissions to make changes to (parts of)
       the system.

       For now the staffmember only has methods
    """
    user = models.OneToOneField(ExtendedUser, related_name="staff_member")

class Student(models.Model):
    """
      A student can participate in some courses
    """
    user = models.OneToOneField(ExtendedUser, related_name="student")

class Teacher(models.Model):
   user = models.OneToOneField(ExtendedUser, related_name="teacher")
   subjects = models.ManyToManyField(..)

class Parent(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="parent")
    children = models.ManyToManyField(Student, related_name=parents")

Это работает в Django (и во многих других основанных на MVC фреймворках). Но я не могу найти правильный способ отобразить вышесказанное в админке.

В идеале я хотел бы добавить пользователя, а затем в окне изменения пользователя добавить разные роли. Сначала я подумал, что мог бы использовать Inlines:

class StudentInlineAdmin(admin.StackedInline):
    model = Student
    extra = 0
    template = 'accounts/admin/role.html'

Затем я внесу небольшие изменения во встроенный шаблон, чтобы добавить кнопку редактирования пользователю с надписью «Добавить роль учащегося». Как только мы нажимаем кнопку, мы отображаем форму и добавляем роль пользователя. Не идеально, но работает.

Очень жаль, что для штатных сотрудников нет полей для добавления в встроенную форму. Таким образом, невозможно вызвать свойство has_changed для встроенных форм. Это приводит к тому, что новая роль не сохраняется в базе данных.

Чтобы решить эту последнюю проблему, я немного взломал и добавил пустое поле формы в пустые пользовательские роли, а затем скрыл это поле с помощью JS. Это вызвало has_changed.

Тем не менее, это не сработает, так как ни одна из моих встроенных моделей не будет сохранена во время последующих тестов.

Так что я думаю, что просто делаю это неправильно. Я много гуглял и обнаружил, что многие люди сталкиваются с такими же проблемами. Наиболее подходящим для моей ситуации был http://www.mail-archive.com/django-users@googlegroups.com/msg52867.html. Но все же это решение не дает мне простого способа реализовать админа.

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

Еще одна вещь, о которой я подумал, - это попытка «Добавить студента» вместо добавления пользователя и назначения ему роли. Это хорошо работает в админке, если вы просто наследуете пользователя:

class StudentUser(auth.models.User):
  pass

Но здесь две проблемы. Сначала невозможно быть сотрудником и учителем. Во-вторых, он не работает в остальной части Django, поскольку объект запроса возвращает объект User, для которого невозможно запросить объект Student, Parent, Staffmember. Единственный способ получить один из них - создать новый объект Student на основе объекта User.

Итак, вот вопрос: какой тип дизайна модели я должен использовать, чтобы добавлять роли пользователям таким образом, чтобы он работал в Django и в администраторе Django?

С уважением, Wout

1 Ответ

2 голосов
/ 27 ноября 2010

В дальнейшем я предполагаю, что вы не хотите изменять администратора или делать копию django.contrib.admin и взламывать ее по желанию.

Пользователь может быть сотрудником, учителем, учеником и / или родителем

Вы можете сохранить это в профиле пользователя . Наследование от User также будет работать, но вместо использования User.get_profile() вам нужно вручную отобразить с User на ExtendedUser.

Если пользователю назначена роль, он получит разрешения (например, штатные сотрудники могут войти в администратор Django)

В этом конкретном случае вы не можете использовать автоматическое назначение на основе ролей. Возможность доступа администратора к администратору определяется полем is_staff в его записи User.

Самым автоматическим способом, который я могу придумать, является создание «Обновления разрешений» команда администратора , которая будет обновлять поля администратора, например is_staff, и разрешения, основанные на роли, установленной в профиле пользователя. Кстати, хотя это и не полностью «автоматический», это денормализация, которая может улучшить производительность.

В некоторых ролях могут быть дополнительные поля (например, у родителя есть связь хотя бы с одним учеником, а у каждого ученика есть поле со своей группой классов

Не каждая роль имеет дополнительные поля

Родитель может быть учителем или сотрудником, и наоборот

Студент не может иметь другую роль

Читайте о проверке формы . Вот где вы можете применять эти правила.

В вашей модели я бы порекомендовал вам изменить связанные имена ваших однозначных полей:

class StaffMember(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="as_staff_member")

class Student(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="as_student")

class Teacher(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="as_teacher")

class Parent(models.Model):
    user = models.OneToOneField(ExtendedUser, related_name="as_parent")

Поскольку theUser.as_teacher намного яснее, чем theUser.teacher (который я бы прочитал как "учитель пользователя").

Это работает в Django (и во многих других основанных на MVC фреймворках). Но я не могу найти правильный способ отобразить вышесказанное в админке.

В каждой роли у администратора будет одна таблица. Нет никакой причудливой функции «нижняя половина страницы редактирования будет перерисовываться при смене ролей». Если вы хотите этого, вам нужно написать своего администратора.

Администратор Django великолепен, но он не пытается быть всем для всех. У меня есть настройки на основе ролей, подобные вашей (за исключением того, что сами роли хранятся в таблице), и администратор работает нормально, если немного неуклюже. Основная идея заключается в том, что если администратор не достаточно хорош, то вы должны писать свои собственные взгляды.

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

Встроенные группы - это не то, что вам нужно.

Еще одна вещь, о которой я думал, это попытка «Добавить студента» вместо добавления пользователя и назначения ему роли. [...] Сначала невозможно быть сотрудником и учителем.

«Субклассинг» является более строгим, один к одному. Я думаю, что ваша начальная модель лучше.

Во-вторых, он не работает в остальной части Django, поскольку объект запроса возвращает объект User, для которого невозможно запросить объект Student, Parent, Staffmember. Единственный способ получить один из них - создать новый объект Student на основе объекта User.

Нет, вместо этого вы находите объект Student, используя автоматически сгенерированный id из объекта User:

try:
   theStudent = Student.objects.get(user_ptr_id=theUser.id)
except Student.DoesNotExist:
   # That user isn't a student; deal with it here.

Если вы собираетесь использовать администратора, Я думаю, что вам придется жить с двухэтапным процессом добавления ExtendedUser, затем добавления Student или любых других записей. для них.

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

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