【问题标题】:Django Models Polymorphism and Foreign KeysDjango 模型多态性和外键
【发布时间】:2014-11-15 23:59:03
【问题描述】:

我的应用程序中有 3 种不同类型的用户。

  1. 继续进行、查找约会和预订约会的客户。
  2. 可以创建预约供客户注册并收取预约费用的个人
  3. 可以为客户创建预约以进行注册、收取预约费用并提供指向组织中雇用的各个提供者(即上述第 2 组中的用户)的链接的组织

我看到类型 1 和 2 有一些重叠,因为它们都是个人,并且具有性别和出生日期等字段,而 3 没有,而 2 和 3 有重叠,因为它们能够创建约会和收款.

我的模型类是这样构建的:

class BaseProfileModel(models.Model):
    user = models.OneToOneField(User, related_name="profile", primary_key=True)
    phone = PhoneNumberField(verbose_name="Phone Number")
    pic = models.ImageField(upload_to=get_upload_file_name,
                            width_field="width_field",
                            height_field="height_field",
                            null=True,
                            blank=True,
                            verbose_name="Profile Picture"
                           )
    height_field = models.PositiveIntegerField(null=True, default=0)
    width_field = models.PositiveIntegerField(null=True, default=0)
    thumbnail = ImageSpecField(source='pic',
                                   processors=[ResizeToFill(180,180)],
                                   format='JPEG',
                                   options={'quality': 100})
    bio = models.TextField(
        verbose_name="About",
        default="",
        blank=True,
        max_length=800
    )
    is_provider=False

    class Meta:
        abstract = True

    def __str__(self):
        if self.user.email:
            return self.user.email
        else:
            return self.user.username

    @property
    def thumbnail_url(self):
        """
        Returns the URL of the image associated with this Object.
        If an image hasn't been uploaded yet, it returns a stock image

        :returns: str -- the image url

        """
        if self.pic and hasattr(self.pic, 'url'):
            return self.thumbnail.url
        else:
            # Return url for default thumbnail
            # Make it the size of a thumbnail
            return '/media/StockImage.png'

    @property 
    def image_url(self):
        if self.pic and hasattr(self.pic, 'url'):
            return self.pic.url
        else:
            # Return url for full sized stock image
            return '/media/StockImage.png'

    def get_absolute_url(self):
        return reverse_lazy(self.profile_url_name, kwargs={'pk': self.pk})

class BaseHumanUserModel(BaseProfileModel):
    birth_date = models.DateField(verbose_name="Date of Birth", null=True, blank=True)
    GENDER_CHOICES = (
        ('M', 'Male'),
        ('F', 'Female'),
        ('N', 'Not Specified'),
    )
    gender = models.CharField(
        max_length=1, choices=GENDER_CHOICES, blank=False, default='N', verbose_name='Gender')

    class Meta:
        abstract = True

class BaseProviderModel(models.Model):
    stripe_access_token = models.TextField(blank=True, default='')
    is_provider=True

    class Meta:
        abstract = True

    def rating(self):
        avg = self.reviews.aggregate(Avg('rating'))
        return avg['rating__avg']

    def rounded_rating(self):
        avg = self.rating()
        return round(avg * 2) / 2

    # More methods...


class IndividualProviderProfile(BaseProviderModel, BaseHumanUserModel):
    locations = models.ManyToManyField(Location, null=True, blank=True, related_name='providers')
    specialties = models.CharField(
        verbose_name = "Specialties",
        max_length=200,
        blank=True,
    )
    certifications = models.CharField(
        verbose_name = "Certifications", max_length=200,
        blank=True, null=True
    )
    self.profile_url_name = 'profiles:individual_provider_profile'

    def certifications_as_list(self):
        return ''.join(self.certifications.split()).split(',')

    def specialties_as_list(self):
        return ''.join(self.specialties.split()).split(',')


class CustomerProfile(BaseHumanUserModel):
    home_location = models.OneToOneField(
        Location,
        related_name='customer',
        null=True,
        blank=True,
        on_delete=models.SET_NULL
        )
    self.profile_url_name = 'profiles:customer_profile'

    # More methods...

class OrganizationProviderProfile(BaseProviderModel):
    website = models.URLField(blank=True)
    location = models.ForeignKey(Location)
    employees = models.ManyToManyField(IndividualProviderProfile)
    self.profile_url_name = 'profiles:organization_provider_profile'

    # More methods

