Несколько типов пользователей и рейтингов в Django

#python #django #django-models #django-rest-framework

#python #django #django-модели #django-rest-framework

Вопрос:

Я создаю серверную часть DRF с тремя типами пользователей: клиент, персональный тренер и владелец тренажерного зала. Я хочу, чтобы все поля в классе CustomUser применялись к каждому типу пользователя. Мне также нужны некоторые специфические атрибуты для каждого типа пользователя (например, фотография только для личного тренера и владельца тренажерного зала). Это правильный способ сделать это?

 # models.py

class CustomUser(AbstractUser):
    USER_TYPE_CHOICES = (
        ('customer'),
        ('personal_trainer'),
        ('gym_owner'),
    )

    user_type = models.CharField(blank=False, choices=USER_TYPE_CHOICES)
    name = models.CharField(blank=False, max_length=255)
    country = models.CharField(blank=False, max_length=255)
    city = models.CharField(blank=False, max_length=255)
    phone = models.CharField(blank=True, max_length=255)
    ratings = models.ForeignKey(Rating, on_delete=models.CASCADE, null=True)
    created_at = models.DateField(auto_now_add=True)

    def __str__(self):
        return self.name


class Customer(models.Model):
    user = models.OneToOneField(
        CustomUser, on_delete=models.CASCADE, primary_key=True)


class PersonalTrainer(models.Model):
    user = models.OneToOneField(
        CustomUser, on_delete=models.CASCADE, primary_key=True)
    photo = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)


class GymOwner(models.Model):
    user = models.OneToOneField(
        CustomUser, on_delete=models.CASCADE, primary_key=True)
    photo = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
  

У меня также есть модель рейтингов. Я хочу иметь возможность оставлять рейтинг в качестве клиента личному тренеру или тренажерному залу. Каждый рейтинг будет иметь отношение один к одному с его владельцем и его целью. Однако я не уверен, как я могу установить отношения ..?

 # models.py

class Rating(models.Model):
    STAR_CONVERSION = (
        (1, 'One Star'),
        (2, 'Two Stars'),
        (3, 'Three Stars'),
        (4, 'Four Stars'),
        (5, 'Five Stars'),
    )

    rating = models.PositiveSmallIntegerField(choices=STAR_CONVERSION)
    caption = models.TextField(blank=True)

    owner = models.OneToOneField(Customer, on_delete=models.CASCADE)

    # I want a target as a one to one relation to either PersonalTrainer or GymOwner
    target = models.OneToOneField(*either personal trainer or gym owner*)
  

Ответ №1:

Вам нужно сделать оба owner и target ForeignKey вместо OneToOneField a. С последним вы могли бы иметь только один рейтинг для каждого клиента и один для каждого поставщика, что было бы немного ограничительно :).

Для PersonalTrainer и GymOwner вам необходимо наследование модели. Родительская модель должна быть либо абстрактным классом (с данными, сохраненными в таблицах отдельных дочерних моделей), либо (предпочтительно в этом случае, поскольку поля обеих моделей одинаковы) данные будут сохранены в родительской модели (например, Provider ), в то время как дочерние модели будут прокси-моделями, основанными на данных родительской модели, но обеспечивающими другое поведение, где это уместно.

В документах Django довольно много говорится о различных вариантах наследования.

 class Provider(models.Model):
    user = models.OneToOneField(
        CustomUser, on_delete=models.CASCADE, primary_key=True)
    photo = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)

class PersonalTrainer(Provider):
    class Meta:
        proxy = True

class GymOwner(Provider):
    class Meta:
        proxy = True

class Rating(models.Model):   
    # ...
    owner = models.ForeignKey(Customer, on_delete=models.CASCADE)
    target = models.ForeignKey(Provider, on_delete=models.CASCADE)
  

Комментарии:

1. Поскольку owner и target являются полями FK, возможно, придется подумать о введении некоторого unique_together ограничения. Если только не требуется разрешить одному и тому же клиенту «проверять-бомбить» одного и того же поставщика, то есть. Даже если рейтинг будет дополнительно привязан к дате или определенному курсу, я чувствую, что разрешить более одного рейтинга для каждого клиента, поставщика и контекста звучит неправильно.

2. @shmee Это важное соображение, да. Если вам нужен только один отзыв на пару клиент / поставщик, unique_together достаточно; для других ограничений (например, один отзыв в течение определенного времени и т.д.) Вам нужен либо код Python, либо дополнительные поля модели.