关于python:Django模型多态性和外键

Django Models Polymorphism and Foreign Keys

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

  • 继续、查找约会和预订约会的客户。
  • 可以为客户创建预约以注册和收取预约付款的个人
  • 可以为客户创建预约以注册、收取预约付款,并提供与组织中雇用的个别提供商(即上面组2中的用户)的链接的组织。
  • 我看到类型1和类型2有一些重叠,因为它们都是个人,并且具有诸如性别和出生日期之类的字段,而类型2和类型3有重叠,因为它们能够创建约会和收取付款。

    我的模型类结构如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    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就不会剪切它,除非我使用django contenttypes框架,我还没有深入研究过这个框架。GenericForeignKeys似乎是一种可行的方法,除非这是一种糟糕的做法,即使用一个只适用于两个类的GenericForeignKey。所以我的问题是,对于以前使用过ContentTypes框架的人(或者有过类似困境的人),这是一个糟糕的实践,和/或如果我建立这样的模型并使用通用外键将关系分配给提供者,我的代码最终会变得混乱吗?

    编辑

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

    保持baseProfileModel、baseHumanUserModel和customerProfileModel与上面相同,并将以下内容更改为具有一对一关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    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


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

    1
    2
    3
    4
    5
    6
    7
    8
    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和可用的操作。后者要复杂得多。