检索人员的最新对象
所以,我有一个 Person 模型,然后有一个模型名称 Carusage。 Carusage的相关部分是这样的:
class Carusage(models.Model):
person = models.ForeignKey(Person)
start = models.DateTimeField()
end = models.DateTimeField(null=True, blank=True)
一个人可以乘坐一辆车,然后系统创建一个新的Carusage实例并将其保存为当前时间。然后当人还车时,当前时间被保存到结束。
现在,在我的代码中,我有一个 Person 模型列表,我想检索 Carusage 中每个人的最新日期。因此,如果一个人刚刚归还汽车,我希望最新 Carusage 的结束字段与该人相关联,如果该人仍然拥有汽车,我希望获得起始字段。
我最好在一个 SQL 语句中执行此操作,因为我的人员列表可能会变得相当大(下限~10,上限~10.000)。我尝试了这样的事情:
Carusage.objects.filter(person__in(person_list)).exclude(start__gte(time_now))
然后正在考虑注释,但无法想出如何继续。
所以目前我正在这样做:
time_now = datetime.datetime.now()
time_list = []
for p in person_list:
latest = Carusage.objects.filter(person=p).exclude(start__gte=time_now).only('start', 'end').latest('start')
try:
if latest.end<time_now:
time=latest.end
else:
raise
except:
time=latest.start
time_list.append(time)
显然我的代码运行速度很慢(500 人列表大约需要 5 秒)。运行这个/这些查询的“django-way”是什么? 我想实现两件事:Carusage 只访问数据库一次(至少不是 len(person_list) 次),并且只从数据库获取相关时间(只需要最新时间...)。 有什么办法可以实现这一点吗?
So, I have a Person model and then I have a model name Carusage. The relevant part of Carusage is this:
class Carusage(models.Model):
person = models.ForeignKey(Person)
start = models.DateTimeField()
end = models.DateTimeField(null=True, blank=True)
A Person can take a car and then the system creates a new Carusage instance and saves it with start as the current time. Then when the Person returns the car, the current time is saved to end.
Now, in my code I have a list of Person models and I want to retrieve the newest date in Carusage for each Person. So if a Person has just returned the car I'd want the end-field of the newest Carusage linked to that person and if the Person still has the car I'd want the start-field.
Preferrably I would like to do this in one SQL-statement as my Person-list can grow quite large (lower-bounds~10, upper-bounds~10.000). I tried something like this:
Carusage.objects.filter(person__in(person_list)).exclude(start__gte(time_now))
And was then thinking of annotating but couldn't think up how I would proceed.
So currently I am doing this:
time_now = datetime.datetime.now()
time_list = []
for p in person_list:
latest = Carusage.objects.filter(person=p).exclude(start__gte=time_now).only('start', 'end').latest('start')
try:
if latest.end<time_now:
time=latest.end
else:
raise
except:
time=latest.start
time_list.append(time)
Obviously my code runs way to slow (about 5 secs for 500 person list). What would be the "django-way" of running this/these queries?
Two things I'd like to achieve: Only hit the database once for the Carusage (at least not len(person_list) times) and only get the relevant time from the database (only need the newest time...).
Is there any way to achieve this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
实际上,您有两个单独的结果,需要通过 Union 组合在一起。
汽车已归还。既是开始时间又是结束时间。每人(可能)有很多辆车,而你只想要其中一辆车。即使在纯 SQL 中,这也是一个相当复杂的查询,需要 HAVING 子句并导致(可能)性能下降。
汽车尚未归还。
通常,当您有两个单独的规则时,您会对两个单独的查询感到更满意。
实际上,您正在对按具有等于组最大值的开始(或结束)的人员分组的 CarUsage 进行聚合。
我想这就是你要找的。
You actually have two separate results that you're putting together with an Union.
Cars returned. Both a start and an end time. There are (possibly) many cars per person and you want only one of those cars. Even in pure SQL, this is a rather complex query requiring a HAVING clause and leading to (potentially) slow performance.
Cars not yet returned.
Often, you'll be happier with two separate queries when you have two separate rules.
You're actually doing aggregation of CarUsage grouped by Person having a start (or end) equal to the max of the group.
I think this is what you're looking for.
通过以这种方式使用异常,你肯定会得到缓慢的代码。异常的代价非常昂贵,并且可能会减慢速度你的代码会变慢很多(除了例外情况,速度会慢 5-10 倍)。不用引发和捕获异常,只需使用 else:
另外,您可以简单地使用 Django 的 order_by 过滤器吗?例如,要按起始值获取给定人员的所有“Carusages”,请调用:
By using exceptions that way, you are GUARANTEED to get slow code. Exceptions are horribly expensive and potentially slow down your code a lot (up to 5-10x slower with exceptions). Instead of raising and catching an exception, simply use else:
Also could you simply use Django's order_by filter? For instance to get all "Carusages" for a given person order by start value, invoke: