Django循环模型参考

时间:2011-12-11 19:21:55

标签: python django

我开始在一个小型足球联赛管理网站(主要用于学习目的)上工作,并且不能围绕Django模型关系。为简单起见,假设我有两种类型的对象 - 玩家和团队。当然,玩家属于一个团队,因此是玩家模型中的ForeignKey(团队)。 我走了:

class Team(models.Model):
    name = models.CharField()
class Player(models.Model):
    name = models.CharField()
    team = models.ForeignKey(Team)

然后我希望每支球队都有一名队长,这将是球员模型中的一名外国球员(球员)。但这会产生循环依赖。 虽然我的Django体验有限,但它似乎是一个简单的问题,虽然我无法弄清楚我在概念上做错了什么。

8 个答案:

答案 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)