Django F() 和 ExpressionWrapper

ian*_*ian 1 python django django-queryset django-views

我试图避免数据库命中我的views.py。如果我使用 F() 和 ExpressionWrapper,我是否对数据库进行查询?我正在尝试优化我的代码,当我阅读文档时有点困惑。我还包含了 Models.py 以供参考。我正在对我正在工作的网络应用程序进行优化。

视图.py

from django.shortcuts import render
from django.db.models import F, Q, Count, Sum, ExpressionWrapper, IntegerField
from .models import Student, Subject, Enrollment


def home(request):
    student = Student.objects.all()
    subject = Subject.objects.all()
    sem_score = Enrollment.objects.all().update(sem_score=ExpressionWrapper((F("prelim_score") + F("midterm_score") + F("final_score"))/3, output_field=IntegerField()))
    enrollment = Enrollment.objects.all()
    num_student = Enrollment.objects.all().count()
    context = {"student":student, "subject":subject, "enrollment":enrollment, "num_student":num_student}
    return render(request, 'core/home.html', context)
Run Code Online (Sandbox Code Playgroud)

模型.py

class Professor(models.Model):
    name = models.CharField(max_length=50)
    
    def __str__(self):
        return self.name
    
class Student(models.Model):
    name = models.CharField(max_length=50)
    
    def __str__(self):
        return self.name
    
class Course(models.Model):
    name = models.CharField(max_length=50)
    
    def __str__(self):
        return self.name
    
class Subject(models.Model):
    name = models.CharField(max_length=50)
    course = models.ManyToManyField(Course)
    enroll = models.ManyToManyField(Student, through='Enrollment')

    def __str__(self):
        return self.name
    
    
    
class Enrollment(models.Model):
    GRADE_STATUS = (
        ("A","A"),
        ("B", "B"),
        ("C", "C"),
    )
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
    professor = models.ForeignKey(Professor, on_delete=models.CASCADE, null=True, blank=True)
    date = models.DateTimeField(auto_now_add=True)
    prelim_score = models.IntegerField(null=True, blank=True, default=0)
    midterm_score = models.IntegerField(null=True, blank=True,  default=0)
    final_score = models.IntegerField(null=True, blank=True,  default=0)
    sem_score = models.IntegerField(null=True, blank=True,  default=0)
    sem_status = models.CharField(choices=GRADE_STATUS , max_length=50, null=True, blank=True,  default=0)
    
    
    def __str__(self):
        return f"{self.student} enrolled in {self.subject}"
    
Run Code Online (Sandbox Code Playgroud)

syt*_*ech 7

我使用了 F() 和 ExpressionWrapper,我是否对数据库进行查询

sem_score = Enrollment.objects.all().update(
    sem_score=ExpressionWrapper(
        (F("prelim_score") + F("midterm_score") + F("final_score")) / 3,
        output_field=IntegerField(),
    )
)
Run Code Online (Sandbox Code Playgroud)

上述内容需要考虑的一些重要注意事项:

  1. 查询集是惰性的,这意味着它们在被评估之前不会执行任何操作。
  2. F()表达式(和ExpressionWrapper)用于定义由数据库执行的表达式,而不是由 Python 在内存中执行的表达式。Django 创建一个表达式作为发送到数据库的查询的一部分;它永远不会被 Django 评估,甚至不会返回给 Django。
  3. 当您调用.update从 返回的查询集时.all(),它将立即评估查询集并执行更新。此时,查询(以及表达式)被发送到数据库,并且数据库在数据库服务器上计算表达式。

因此,直接回答您的问题:不,单独调用F()orExpressionWrapper()不会查询数据库。一旦 django与数据库对话,它就会发送你的表达式以供数据库评估。表达式求值严格在数据库中进行,而不是在 Python 中进行。

此外,由于表达式中引用的模型字段F()都是相同类型,因此您不需要ExpressionWrapper

sem_score = Enrollment.objects.all().update(
    sem_score=(F("prelim_score") + F("midterm_score") + F("final_score")) / 3
)
Run Code Online (Sandbox Code Playgroud)