我是一个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)
这是对的吗?实际目的有很大的不同吗?这是每种方法的利弊吗?
答案 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)