django 如何从表B中按表A进行过滤(表A链接到表B,而不是其他)

5cg8jx4n  于 2023-07-01  发布在  Go
关注(0)|答案(1)|浏览(144)

我有以下型号:

# models.py

class Movement(models.Model):
    class Direction(models.TextChoices):
        SENT = 'S', _('Sent')
        RETURNED = 'R', _('Returned')

    case = models.ForeignKey(Case, on_delete=models.CASCADE, unique=False, null=False, blank=False, db_index=True, db_column='case_id')
    direction = models.CharField(unique=False, null=False, blank=False, db_index=False, max_length=1, choices=Direction.choices, default=Direction.SENT, db_column='direction')
    date = models.DateField(auto_now=False, auto_now_add=False, db_index=True, default=current_date, db_column='date')

class Case(models.Model):
    class Meta:
        constraints = [models.UniqueConstraint(fields=['number', 'year', 'kind', 'police_station'], name='unique_case2')]

    class Kind(models.TextChoices):
        MISDEMEANOR = 'M', _('Misdemeanor')
        TRAFFIC = 'T', _('Traffic')

    police_station = models.ForeignKey(PoliceStation, on_delete=models.PROTECT, unique=False, null=False, blank=False, db_index=True, db_column='police_station_id')
    name = models.CharField(unique=False, null=False, blank=False, db_index=True, default='', max_length=100, db_column='name')
    number = models.SmallIntegerField(unique=False, null=False, blank=False, db_index=True, db_column='number')
    year = models.SmallIntegerField(unique=False, null=False, blank=False, db_index=True, default=current_year, db_column='year')
    kind = models.CharField(unique=False, null=False, blank=False, db_index=False, max_length=1, choices=Kind.choices, default=Kind.MISDEMEANOR, db_column='kind')
    registered_at = models.DateTimeField(auto_now=False, auto_now_add=False, db_index=True, default=current_datetime, db_column='registered_at')

关于运动表的一些事实:

  • “移动”表可能具有同一病例的多个记录。
  • direction字段不能为一行中的两个记录具有相同的值。(例如:如果case_id = 2的最后一个移动记录是direction = 'S',则下一个移动必须是direction = 'R')。

我想从CaseAdmin类中通过它们最后一次运动的方向来过滤案例,分为三种类型:
1.发送。
1.回来了。
1.还没有动静。
现在,如何构建一个查询来根据最后一次移动过滤案例?
过滤规则:

  • 仅应考虑最后一个移动记录。
  • 最后的移动记录应该由其-date而不是id确定。
  • '发送'选项,必须显示可用的情况下,他们的最后运动direction = 'S'.
  • “返回”选项,必须显示可用的情况下,他们的最后运动direction = 'R'
  • “还没有移动”选项,必须显示还没有移动记录的可用案例。

我想了想,想出了两个解决办法:
1.在Movement表中添加一个名为is_latest的额外布尔字段,以便在构建查询时知道哪个记录将被视为最新的运动。每当为同一案例添加每个新的移动记录时,这需要两个用于Movement表的update语句。
1.或者,向Case表中添加一个名为last_movement的额外外键字段,该字段指向Movement表中的最新移动记录。我认为这一个更好,因为它只需要一个更新语句的情况下,每当每个新的运动记录被添加到相同的情况下表。
当每个新的移动记录被插入到移动表中时,这两种解决方案都需要被维护。
但我想知道是否有另一个好的/更好的/实际的解决方案。
谢谢!

qvk1mo1f

qvk1mo1f1#

不需要定义额外的ForeignKey s或向Movement表添加额外的字段。我们可以使用**Subquery**表达式[Django-doc]:

from django.db.models import OuterRef, Subquery

class CaseAdmin(admin.ModelAdmin):
    def get_queryset(self, *args, **kwargs):
        return (
            super()
            .get_queryset(*args, **kwargs)
            .annotate(
                last_direction=Subquery(
                    Movement.objects.filter(case_id=OuterRef('pk'))
                    .order_by('-date')
                    .values('direction')[:1]
                )
            )
        )

    list_filter = [LastDirectionFilter]

LastDirectionFilter

from django.contrib import admin

class LastDirectionFilter(admin.SimpleListFilter):
    title = 'Last direction'
    parameter_name = 'last_direction'

    def lookups(self, request, model_admin):
        return [
            ('S', 'Sent'),
            ('R', 'Returned'),
            ('-', 'No movement yet'),
        ]

    def queryset(self, request, queryset):
        value = self.value()
        if value == '-':
            return queryset.filter(last_direction=None)
        elif value is not None:
            return queryset.filter(last_direction=value)
        return queryset

在这里,我们因此过滤我们注解,如果还没有移动,则可以是None,或者'S''R'

相关问题