【问题标题】:Django ManyToManyField ordering using through使用通过的 Django ManyToManyField 排序
【发布时间】:2010-10-08 20:07:37
【问题描述】:

以下是我的模型设置的简要说明:

class Profile(models.Model):     
    name = models.CharField(max_length=32)

    accout = models.ManyToManyField(
        'project.Account',
        through='project.ProfileAccount'
    )

    def __unicode__(self)
        return self.name

class Accounts(models.Model):
    name = models.CharField(max_length=32)
    type = models.CharField(max_length=32)

    class Meta:
        ordering = ('name',)

    def __unicode__(self)
        return self.name

class ProfileAccounts(models.Model):
    profile = models.ForeignKey('project.Profile')
    account = models.ForeignKey('project.Accounts')

    number = models.PositiveIntegerField()

    class Meta:
        ordering = ('number',)

那么当我访问 Accounts 时,如何在中间 ProfileAccounts 模型中按“数字”而不是 Accounts 模型中的默认“名称”排序?:

for acct_number in self.profile.accounts.all():
    pass

这不起作用,但这是我希望它如何访问这些数据的要点:

for scanline_field in self.scanline_profile.fields.all().order_by('number'):
    pass

【问题讨论】:

    标签: python django many-to-many


    【解决方案1】:

    我刚刚经历了这个。

    class Profile(models.Model):     
        accounts = models.ManyToManyField('project.Account',
                                          through='project.ProfileAccount')
    
        def get_accounts(self):
            return self.accounts.order_by('link_to_profile')
    
    
    class Account(models.Model):
        name = models.CharField(max_length=32)
    
    
    class ProfileAccount(models.Model):
        profile = models.ForeignKey('project.Profile')
        account = models.ForeignKey('project.Account', related_name='link_to_profile')
        number = models.PositiveIntegerField()
    
        class Meta:
            ordering = ('number',)
    

    我删除了除Account.name 之外的离题字段。 这是我找到的最短的解决方案,不知道是否可以在 2010 年使用,但现在肯定可以。

    【讨论】:

    • 对于 OP 的问题,他只需将for acct_number in self.profile.accounts.all() 重写为for acct_number in self.profile.accounts.all().order_by('link_to_profile')。如果不清楚。您不必将get_accounts 方法添加到Profile 模型。 Django 总是在多对多表上创建一个外连接,所以根据字段指定一个order_by 就可以了。
    • 这到底是怎么工作的? account.link_to_profile 不是这里的相关经理,可能有许多不同的 ProfillAccount 实例与之相关联吗?
    【解决方案2】:

    ManyToManyField 管理器允许您直接从相关模型中选择/过滤数据,而无需在 django 级别上使用直通模型的任何模型连接...

    同样,

    如果你尝试:

    pr = Profile.objects.get(pk=1)
    pr.account.all()
    

    返回与该个人资料相关的所有帐户。如您所见,与直通模型 ProfileAccount 不存在直接关系,因此此时您不能使用 M2M 关系...您必须使用与直通模型的反向关系并过滤结果...

    pr = Profile.objects.get(pk=1)
    pr.profileaccount_set.order_by('number')
    

    会给你一个有序的查询集,但是,在这种情况下,你在查询集中拥有的是 profileaccount 对象,而不是帐户对象......所以你必须使用另一个 django 级别的关系来访问每个相关帐户:

    pr = Profile.objects.get(pk=1)
    for pacc in pr.profileaccount_set.order_by('number'):
        pacc.account
    

    【讨论】:

      【解决方案3】:

      将相关名称添加到 ProfileAccounts,然后使用“related_name__number”更改帐户中的顺序。注意 related_name 和 number 之间的两个下划线。见下文:

      class Accounts(models.Model):
          .
          .
          .
          class Meta:
              ordering = ('profile_accounts__number',)
      
      
      class ProfileAccounts(models.Model):
          .
          .
          .
          account = models.ForeignKey('project.Accounts', related_name='profile_accounts')
      
          number = models.PositiveIntegerField()
      
          class Meta:
              ordering = ('number',)
      

      【讨论】:

      • 不会在 Accounts 中放置关系跨越顺序子句要求对 Account 的每个查询也获取相关的配置文件帐户?这可能不是您想要做的。
      • 如果您不想使用related_name,可以只设置ordering=('profileaccounts_set__number')。个人喜好?这个解决方案最适合我的情况。 YMMV。
      【解决方案4】:

      这对我有用:

      Profile.objects.account.order_by('profileaccounts')
      

      【讨论】:

      • 优雅。正是我需要的。谢谢。
      【解决方案5】:

      个人资料中有一个错字(当我认为您的意思是“帐户”时,它是“帐户”),但更重要的是,您在模型中混合了单数/复数形式。

      在 Django 中,实践通常是将类命名为单数,而 ManyToManyField 命名为复数。所以:

      class Profile(models.Model):     
          name = models.CharField(max_length=32)
      
          accounts = models.ManyToManyField(
              'Account',
              through='ProfileAccount'
          )
      
          def __unicode__(self)
              return self.name
      
      class Account(models.Model):
          name = models.CharField(max_length=32)
          type = models.CharField(max_length=32)
      
          class Meta:
              ordering = ('name',)
      
          def __unicode__(self)
              return self.name
      
      class ProfileAccount(models.Model):
          profile = models.ForeignKey(Profile)
          account = models.ForeignKey(Account)
      
          number = models.PositiveIntegerField()
      
          class Meta:
              ordering = ('number',)
      

      我对你试图用这个模型做什么有点困惑,但如果你做出这些改变,那么for acct_number in self.profile.accounts.all().order_by('number'): 应该可以工作。假设没有其他问题。

      【讨论】:

      • self.profile.accounts.all().order_by('number') 不起作用,因为“数字”不是帐户上的字段。
      • 是的,那行不通,不是吗?如果我正确理解了这个问题(我在一年半之后再次查看它,所以我可能会感到困惑),最简单的方法是迭代与 Profile 关联的 ProfileAccount 对象,而不是Account 对象,然后只获取与每个 ProfileAccount 对象关联的 Account。但另一方面,我认为有一种语法可以对相关模型进行排序——也许 order_by('profileaccount__number')?
      • 我不是专家,但我无法让任何 order_by 在那里工作。我能想到的最好的方法是使用self.profile.profileaccount_set.order_by(),但这不是很漂亮,它会给你留下一堆 ProfileAccounts,而不是 Account 实例。
      【解决方案6】:

      这个特定问题的最简单的解决方案似乎是

      for acct in self.profile.accounts.order_by('profileaccounts'):
          pass
      

      【讨论】:

        【解决方案7】:

        我的许多模型都有这个,但在我看来(与所有其他答案不同)你不需要再次指定 order_by,因为它已经在 through 模型中指定.再次指定它会破坏 DRY(不要重复自己)原则。

        我会使用:

        qs = profile.profileaccounts_set.all()
        

        这会使用您配置的顺序提供与配置文件关联的一组 ProfileAccounts。那么:

        for pa in qs:
            print(pa.account.name)
        

        对于奖励积分,您还可以通过在查询中使用select_related 来加快整体流程。

        【讨论】:

        • 如果有人在这么久之后还在阅读这篇文章,这是错误的 ManyToMany 字段,通过模型没有“稳定”排序
        猜你喜欢
        • 1970-01-01
        • 2017-07-21
        • 1970-01-01
        • 2011-09-26
        • 2019-12-04
        • 2011-05-19
        • 2010-12-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多