我想知道一些事情:

将模型分成不同的类有意义吗?还是将提供程序制作成一个模型(无论是否单独)会更好,并且仅将某些字段留空并指定提供程序类型的字段?这对我来说似乎是一团糟。

但是,当涉及到 ForeignKey 关系时,我发现我想做的事情的方式存在问题。我希望用户能够对提供者发表评论,这需要提供者的外键。如果它们是不同的模型类,那么一个 ForeignKey 不会削减它,除非我使用 django contenttypes 框架,我并没有真正研究过。 GenericForeignKeys 似乎是要走的路,除非使用实际上只适用于两个类的 GenericForeignKey 是不好的做法。 所以我的问题是,对于以前使用过 contenttypes 框架的人(或遇到过类似困境的人),这是不好的做法,和/或我的代码最终会变得混乱,如果我这样设置我的模型并且使用通用外键将关系分配给提供者?

编辑

重新考虑后,也许这会是一个更好的结构:让我知道您对上述内容的看法:

BaseProfileModel、BaseHumanUserModel、CustomerProfileModel 保持与上面相同,并将以下内容更改为 OneToOne 关系

class ProviderDetails(models.Model):
    stripe_access_token = models.TextField(blank=True, default='')

    def rating(self):
        avg = self.reviews.aggregate(Avg('rating'))
        return avg['rating__avg']

    def rounded_rating(self):
        avg = self.rating()
        return round(avg * 2) / 2

    # More methods...


class IndividualProviderProfile(BaseHumanUserModel):
    provider_details = models.OneToOneField(ProviderDetails, related_name='profile')
    locations = models.ManyToManyField(Location, null=True, blank=True, related_name='providers')
    specialties = models.CharField(
        verbose_name = "Specialties",
        max_length=200,
        blank=True,
    )
    certifications = models.CharField(
        verbose_name = "Certifications", max_length=200,
        blank=True, null=True
    )
    self.profile_url_name = 'profiles:individual_provider_profile'

    def certifications_as_list(self):
        return ''.join(self.certifications.split()).split(',')

    def specialties_as_list(self):
        return ''.join(self.specialties.split()).split(',')


class OrganizationProviderProfile(BaseProfileModel):
    provider_details = models.OneToOneField(ProviderDetails, related_name='profile')
    website = models.URLField(blank=True)
    location = models.ForeignKey(Location)
    employees = models.ManyToManyField(IndividualProviderProfile)
    self.profile_url_name = 'profiles:organization_provider_profile'

    # More methods

【问题讨论】:

    标签: python django django-models django-contenttypes


    【解决方案1】:

    我认为这非常复杂。您没有客户、用户和组织。您拥有属于不同组织(或帐户)的具有不同权限或访问权限的 Users。您可能还会拥有至少一种其他类型的用户。站点管理员。并不意味着他们应该是不同的班级。你实现它是这样的:

    class User(models.Model):
        role = models.TextField()
    
        def is_administrator(self):
            return self.role == "admin"
    
        def can_create_appointment(self):
            return self.role == "publisher"
    

    这个角色也有可能在组织中吗?这样一个帐户的所有成员都具有相同的权限。但是你可以看到它是如何工作的。

    编辑,澄清我的推理:

    当你有一个人登录时,Django 会给你一个用户的访问权限。你真的想创造一个你必须不断考虑你有哪些类型的用户的情况吗?或者您只是希望能够使用已登录的用户并根据一些简单的规则修改可访问的 url 和可用的操作。后者要简单得多。

    【讨论】:

    • 那么你会建议如何组织一种类型的用户具有某些字段而另一种类型的用户具有其他字段的字段?
    • 也许这些字段不属于用户?也许它们是一个与用户有关系的单独模型,但对于那些不需要这种关系的用户来说,它们通常是 None 。看起来专业和认证实际上是多对多的关系?
    • 是的,其他字段可能在与用户的多对多关系中的成员资格模型中。
    猜你喜欢
    • 2015-01-29
    • 2020-11-13
    • 2012-03-16
    • 2021-10-31
    • 1970-01-01
    • 1970-01-01
    • 2012-07-22
    • 2010-11-10
    • 2020-11-26
    相关资源
    最近更新 更多