Django задает параметры конфиденциальности для каждой модели

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

Хорошо, у меня есть модель User. Я хочу, чтобы пользователь мог контролировать конфиденциальность каждого поля своего профиля (может быть gender, education, interests и т.д.).

Параметры конфиденциальности не должны ограничиваться только частными или общедоступными, но как описательными как

  • public
  • друзья
  • только я
  • friend Список 1 (User.friendlist.one)
  • friend Список 2 (User.friendlist.two)
  • friend Список 3 (User.friendlist.three)
  • еще один список, который пользователь может создать.

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

так что если у меня есть UserModel,

class User(models.Model):
    name = models.CharField()
    email = models.EmailField()
    phone = models.CharField()

Как настроить настройку конфиденциальности здесь? Я использую postgres, могу ли я отобразить поле JSON или Hstore даже ArrayField?

Какое лучшее решение, с которым люди обычно сталкивались с Django с такой же проблемой?

обновление:

У меня есть n полей модели. Я действительно хочу сохранить настройки конфиденциальности каждого экземпляра самостоятельно или каким-либо другим удобным способом.

Ответ 1

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

Итак, мой выбор состоял в том, чтобы пойти с Postgres JSONField или HStoreField. Поскольку Django имеет хорошую поддержку для postgres freatures, я нашел эти очки для выбора, который я сделал.

  • JSON/HashStore можно запросить с помощью Django ORM.
  • Конфигурации - это простой JSON/HashStore, которые легко редактировать и поддерживать, чем разрешения и отношения.
  • Я обнаружил, что время запроса к базе данных больше с разрешениями, чем с JSON/HStore. (хиты выше с разрешениями)
  • Добавление и проверка разрешений для каждого поля сложный, чем добавление/проверка JSON.
  • В какой-то момент в будущем , если приходит более простое или бесплатное решение,, я могу перейти на него, имея всю конфигурацию в одном поле.

Итак, мне нужно было выбрать конфигурационную модель.

class UserConfiguration(models.Model):
    user = # link to the user model
    configuration = #either an HStore of JSONFeild

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

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

Обновление

У меня есть более подробные запросы к базе данных. В предыдущей реализации отношения между пользователями, где итерации, которые были рассчитаны примерно на 1-2 мс, изменили эту реализацию на .value_list('relations', flat=True), сократили время запроса до 400-520 мкс.

Ответ 2

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

Я бы посоветовал вам отделить объекты конфиденциальности от UserModel, чтобы не испортить ваши данные пользователей вместе с этими параметрами. Чтобы свести к минимуму количество запросов к базе данных, используйте djangos select_related и prefetch_related.

Требования, определенные вами IMO, приводят к набору объектов, связанных с конфиденциальностью, которые привязаны к UserModel. django.contrib.auth - это хороший момент для начала. Он будет расширяться. Прочитайте документы по этой теме.

Если вы ожидаете большого количества пользователей и, следовательно, еще более большое количество групп, вы можете захотеть написать разрешения, разрешенные для одного пользователя на сеансе на основе redis, чтобы иметь возможность быстро их загружать при каждой загрузке страницы.

UPDATE:

Я подумал немного больше о ваших требованиях и пришел к выводу, что вам нужно разрешение каждого объекта, реализованное в django-guardian. Сначала вы должны начать читать свои образцы и. Они создают это поверх django.contrib.auth, но не зависят от него, что делает его также полезным с пользовательскими реализациями, которые следуют за интерфейсами в django.contrib.auth.

Ответ 3

Как насчет этого?

class EditorList(models.Model):
    name = models.CharField(...)
    user = models.ForeignKey(User)
    editor = models.ManyToManyField(User)

class UserPermission(models.Model):
    user = models.ForeignKey(User)
    name = models.BooleanField(default=False)
    email = models.BooleanField(default=False)
    phone = models.BooleanField(default=False)
    ...
    editor = models.ManyToManyField(User)
    editor_list = models.ManyToManyField(EditorList)

Если пользователь хочет предоставить разрешения "электронной почты" для public, то она создает UserPermission с editor=None и editor_list=None и email=True.

Если она хочет разрешить пользователю "rivadiz" редактировать электронную почту, она создает UserPermission с editor='rivadiz' и email=True.

Если она хочет создать список друзей, которые могут редактировать свой телефон, то она создает и заполняет EditorList, называемую "my_friends", а затем создает UserPermission с editor_list='my_friends' и phone=True

Затем вы можете запросить всех пользователей, у которых есть разрешение на редактирование любого поля для любого пользователя. Вы можете определить некоторые свойства в модели User, чтобы легко проверить, какие поля доступны для редактирования, с учетом User и editor. Сначала вам нужно получить все EditorLists, к которому принадлежит редактор, затем сделать что-то вроде

perms = UserPermissions.objects.filter(user=self).filter(Q(editor=editor) | Q(editor_list=editor_list))

Ответ 4

Это будет намного более громоздким без отдельной модели разрешений. Тот факт, что вы можете связать заданное поле отдельного профиля пользователя с более чем одним списком друзей, подразумевает таблицу Many to Many, и вам лучше просто позволить Django обрабатывать это для вас.

Я думаю что-то более похожее:

class Visibility(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    field = models.CharField(max_length=32)
    public = models.BooleanField(default=False)
    friends = models.BooleanField(default=False)
    lists = models.ManyToManyField(FriendList)

    @staticmethod
    def visible_profile(request_user, profile_user):
        """Get a dictionary of profile_user profile, as
           should be visible to request_user..."""

(Я оставлю детали такого метода, как упражнение, но это не слишком сложно.)

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

Ключевым преимуществом таблицы M2M является то, что она будет самоподдерживаться, если пользователь или любой список друзей будут удалены - за одним исключением. Идея в этой схеме заключается в том, что без каких-либо записей Visibility все данные являются частными (чтобы все могли видеть ваше имя, вы добавили бы запись видимости с пользователем = (самостоятельно), поле = "имя" и public = True. Поскольку запись Видимости, где public = False, friends = False и lists = [] бессмысленна, я проверял бы эту ситуацию после того, как пользователь ее отредактирует и полностью удалит эту запись.

Еще одна действительная стратегия - иметь две специальные записи FriendList: один для "public", а другой для "всех друзей". Это упрощает модель видимости совсем немного за счет немного большего количества кода в другом месте.

Ответ 5

Прежде всего, на мой взгляд, вы должны пойти на несколько моделей и сделать запросы быстрее, как уже упоминалось в других ответах, вы можете использовать кеширование или select_related или prefetch_related в соответствии с вашей usecase.

Итак, вот мое предлагаемое решение:

Модель пользователя

class User(models.Model):
    name = models.CharField()
    email = models.EmailField()
    phone = models.CharField()
    ...
    public_allowed_read_fields = ArrayField(models.IntegerField())
    friends_allowed_read_fields = ArrayField(models.IntegerField())
    me_allowed_read_fields = ArrayField(models.IntegerField())
    friends = models.ManyToManyField(User)
    part_of = models.ManyToManyField(Group, through=GroupPrivacy)

Модель группы (список друзей)

class Group(models.Model):
    name = models.CharField()

Через модель

class GroupPrivacy(models.Model):
    user = models.ForeignKey(User)
    group = models.ForeignKey(Group)
    allowed_read_fields = ArrayField(models.IntegerField())

Отображение полей модели пользователя для целых чисел

USER_FIELDS_MAPPING = (
    (1, User._meta.get_field('name')),
    (2, User._meta.get_field('email')),
    (3, User._meta.get_field('phone')),
    ...
)

КАК ЭТО ПОМОГАЕТ?

  • для каждого из public, friends и me, вы можете иметь поле в самой модели пользователя, как уже упоминалось выше, т.е. public_allowed_read_fields, friends_allowed_read_fields и me_allowed_read_fields соответственно. Каждое из этого поля будет содержать список целых чисел, сопоставленных с ними внутри USER_FIELDS_MAPPING (подробно объясняется ниже)

  • для friend_list_1, у вас будет группа с именем friend_list_1. Теперь дело в том, что пользователь хочет показать или скрыть определенный набор полей в этом списке друзей. То, что в игру входит модель through, GroupPrivacy. Используя эту модель, вы определяете отношение M2M между пользователем и группой с некоторыми дополнительными свойствами, которые являются уникальными для этого отношения. В этой модели GroupPrivacy вы можете увидеть поле allowed_read_fields, оно используется для хранения массива целых чисел, соответствующих тем, которые находятся в USER_FIELDS_MAPPING. Итак, скажем, для группы friend_list_1 и пользователя A, allowed_read_fields= [1,2]. Теперь, если вы сопоставляете это с USER_FIELDS_MAPPING, вы узнаете, что пользователь A хочет показывать только имя и адрес электронной почты друзьям в этом списке. Аналогично разные пользователи в группе friend_list_1 будут иметь разные значения в allowed_read_fields для своего соответствующего экземпляра модели GroupPrivacy.

Это будет похоже на несколько групп.