【问题标题】:Filtering on foreign key and Many-to-many join tables过滤外键和多对多连接表
【发布时间】:2014-06-11 16:48:47
【问题描述】:

我有一些模型,其中对象与日期相关,但它们之间的关系也与日期相关。所以,为了说明,我可能有

class ValidCommonManager(models.Manager):
    ValidCommonManager(self, date):
        self.date = date

    def get_query_set(self):
        return super(ValidCommonManager, self).get_query_set().filter(
            models.Q(start__isnull=True)|
            models.Q(start__lte=self.date)
        ).filter(
            models.Q(end__isnull=True)|
            models.Q(end__gte=self.date)
        )

class CommonModel(models.Model):
    start = models.DateTimeField(null=True)
    end = models.DateTimeField(null=True)

    valid_objects = ValidCommonManager(now())
    objects = models.Manager()

class Group(CommonModel):
    name = models.CharField(max_length=25)
    description = models.TextField(blank=True)

    group_members = models.ManyToManyField(
      'Person', through='GroupMember')

class GroupMember(CommonModel):
    group = models.ForeignKey(Group)
    member = models.ForeignKey(Person)

class Person(CommonModel):
    name = models.CharField(max_length=100)

现在组可能存在于日期 A 到日期 Z,但成员资格在日期 B 和日期 C 可能不同,这反映在 GroupMember 中的开始和结束字段中。现在可以很容易地找到在我当前处理时处于活动状态的组

groups = Group.valid_objects.all()

但我想不出一个简单的方法来让小组成员在给定日期不到

members_of_my_group = group.group_members.filter(
            models.Q(start__isnull=True)|
            models.Q(start__lte=date)
        ).filter(
            models.Q(end__isnull=True)|
            models.Q(end__gte=date)
        ).filter(
            models.Q(group_member__start__isnull=True)|
            models.Q(group_member__start__lte=date)
        ).filter(
            models.Q(group_member__end__isnull=True)|
            models.Q(group_member__end__gte=date)
        )

这很快变得笨拙并违反了 DRY 的基本原则。我会在ValidCommonManager 上设置use_for_related_fields,但是如果你在过滤的经理身上这样做,Django docs 承诺会发生令人讨厌的事情。

【问题讨论】:

    标签: django django-orm


    【解决方案1】:

    相关管理器只是为了方便起见,您始终可以为 Person 定义一个自定义的 Manager 来做同样的事情。 (另请注意,从 Django 1.7 开始,您可以根据具体情况指定要用于相关查找的管理器。有关更多信息,请参阅the docs。)

    也就是说,

    group_members = Group.group_members.filter(...)
    

    相当于:

    class PersonManager(models.Manager):
    
        def get_valid_members(self, group):
            return self.filter(groupmember__group=group, 
                               Q(groupmember__start__isnull=True)...
    
    group_members = Person.objects.get_valid_members(group)
    

    另一种方法是使用子查询:

    group_members = Person.objects.filter(groupmember__in=
                        GroupMember.valid_objects.filter(group=group))
    

    【讨论】:

    • 这完全绕过了ManyToManyField,不是吗?它会返回 GroupMember 对象,而不是 group.group_members 那样的 Person 对象。在这种情况下,由于继承自 CommonModel,我已经在 GroupMember 上获得了经理 - 我可以做到 group_members = GroupMember.valid_objects.filter(group=group),不是吗?
    • 我希望有一个解决方案来获取个人记录,而不是 GroupMember 记录。哦,好吧。
    • @Paul:抱歉,我对您使用Group.group_members 指代Persons 而不是GroupMembers 这一事实感到困惑。同样的方法应该可以正常工作,我已经编辑了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    相关资源
    最近更新 更多