【问题标题】:Query for items using filter for most recent entry in a related table使用过滤器查询相关表中最新条目的项目
【发布时间】:2011-07-25 07:02:51
【问题描述】:

您好,我的查询似乎有问题。我有一个项目清单。任何项目都可以设置状态(进、出、收集、销毁等)。以下是我的看法。

def client_summary(request, client_id):
    client = None
    items = None
try:
    client = models.Client.objects.get(pk = client_id)
    items = client.storageitem_set.all()
    total_items = items.count()
    except:
        return HttpResponse(reverse(return_clients))
    return render_to_response('client_summary.html', {'items':items, 'total_items':total_items, 'client':client}, context_instance = RequestContext(request))

如果我的模板中有

{%for item in items%}
        {{item.itemstatushistory_set.latest}}
{%endfor%}

这将显示所有最新状态。现在我只想打印出所有状态为 Destroyed 的项目。出于某种原因,我似乎无法做到这一点。

这里还有一些来自我的模型的更多信息。

class StorageItem(models.Model):
    type = models.ForeignKey(StorageObject)
    client = models.ForeignKey(Client)
    company_id = models.PositiveIntegerField(unique = True, blank = True, null = True)
    content = models.TextField(blank = True)
    alternative_id = models.CharField(verbose_name = 'Client no.', max_length = 60, blank = True)
    title = models.CharField(max_length = 100)
    format = models.ForeignKey(Format, blank = True, null = True)
    location = models.CharField(max_length = 20, blank = True)
    item_class = models.TextField(blank = True)

    def __unicode__(self):
        return self.title

class Status(models.Model):
    description = models.CharField(max_length = 60)
    notes = models.TextField(blank = True)
    def __unicode__(self):
        return self.description

    class Meta:
        verbose_name_plural = 'Status'
        get_latest_by = 'date'
        ordering = ['date']

class ItemStatusHistory(models.Model):
    date = models.DateTimeField(auto_now = True)
    contact = models.ForeignKey(Contact)
    item = models.ForeignKey(StorageItem)
    status = models.ForeignKey(Status)
    user = models.ForeignKey(User)

    def __unicode__(self):
        return str(self.status

编辑:仍然存在一些问题,因为一个项目之间的关系可能有很多状态。但我只想列出已损坏物品的最新状态。

示例:假设有 3 个项目,它们有集合 item1 = [in, out, destroyed]item2 = [destroyed, in]item3 = [destroyedcollected, destroyed]item4 = [in] 其中[1st status, 2nd status, 3rd status, etc]。我只想显示该项目的最新状态。

Mike 和 kriegar 都会得到类似 [item1, item2, item3, item3] 的结果。 因为 Yuji 使用了 distinct 函数,所以他会得到[item1, item2, item3]

最后我需要得到的答案应该是[item1, item3]

【问题讨论】:

  • 您是否正在寻找 _only_ 已销毁状态的项目?还是具有任何已销毁状态的物品?
  • @Yuji 抱歉回复晚了。您的解决方案几乎是正确的。但是,由于用户可以更改项目状态,因此我只希望项目具有 已销毁 状态(如果这是 最新状态)。因此,一个项目可能在一周内处于销毁状态,但用户将其状态更改为已收集。一旦它的状态改变了。它不应该在 destroyed 状态列表中。您的代码所做的是查看任何项目以查看它是否具有 destroyed 状态。然后打印。仅当 destroyed 为最新状态时才应打印。

标签: python html django models


【解决方案1】:

kriegar 的解决方案会奏效。还有一个,它按状态搜索 id 而不是 description 上的文本匹配:

destroyedStatus = Status.objects.get(description="destroyed")
clients_destroyed_items = StorageItem.objects.filter(client=client, 
    itemstatushistory__status=destroyedStatus)

这假定描述是唯一的,但您的模型中没有这样的约束。我不知道哪个实现更快。

编辑:顺便说一句,如果你有一个疯狂的系统,你有多个状态,描述为“已销毁”,并且你想通过状态查询ids而不是description,你会这样做:

destroyedStatusIDs = Status.objects.filter(description="destroyed").values_list("id", flat=True)
clients_destroyed_items = StorageItem.objects.filter(client=client, 
    itemstatushistory__status__in=destroyedStatusIDs)

顺便说一句,在您的 ForeignKeyOneToOneFieldManyToManyField 关系上设置 related_name 被认为是一种很好的做法,通常是复数形式。所以你的历史课变成了:

class ItemStatusHistory(models.Model):
    date = models.DateTimeField(auto_now=True)
    contact = models.ForeignKey(Contact, related_name="history")
    item = models.ForeignKey(StorageItem, related_name="history")
    status = models.ForeignKey(Status, related_name="history")
    user = models.ForeignKey(User, related_name="history")

这会将我的第一个示例更改为:

destroyedStatus = Status.objects.get(description="destroyed")
clients_destroyed_items = StorageItem.objects.filter(client=client, 
    history__status=destroyedStatus)

编辑 2: 啊,所以您只想考虑当前(即最新)状态。这就是aggregationF objects 的用武之地。基本上,这个想法是让数据库在具有date 的最新(即最大date)状态的表中创建一个“假列”,然后要求匹配日期和状态:

from django.db.models import F, Max

destroyedStatus = Status.objects.get(description="destroyed")
clients_destroyed_items = StorageItem.objects.annotate(
    last_change_date=Max("itemstatushistory__date")).filter(client=client, 
    itemstatushistory__status=destroyedStatus, 
    itemstatushistory__date=F("last_change_date"))

这个我没测试过,第一次尝试这个,可能有更好的方法,欢迎cmets。

【讨论】:

  • 我也一直在使用related_name,因为我喜欢显式而不是隐式。
  • 我似乎收到了一条错误消息'Status' object has no attribute 'values_list'
  • 糟糕,忘记将get 更改为filter。固定。
  • 嗯,似乎得到了数据库错误。 Caught OperationalError while rendering: (1054, "Unknown column 'T6.date' in 'having clause'")
  • 好吧,我没有主意了。您可能需要通过将status = models.ForeignKey(Status) 放入StorageItem 来对数据库进行非规范化,然后使用该字段来存储项目的当前状态,在这种情况下,您的查询类似于Status.objects.get(description="destroyed").storageitem_set.all()
【解决方案2】:

如果您想要一个属于客户端并被销毁的项目的查询集:

clients_destroyed_items = StorageItem.objects.filter(client=client, 
    itemstatushistory__status__description='destroyed')

跨越关系的查找¶

Django 提供了一个强大而直观的 “跟随”关系的方法 查找,处理 SQL JOIN 自动为你,背后 场景。要跨越一段关系,只需 使用相关字段的字段名 跨模型,由双分隔 下划线,直到你到达 你想要的字段。

此示例检索所有条目 具有名称为的 Blog 的对象 “披头士博客”:

Entry.objects.filter(blog_name_exact='Beatles 博客')

这个跨越可以像你想的那么深 喜欢。

它也可以反向工作。参考一个 “反向”关系,只需使用 模型的小写名称。

【讨论】:

猜你喜欢
  • 2012-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 2015-01-21
  • 1970-01-01
  • 2017-06-05
相关资源
最近更新 更多