Django模型多态性和外键

时间:2014-11-15 23:59:04

标签: python django django-models django-contenttypes

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

  1. 继续进行的客户,查找约会和预约约会。
  2. 可以为客户创建约会的个人注册并收取约会付款
  3. 可以为客户创建约会以进行注册,收集约会付款以及提供组织中雇用的各个提供商(即上述第2组中的用户)的链接的组织
  4. 我认为类型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的不好的做法,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
    

1 个答案:

答案 0 :(得分:2)

我认为这非常复杂。您没有客户,用户和组织。您拥有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和可用的操作。后者则不那么复杂。