我开始在一个小型足球联赛管理网站(主要用于学习目的)上工作,并且不能围绕Django模型关系。为简单起见,假设我有两种类型的对象 - 玩家和团队。当然,玩家属于一个团队,因此是玩家模型中的ForeignKey(团队)。 我走了:
class Team(models.Model):
name = models.CharField()
class Player(models.Model):
name = models.CharField()
team = models.ForeignKey(Team)
然后我希望每支球队都有一名队长,这将是球员模型中的一名外国球员(球员)。但这会产生循环依赖。 虽然我的Django体验有限,但它似乎是一个简单的问题,虽然我无法弄清楚我在概念上做错了什么。
答案 0 :(得分:34)
正如您在the docs中看到的那样,正是由于这个原因,可以将外部模型指定为字符串。
team = models.ForeignKey('Team')
答案 1 :(得分:9)
这是解决此问题的另一种方法。 我创建了一个存储玩家和团队之间关系的附加表,而不是创建循环依赖。所以最后它看起来像这样:
class Team(Model):
name = CharField(max_length=50)
def get_captain(self):
return PlayerRole.objects.get(team=self).player
class Player(Model):
first_name = CharField(max_length=50)
last_name = CharField(max_length=50, blank=True)
def get_team(self):
return PlayerRole.objects.get(player=self).team
PLAYER_ROLES = (
("Regular", "Regular"),
("Captain", "Captain")
)
class PlayerRole(Model):
player = OneToOneField(Player, primary_key=True)
team = ForeignKey(Team, null=True)
role = CharField(max_length=20, choices=PLAYER_ROLES, default=PLAYER_ROLES[0][0])
class Meta:
unique_together = ("player", "team")
存储方面的效率可能略低于建议的解决方法,但它避免了循环依赖性并保持数据库结构清晰明了。 欢迎提出意见。
答案 2 :(得分:6)
您可以在尚未定义的模型的外键中使用完整的应用程序标签,并使用related_name来避免名称冲突:
class Team(models.Model):
captain = models.ForeignKey('myapp.Player', related_name="team_captain")
class Player(models.Model):
team = models.ForeignKey(Team)
答案 3 :(得分:2)
这就是你要找的东西:
class Team(models.Model):
name = models.CharField()
captain = models.ForeignKey('Player')
class Player(models.Model):
name = models.CharField()
team = models.ForeignKey(Team)
答案 4 :(得分:1)
拥有一个队长表,其中包含球员/球队列以及其他表格,并让队长成为球队的一种方法:
class Team(models.Model):
name = models.CharField()
def captain(self):
[search Captain table]
return thePlayer
class Player(models.Model):
name = models.CharField()
team = models.ForeignKey(Team)
class Captain(models.Model):
player = models.ForeignKey(Player)
team = models.ForeignKey(Team)
你必须检查你在同一个团队中从来没有超过一名队长......但是你没有这样的循环引用。你也可能最终得到一名不属于他被标记为队长的队长。所以这有一些陷阱。
答案 5 :(得分:1)
虽然对同一模型有两个引用没有任何问题,但也许有更好的方法来解决这个特定的问题。
在Team
模型中添加一个布尔值,以识别玩家+团队组合作为队长:
class Team(models.Model):
player = models.ForeignKey(Player)
name = models.CharField(max_length=50)
is_captain = models.BooleanField(default=False)
寻找团队的队长:
Team.objects.filter(is_captain=True)
就我个人而言,我不喜欢这种方法,因为搜索语义没有意义(即“团队”不是“队长”)。
另一种方法是识别每位球员的位置:
class Player(models.Model):
name = models.CharField(max_length=50)
position = models.IntegerField(choices=((1,'Captain'),(2,'Goal Keeper'))
jersey = models.IntegerField()
def is_captain(self):
return self.position == 1
class Team(models.Model):
name = models.CharField(max_length=50)
player = models.ForeignKey(Player)
def get_captain(self):
return self.player if self.player.position == 1 else None
搜索时会更有意义:
Player.objects.filter(position=1)
(返回所有队长)
Team.objects.get(pk=1).get_captain()
(返回此队的队长)
在任何一种情况下,你都必须做一些预先保存检查,以确保特定位置只有一个玩家。
答案 6 :(得分:0)
这里的答案都不是那么好 - 创建循环引用从来都不是一个好主意。想象一下,如果您的数据库崩溃并且您必须从头开始创建 - 在创建团队之前如何创建播放器,反之亦然?在这里看一个问题:ForeignKey field with primary relationship几天前我问过一个问题。在Player上放置一个指定队长的布尔值,并放置一些预保存钩子,验证每个团队必须拥有一个且只有一个队长。
答案 7 :(得分:0)
现在可以使用AppConfig功能来导入模型:
Retailer = apps.get_model('retailers', 'Retailer')
retailer = Retailer.objects.get(id=id)