拥有两个模型而不是一个模型有什么好处?

时间:2015-03-28 17:31:55

标签: python django django-orm

我是一个Django模型

class Person(models.Model):
    name = models.CharField(max_length=50)
    team = models.ForeignKey(Team)

团队模型

class Team(models.Model):
    name = models.CharField(max_length=50)

然后,我想添加一个'教练'属于与人的一对一关系的财产。如果我没有错,我有两种方法可以做到。

第一种方法是将字段添加到团队:

class Team(models.Model):
    name = models.CharField(max_length=50)
    coach = models.OneToOneField(Person, related_name='master')

第二个是创建一个新模型:

class TeamCoach(models.Model):
    team = models.OneToOneField(Team)
    coach = models.OneToOneField(Person)

这是对的吗?实际目的有很大的不同吗?这是每种方法的利弊吗?

2 个答案:

答案 0 :(得分:2)

我会说 NEITHER ,因为每个都有团队,并且每个团队都有教练,它相当冗余,并且有点不必要。

最好直接在中添加一个名为类型的字段,这样更干净,更直接,例如:

class Person(models.Model):
    # use _ if you care about i18n
    TYPES = ('member', 'member',
             'coach', 'coach',)

    name = models.CharField(max_length=50)
    team = models.ForeignKey(Team)
    type = models.CharField(max_length=20, choices=TYPES)

虽然我会认真考虑将重构为更通用,然后让团队 ManyToMany 设为 ...在这种情况下,您可以在其他领域重新使用,例如啦啦队。

class Person(models.Model):
    # use _ if you care about i18n
    TYPES = ('member', 'member',
             'coach', 'coach',)

    name = models.CharField(max_length=50)
    type = models.CharField(max_length=20, choices=TYPES)

class Team(models.Model):
    name = models.CharField(max_length=50)
    member = models.ManyToManyField(Person, related_name='master')

让你的模型更通用和干燥,它们应该易于管理,并且不能紧密耦合到某些领域(除非绝对必要),然后模型更具有未来性,并且不会轻易落入迁移噩梦。

希望这有帮助。

答案 1 :(得分:1)

我不能同意 @Anzel 那么容易,因为问题的名称是

  

拥有两个模型而不是一个模型有什么好处?

我会试着给我两分钱。但在我开始之前,我想从docs中添加一些引号。

  

哪个模型具有ManyToManyField并不重要,但您应该这样做   只把它放在其中一个模型中 - 而不是两者。

     

通常,ManyToManyField实例应该放在对象中   将在表格上进行编辑。在上面的例子中,浇头是在   比萨(而不是Topping有比萨饼ManyToManyField)因为   想想比萨饼有馅料的披萨更自然   在多个比萨饼上打顶。它的设置方式就是披萨   表格会让用户选择浇头。

基本上,在创建M2M关系时,您应该首先考虑的是(您的TeamCoach模型就是那个,但在一秒钟内更多关于这一点)哪一个是持有关系的对象。什么更适合您的问题 - 在创建团队时为团队选择教练,或在创建团队时为团队选择团队?如果你问我,我会更喜欢第二个变体并将teams保留在Person类中。

现在让我们转到文档的下一部分

多对多关系中的额外字段

  

当你只处理简单的多对多关系时   作为混合和匹配的比萨饼和浇头,标准的ManyToManyField   是你所需要的全部。但是,有时您可能需要关联数据   与两个模型之间的关系。

     

例如,考虑跟踪音乐剧的应用程序的情况   音乐家所属的团体。有多对多的关系   在一个人和他们所属的团体之间,所以你   可以使用ManyToManyField来表示这种关系。然而,   关于您可能想要的会员资格有很多细节   收集,例如该人加入该组的日期。

     

对于这些情况,Django允许您指定将要使用的模型   用于管理多对多关系。你可以放   中间模型上的额外字段。中间模型是   使用through参数与ManyToManyField相关联   指向将充当中间人的模型。

这实际上是您问题的答案,拥有一个中间模型可以让您存储有关该集合的其他数据。考虑下一季教练转移到另一支球队的情况,如果你只是更新M2M关系,你将失去他过去执教的过去球队的轨迹。或者你永远无法回答在XXX年担任该队教练的问题。因此,如果您需要更多数据,请使用中间模型。这也是@Anzel出错了,type字段是该中间模型的附加数据,它的位置必须在其中。

现在我可以创建关系:

class Person(models.Model):
    name = models.CharField(max_length=50)
    teams = models.ManyToManyField('Team', through='TeamRole')


class Team(models.Model):
    name = models.CharField(max_length=50)


class TeamRole(models.Model):
    COACH = 1
    PLAYER = 2
    CHEERLEADER = 3
    ROLES = (
        (COACH, 'Coach'),
        (PLAYER, 'Player'),
        (CHEERLEADER, 'Cheerleader'),
    )
    team = models.ForeignKey(Team)
    person = models.ForeignKey(Person)
    role = models.IntegerField(choices=ROLES)
    date_joined = models.DateField()
    date_left = models.DateField(blank=True, null=True, default=None)

我该如何查询?好吧,我可以使用role来获取我正在寻找的persons类型,我也可以使用date_left字段来获取当前persons参与者那个团队现在。以下是一些示例方法:

class Person(models.Model):
    #...

    def get_current_team(self):
        return self.teams.filter(teamrole__date_left__isnull=True).get()


class Team(models.Model):
    #...

    def _get_persons_by_role(self, role, only_active):
        persons = self.person_set.filter(teamrole__role=role)
        if only_active:
            return persons.filter(teamrole__date_left__isnull=True)
        return persons

    def get_coaches(self, only_active=True):
        return self._get_persons_by_role(TeamRole.COACH, only_active)

    def get_players(self, only_active=True):
        return self._get_persons_by_role(TeamRole.PLAYER, only_active)