Django 使用 for 循环优化查询

发布于 2024-10-19 18:15:09 字数 1108 浏览 4 评论 0原文

是否可以优化 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

花桑 2024-10-26 18:15:09

如果您发布模型代码和您省略的“do stuff”代码,这将会很有帮助。这样,我们就可以了解如何在您的案例中进行有效的查询。

尽管如此,我认为可能会有所帮助。它涵盖了 select_lated 没有涵盖的一些情况。请注意,自 Django 1.4 起,prefetch_lated 可用,因此您可能需要升级到此版本才能使用它。

对我们来说,帮助您在此处添加“do stuff”代码非常重要,如果相关(并且我认为会相关),请在此处添加您的模型代码(只需字段声明就可以了)。因为获得优化查询的方式取决于模型的关联方式以及使用查询集结果的方式。

编辑:

为了优化最后一个示例的最后一个“for”,您可以执行以下操作:

failed_grades = Grade.objects.filter(course__department=department,course__courseenrollment__user=student,grade__lte=70).select_related('course')
for failed_grade in failed_grades:
    print grade.course
    print grade.grade

在此示例中,当您执行 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 that prefetch_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:

failed_grades = Grade.objects.filter(course__department=department,course__courseenrollment__user=student,grade__lte=70).select_related('course')
for failed_grade in failed_grades:
    print grade.course
    print grade.grade

In this example, when you do grade.course, the select_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)

烂人 2024-10-26 18:15:09

您可以使用 select_lated() ,它只会访问数据库一次。

更多信息请参见此链接(django 的文档)
http://docs.djangoproject.com/en/1.2/ ref/models/querysets/#select-lated

如何在您的案例中使用它的示例

for x in Student.objects.select_related():
    do stuff with x.course.grade`

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

for x in Student.objects.select_related():
    do stuff with x.course.grade`
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文