python - How to write manager class which use filter field as computed field not as a part of model fields?
问题描述
I have a model Student
with manager StudentManager
as given below. As property gives the last date by adding college_duration
in join_date
. But when I execute this property computation is working well, but for StudentManager
it gives an error. How to write manager class which on the fly computes some field using model fields and which is used to filter records.
The computed field is not in model fields. still, I want that as filter criteria.
class StudentManager(models.Manager):
def passed_students(self):
return self.filter(college_end_date__lt=timezone.now())
class Student(models.Model):
join_date = models.DateTimeField(auto_now_add=True)
college_duration = models.IntegerField(default=4)
objects = StudentManager()
@property
def college_end_date(self):
last_date = self.join_date + timezone.timedelta(days=self.college_duration)
return last_date
Error Django gives. when I tried to access Student.objects.passed_students()
django.core.exceptions.FieldError: Cannot resolve keyword 'college_end_date' into field. Choices are: join_date, college_duration
解决方案
Q 1. How alias queries done in Django ORM?
By using the annotate(...)
--(Django Doc) method of QuerySet
Q 2. Why property not accessed in Django managers?
Because the model managers (more accurately, the QuerySet
s) are wrapping things that are being done in the database. You can call the model managers as a high-level database wrapper too.
But, the property college_end_date
is only defined in your model class and the database is not aware of it, and hence the error.
Q 3. How to write manager to filter records based on the field which is not in models, but can be calculated using fields present in the model?
Using annotate(...)
method is the proper Django way of doing so. As a side note, a complex property logic may not be re-create with the annotate(...)
method.
In your case, I would change college_duration
field from IntegerField(...)
to DurationField(...)
--(Django Doc) since its make more sense (to me)
Later, update your manager and the properties as,
from django.db import models
from django.utils import timezone
class StudentManager(models.Manager):
def passed_students(self):
default_qs = self.get_queryset()
college_end = models.ExpressionWrapper(
models.F('join_date') + models.F('college_duration'),
output_field=models.DateField()
)
return default_qs \
.annotate(college_end=college_end) \
.filter(college_end__lt=timezone.now().date())
class Student(models.Model):
join_date = models.DateTimeField()
college_duration = models.DurationField()
objects = StudentManager()
@property
def college_end_date(self):
# return date by summing the datetime and timedelta objects
return (self.join_date + self.college_duration).date()
Note:
DurationField(...)
will work as expected in PostgreSQL and this implementation will work as-is in PSQL. You may have problems if you are using any other databases, if so, you may need to have a "database function" which operates over the datetime and duration datasets corresponding to your specific database.Personally, I like this solution,
推荐阅读
- javascript - 没有 new 就不能调用类构造函数
- c - _mm_lfence() 时间开销是不确定的?
- android - 等待方法的输出以启动 Activity
- sapui5 - SAPUI5 从 SAP WEB IDE 部署
- handlebars.js - 如何在基础电子邮件模板上使用对象数组?
- database-design - 如何在 SQLite3 中为一个资源管理两个闭包表
- python - 如何使用整数列表创建树而不覆盖先前输入的值
- javascript - 按句点分割字符串,但如果句点后有空格则不分割
- java - 控制器无法理解 UUID 何时发送
- python - yolo v3如何提取检测到的物体的图像