class Author(models.Model):
name = models.CharField(max_length=50)
age = models.PositiveIntegerField(null=True, blank=True)
alias = models.CharField(max_length=50, null=True, blank=True)
goes_by = models.CharField(max_length=50, null=True, blank=True)
我们通常不建议允许 null=True
为 CharField
,因为这允许字段有两个 Coalesce
,但它对下面的 Coalesce
例子很重要。
比较和转换函数
Cast
class Cast(expression, output_field)[source]
强制 expression
的结果类型为 output_field
的类型。
用法示例:
>>> from django.db.models import FloatField
>>> from django.db.models.functions import Cast
>>> Author.objects.create(age=25, name="Margaret Smith")
>>> author = Author.objects.annotate(
... age_as_float=Cast("age", output_field=FloatField()),
... ).get()
>>> print(author.age_as_float)
接受至少两个字段名或表达式的列表,并返回第一个非空值(注意,空字符串不被视为空值)。每个参数必须是同样的类型,因此混合文本和数字将导致数据库错误。
使用示例:
>>> # Get a screen name from least to most public
>>> from django.db.models import Sum
>>> from django.db.models.functions import Coalesce
>>> Author.objects.create(name="Margaret Smith", goes_by="Maggie")
>>> author = Author.objects.annotate(screen_name=Coalesce("alias", "goes_by", "name")).get()
>>> print(author.screen_name)
Maggie
>>> # Prevent an aggregate Sum() from returning None
>>> # The aggregate default argument uses Coalesce() under the hood.
>>> aggregated = Author.objects.aggregate(
... combined_age=Sum("age"),
... combined_age_default=Sum("age", default=0),
... combined_age_coalesce=Coalesce(Sum("age"), 0),
... )
>>> print(aggregated["combined_age"])
>>> print(aggregated["combined_age_default"])
>>> print(aggregated["combined_age_coalesce"])
Warning
在 MySQL 上传递给 Coalesce
的 Python 值可能会被转换为不正确的类型,除非明确地转换为正确的数据库类型:
>>> from django.db.models import DateTimeField
>>> from django.db.models.functions import Cast, Coalesce
>>> from django.utils import timezone
>>> now = timezone.now()
>>> Coalesce("updated", Cast(now, DateTimeField()))
接受一个表达式和一个排序规则名称来进行查询。
例如,在 SQLite 中进行不区分大小写的过滤:
>>> Author.objects.filter(name=Collate(Value("john"), "nocase"))
<QuerySet [<Author: John>, <Author: john>]>
它还可以用于排序,例如在 PostgreSQL 中:
>>> Author.objects.order_by(Collate("name", "et-x-icu"))
<QuerySet [<Author: Ursula>, <Author: Veronika>, <Author: Ülle>]>
接受至少两个字段名或表达式的列表,并返回最大的值。每个参数必须是同样的类型,所以混合文本和数字会导致数据库错误。
使用实例:
class Blog(models.Model):
body = models.TextField()
modified = models.DateTimeField(auto_now=True)
class Comment(models.Model):
body = models.TextField()
modified = models.DateTimeField(auto_now=True)
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
>>> from django.db.models.functions import Greatest
>>> blog = Blog.objects.create(body="Greatest is the best.")
>>> comment = Comment.objects.create(body="No, Least is better.", blog=blog)
>>> comments = Comment.objects.annotate(last_updated=Greatest("modified", "blog__modified"))
>>> annotated_comment = comments.get()
annotated_comment.last_updated
将是 blog.modified
和 comment.modified
中最近的。
Warning
当一个或多个表达式可能为 null
时,Greatest
的行为在不同的数据库之间有所不同。
PostgreSQL:Greatest
将返回最大的非空表达式,如果所有表达式都是 null
,则返回 null
。
SQLite、Oracle 和 MySQL。如果任何表达式是 null
,Greatest
将返回 null
。
如果你知道一个合理的最小值作为默认值,可以使用 Coalesce
来模拟 PostgreSQL 的行为。
JSONObject
class JSONObject(**fields)[source]
接受一组键值对,并返回一个包含这些键值对的 JSON 对象。
用法示例:
>>> from django.db.models
import F
>>> from django.db.models.functions import JSONObject, Lower
>>> Author.objects.create(name="Margaret Smith", alias="msmith", age=25)
>>> author = Author.objects.annotate(
... json_object=JSONObject(
... name=Lower("name"),
... alias="alias",
... age=F("age") * 2,
... )
... ).get()
>>> author.json_object
{'name': 'margaret smith', 'alias': 'msmith', 'age': 50}
当一个或多个表达式可能是 null
时,Least
的行为在不同的数据库之间有所不同。
PostgreSQL:Least
将返回最小的非空表达式,如果所有表达式都是 null
,则返回 null
。
SQLite、Oracle 和 MySQL。如果任何表达式是 null
,Least
将返回 null
。
如果你知道一个合理的最大值作为默认值,可以使用 Coalesce
来模拟 PostgreSQL 的行为。
NullIf
class NullIf(expression1, expression2)[source]
接受两个表达式,如果相等则返回 None
,否则返回 expression1
。
关于 Oracle 的注意事项
由于 Oracle 惯例,当表达式为 CharField
类型时,该函数返回空字符串而不是 None
。
在 Oracle 上禁止将 Value(None)
传递给 expression1
,因为 Oracle 不接受 NULL
作为第一个参数。
我们将在每个函数的例子中使用以下模型:
class Experiment(models.Model):
start_datetime = models.DateTimeField()
start_date = models.DateField(null=True, blank=True)
start_time = models.TimeField(null=True, blank=True)
end_datetime = models.DateTimeField(null=True, blank=True)
end_date = models.DateField(null=True, blank=True)
end_time = models.TimeField(null=True, blank=True)