代码之家  ›  专栏  ›  技术社区  ›  Scott

查询所有行并返回每个重复行中的最新行

  •  3
  • Scott  · 技术社区  · 14 年前

    嘿,伙计们,我有一个模特的身份证不是唯一的。每个型号都有日期。我想返回所有结果,但只返回共享id的每一行中的最新结果。模型看起来像这样:

    class MyModel(models.Model):
        my_id = models.PositiveIntegerField()
        date  = models.DateTimeField()
        title = models.CharField(max_length=36)
    
    
    ## Add some entries
    m1 = MyModel(my_id=1, date=yesterday, title='stop')
    m1.save()
    
    m2 = MyModel(my_id=1, date=today, title='go')
    m2.save()
    
    m3 = MyModel(my_id=2, date=today, title='hello')
    m3.save()
    

    现在尝试检索以下结果:

    MyModel.objects.all()... # then limit duplicate my_id's by most recent
    

    平方米

    3 回复  |  直到 14 年前
        1
  •  6
  •   Ned Batchelder    14 年前

    您将无法仅使用ORM来实现这一点,您需要获取所有记录,然后在Python中丢弃重复的记录。

    objs = MyModel.objects.all().order_by("-date")
    seen = set()
    keep = []
    for o in objs:
        if o.id not in seen:
            keep.append(o)
            seen.add(o.id)
    

    下面是一些可以从数据库中获取所需内容的自定义SQL:

    select * from mymodel where (id, date) in (select id, max(date) from mymodel group by id)
    

        2
  •  1
  •   Sarkis Varozian    14 年前

    您还应该考虑将上面的逻辑抽象为一个管理器:

    http://docs.djangoproject.com/en/dev/topics/db/managers/

    现在,models.py将如下所示:

    class MyModelManager(models.Manager):
        def no_dupes:
            objs = MyModel.objects.all().order_by("-date")
            seen = set()
            keep = []
            for o in objs:
                if o.id not in seen:
                    keep.append(o)
                    seen.add(o.id)
            return keep
    
    class MyModel(models.Model):
        my_id = models.PositiveIntegerField()
        date  = models.DateTimeField()
        title = models.CharField(max_length=36)
        objects = MyModelManager()
    

    上面的代码就绪后,您可以调用:MyModel.objects.no_dupes(),这将给出您想要的结果。如果您希望这样做,您甚至可以重写all()函数:

    http://docs.djangoproject.com/en/1.2/topics/db/managers/#modifying-initial-manager-querysets

    我发现管理器是一个更好的解决方案,以防您需要在整个项目的多个视图中使用它,这样您就不必重写代码X次。

        3
  •  -1
  •   Greg Detre    13 年前

    就像内德说的,我不知道怎么对付虫子。但是,您可以使用db来限制在python的for循环中必须完成的工作量。

    我们的想法是使用Django的 annotate (基本上在运行 group_by )查找具有多行且具有相同行的所有实例 my_id 按照奈德的建议来处理。然后对于其余的行(没有重复的行),您可以只获取单独的行。

    from django.db.models import Count, Q
    annotated_qs = MyModel.objects.annotate(num_my_ids=Count('my_id')).order_by('-date')
    dupes = annotated_qs.filter(num_my_ids__gt=1)
    uniques = annotated_qs.filter(num_my_ids__lte=1)
    for dupe in dupes:
       ... # just keep the most recent, as Ned describes
    keep_ids = [keep.id for keep in keeps]
    latests = MyModel.objects.filter(Q(id__in=keep_ids) | Q(id__in=uniques))
    

    如果您只有少量的重复,这将意味着for循环要短得多,代价是额外的查询(获取重复)。