Django 使用 for 循环优化查询
是否可以优化 Django,使其不再对此类内容运行如此多的查询。
for student in Student.objects.all():
for course in student.course_set.all():
for grade in course.grade_set.filter(student=student):
# do stuff
查询量是学生 * 课程 * 成绩,这可能会变得巨大。
*编辑 从罗斯曼的博客中获得一些想法后的一种可能性。
for grade in student.grade_set.order_by('course', 'marking_period').select_related():
if grade.marking_period_id in some_report_input:
# do stuff
这只是一个片段,但基本上我用一个 for 循环替换了我关心的最后一项(成绩)的 for 循环,成绩引用了我需要的一切(学生、课程、评分期)。使用诸如marking_period_id之类的东西而不是grade.marking_period(它执行另一个查询)是关键。
权衡是代码可读性。我想过滤掉成绩并根据标准进行组织。这从琐碎变得复杂。
这绝不是一个通用的解决方案。我确信有时这根本没有帮助。如果您知道更好的方法请评论。
另一个例子:
for student in students:
print student
for department in departments:
print department
failed_grades = Grade.objects.filter(course__department=department,course__courseenrollment__user=student,grade__lte=70)
for failed_grade in failed_grades:
print grade.course
print grade.grade
学生注册了一门课程。一门课程有一个系。
Is it possible to optimize Django to not run so many queries on something like this
for student in Student.objects.all():
for course in student.course_set.all():
for grade in course.grade_set.filter(student=student):
# do stuff
The amount of queries is students * courses * grades which can get huge.
*edit
One possibility after getting some ideas from roseman's blog.
for grade in student.grade_set.order_by('course', 'marking_period').select_related():
if grade.marking_period_id in some_report_input:
# do stuff
That's just a snippet but basically I replaced the for loops with just one for loops for the last item I care about (grades) Grades has references to everything I need (student, course, marking period). It was key to use things like marking_period_id instead of grade.marking_period (which does another query).
The trade off is code readability. I wanted to filter out grades and organize based on a criteria. This goes from trivial to convoluted.
This is by no means a generic solution. I'm sure there are times when this won't help at all. Please comment if you know a better way.
Another example:
for student in students:
print student
for department in departments:
print department
failed_grades = Grade.objects.filter(course__department=department,course__courseenrollment__user=student,grade__lte=70)
for failed_grade in failed_grades:
print grade.course
print grade.grade
A student gets enrolled in a course. A course has a department.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您发布模型代码和您省略的“do stuff”代码,这将会很有帮助。这样,我们就可以了解如何在您的案例中进行有效的查询。
尽管如此,我认为这可能会有所帮助。它涵盖了
select_lated
没有涵盖的一些情况。请注意,自 Django 1.4 起,prefetch_lated
可用,因此您可能需要升级到此版本才能使用它。对我们来说,帮助您在此处添加“do stuff”代码非常重要,如果相关(并且我认为会相关),请在此处添加您的模型代码(只需字段声明就可以了)。因为获得优化查询的方式取决于模型的关联方式以及使用查询集结果的方式。
编辑:
为了优化最后一个示例的最后一个“for”,您可以执行以下操作:
在此示例中,当您执行
grade.course
时,该查询的 select_lated
部分会缓存与筛选成绩相关的所有课程,因此您只需使用它们进行一个查询即可。因此,如果 Course 模型的 __unicode__ 方法仅使用其自己的字段(我的意思是,如果您没有在 Course 的 unicode 方法中显示任何其他模型数据),您应该得到比您的示例中的性能更好(更少的查询)。我不知道如何根据您的需要改进其他语句。但我认为这可以帮助你得到你想要的(也许我不太了解你的模型,无法更好地帮助你)It would be helpful if you post your models code and the "do stuff" code you ommit. This way, we could understand how to make an efficient query in your case.
Nevertheless, i think this could be helpful. It covers some cases that
select_related
doesn't. Note thatprefetch_related
is available since Django 1.4 so you may need to upgrade to this version in order to use it.It's really important for us to help you that you add your "do stuff" code here, and if it is relevant (and i think it will be) add your models code here (just the fields declarations will be fine). Because the way of getting an optimized query depends on how your models are related and how you are using the queryset results.
EDIT:
in order to optimize the last "for" of your last example, you can do this:
In this example, when you do
grade.course
, theselect_related
part of that query caches all the courses related to the filtered grades, so you can use them making just one query. So, if the__unicode__
method of Course model use only its own fields (i mean, if you don't show any other model data in Course's unicode method) you should get a better performance (less queries) that in your example. I'm not sure how to improve the other for statements as you want. But i think this can help you to get what you want (maybe i'm not understanding your models too much to help you better)您可以使用 select_lated() ,它只会访问数据库一次。
更多信息请参见此链接(django 的文档)
http://docs.djangoproject.com/en/1.2/ ref/models/querysets/#select-lated
如何在您的案例中使用它的示例
you can use select_related() and it will only hit the database once.
More info in this link (django's documentation)
http://docs.djangoproject.com/en/1.2/ref/models/querysets/#select-related
An example of how you could use this in your case