Какой шаблон использовать для модели с разными вариациями? Общий FK, мульти-таблица, наследование, другие? - PullRequest
0 голосов
/ 24 мая 2019

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

У меня будут следующие модели: Сообщения, Группы, Пользователи.

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

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

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

На данный момент я выбрал Альтернатива 4 - наследование нескольких таблиц

class Group(models.Model):
    name = models.CharField(max_length=64)
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='_groups')
    members = models.ManyToManyField(
        settings.AUTH_USER_MODEL)

    def __str__(self):
        return f'{self.name} -- {self.created_by}'

    def save(self, *args, **kwargs):
        # https://stackoverflow.com/a/35647389/1294405
        created = self._state.adding
        super(Group, self).save(*args, **kwargs)
        if created:
            if not self.members.filter(pk=self.created_by.pk).exists():
                self.members.add(self.created_by)


class Post(models.Model):
    content = models.TextField(blank=True, default='')
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="%(app_label)s_%(class)s_posts",
        related_query_name="%(app_label)s_%(class)ss")

    # class Meta:
    #     abstract = True

    def __str__(self):
        return f'{self.content} -- {self.created_by}'


class PostImage(models.Model):
    image = models.ImageField(upload_to=unique_upload)
    post = models.ForeignKey(
        Post, related_name='images', on_delete=models.CASCADE)

    def __str__(self):
        return '{}'.format(self.image.name)

class UserPost(models.Model):
    post = models.OneToOneField(
        Post, null=True, blank=True, related_name='_uPost', on_delete=models.CASCADE)


class GroupPost(models.Model):
    post = models.OneToOneField(
        Post, null=True, blank=True, related_name='_gPost', on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)

Для выполнения некоторых специальных фильтров, например:

Фильтр определенной групповой записи

Post.objects.filter(_gPost__group=group)

Фильтр определенной пользовательской записи

Post.objects.filter(created_by=user) # exclude groups with _gPost__isnull=False

Создать сообщение для пользователя / группы

p = Post.objects.create(...)
up = UserPost.objects.create(post=p)
gp = GroupPost.objects.create(post=p)

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

Итак, Generic ForeignKey - то место, которое можно использовать здесь, или текущий многостоловый подход.Я попытался перейти с наследованием с abstract = True, но это не сработало, так как мне нужен внешний ключ для базовой модели поста.Даже без абстракции я получил ссылку на внешний ключ, но фильтр стал разочаровывать.

Редактировать:

Пока что при фильтрации нужно учитывать только странные проблемы (но не совсем), я должен быть явноИсключить некоторые поля, чтобы получить то, что я хочу, используя только .filter(created_by=...), чтобы получить все другие промежуточные таблицы.Пост фильтра, исключающий все другие планшеты, потребует Post.objects.filter(_uPost__isnull=True, _gPost__isnull=True, _**__isnull=True), что может оказаться утомительным.

Ответы [ 2 ]

0 голосов
/ 25 мая 2019

На данный момент я буду придерживаться моего текущего паттерна.

Дополнительное чтение для всех, кто заинтересован.

https://www.slideshare.net/billkarwin/sql-antipatterns-strike-back/32-Polymorphic_Associations_Of_course_some

0 голосов
/ 25 мая 2019

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

Еще один подход - переместить внешние клавиши Group и Event в модель Post и позволить им быть NULL / None, если сообщение не было.не опубликовано в группе или событии.Это немного повышает производительность и делает фильтры более осмысленными, но я бы избегал такого подхода, если вы думаете, что сообщения могут быть добавлены ко многим другим моделям в будущем (поскольку вам придется продолжать добавлять все больше и больше внешних ключей).

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