你好,我是 EarlGrey,一名双语学习者,会一点编程,目前已翻译出版《Python 无师自通》、《Python 并行编程手册》等书籍。
点击上方蓝字关注我,获取最新编程及AI干货、高赞工具和项目分享。
在后台回复“books”,即可领取超值优质电子书合集。
11月20日,Django团队在博客上发布了 Django 5.1rc1版本,这是5.0正式版发布前最后一个版本,等待译者们提交翻译后即可发布。预计正式发布时间为12月4日,仅剩一周多一点的时间。
如果你现在就想尝鲜的话,可以通过 pip 进行安装:
pip install --pre django
安装时请注意,Django 5.0 仅支持3.10以上的版本。
据团队成员Mariusz Felisiak介绍,Django 5.0的首个版本共合并了接近700个commit,有204位开发者参与版本开发。
新版本带来了很多令人兴奋的新特性,让我们一起来了解一下吧。
以下新特性的介绍,来自 Django 团队成员 Mariusz Felisiak 在 fly.io 上发布的博文(原文地址见文末)。
数据库生成字段 Generated Fields
多年来,Django 开发人员一直在纠结以下问题:
在哪里保存基于模型对象的可复用属性?
如何在所有 Django 组件中使用它们?
Django 5.0 带来了这些需求的答案-- GeneratedField!
它允许创建数据库生成的字段,其值总是由数据库本身根据其他字段计算得出。数据库表达式和函数也可用于即时进行必要的修改。让我们来看看它在实际中是如何工作的。
假设我们有一些经常连在拼接使用的字段,如 first_name 和 last_name。在 5.0 以下版本中,最佳的选择是自定义实现模型的 QuerySet 或 Manager 管理器。
from django.db import models
from django.db.models.functions import Concat
class Order(models.Model):
person = models.ForeignKey("Person", models.CASCADE)
...
class PersonQuerySet(models.QuerySet):
def with_extra_fields(self):
return self.annotate(
full_name=Concat(
"first_name", models.Value(" "), "last_name",
),
)
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
objects = PersonQuerySet.as_manager()
def __str__(self):
return f"{self.first_name} {self.last_name}"
在自定义的 QuerySet 里添加注释,可以实现共享模型对象的常用计算值:
python3 manage.py shell
>>> from order.models import Order, Person
>>> Person.objects.with_extra_fields().filter(full_name="Joe Doe")
<PersonQuerySet [<Person: Joe Doe>]>
>>> mark = Person.objects.with_extra_fields().get(full_name="Mark Doe")
>>> mark.full_name
'Mark Doe'
# It's not available on relationship :(
>>> Order.objects.filter(person__full_name="Catherine Smith")
...
raise FieldError(
django.core.exceptions.FieldError: Unsupported lookup 'full_name'
for ForeignKey or join on the field not permitted.
不过,QuerySet 注释也有局限性。首先,full_name 只适用于从 .with_extra_fields() 方法返回的对象,因此我们必须记住使用它。模型方法中的 self 实例也不能使用它,因为它不是 Person 属性。例如,它不能用于 Person.str()。此外,每次都要计算值,这对于复杂的计算来说可能是个问题。
GeneratedField 使基于其他字段的值的提供变得真正无缝。每次使用给定的数据库表达式更改模型时,它的值都会自动设置。此外,还可以使用 db_persist 参数决定是否存储该值:
db_persist=True:表示存储这些值,并像其他普通列一样占用存储空间。另一方面,只有在值发生变化时才会计算这些值,因此我们可以避免在获取数据时进行额外的计算。在某些数据库(MySQL、Postgres、SQLite)中,它们甚至可以被编入索引!
db_persist=False: 表示列不占用存储空间,但每次都会计算其值。
数据库对使用不同表达式和选项的支持可能有所不同。例如,Postgres 不支持虚拟列,因此 db_persist 必须设置为 True。SQLite 和 MySQL 同时支持虚拟字段和存储生成字段。
回到我们的 "full_name "示例。在 Django 5.0 中,我们可以摆脱之前方法的所有弊端,用 full_name 注解中使用的表达式定义一个 GeneratedField:
from django.db import models
from django.db.models.functions import Concat
class Order(models.Model):
person = models.ForeignKey("Person", models.CASCADE)
...
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
# ↓ Our new GeneratedField based on an expression ↓
full_name = models.GeneratedField(
expression=Concat(
"first_name", models.Value(" "), "last_name"
),
output_field=models.CharField(max_length=511),
db_persist=True,
)
def __str__(self):
# There is no need to re-implement the same logic anymore!
return self.full_name
现在,full_name 是一个正常的 Person 属性,可以像其他字段一样在所有 Django 组件中使用。
数据库计算的默认值
数据库计算的默认值是在数据库中完全表达 Django 模型结构和关系的最后一个重要障碍。在 Django < 5.0 版本中,Field.default 是为字段设置默认值的唯一选项,但它是在 Python 端计算的,并在添加新行时作为参数传递。因此,从数据库结构的角度来看,它是不可见的,直接在数据库中添加行时也无法设置。
如果考虑到数据库和网络延迟,这也会造成数据不一致的风险,例如,当我们想默认为当前时间点时。此外,对于只能直接访问数据库的人员(如数据库管理员或数据科学家)来说,这也是不可见的。
Django 5.0 通过新的 Field.db_default 选项支持数据库计算的默认值。这是解决与之前方法相关的所有问题的灵丹妙药,因为它允许我们在数据库中定义和计算默认值。它接受字面值和数据库函数。让我们来看一个实际例子:
from decimal import Decimal
from django.db import models
from django.db.models.functions import Now
class Order(models.Model):
person = models.ForeignKey("Person", models.CASCADE)
created = models.DateTimeField(db_default=Now())
priority = models.IntegerField(db_default=0)
...
表单字段组渲染
第三个值得一提的重要特性是 Django 5.0 引入的表单字段组渲染概念,这使得表单字段的渲染变得更加简单和可复用。
表单字段通常会伴随着许多相关的属性进行渲染,如帮助文本、标签、错误信息和小部件。每个项目都有自己的 HTML 结构和首选的表单字段渲染方式。假设我们使用基于 <div>
的字段模板(像 Django 那样)来帮助使用辅助技术的用户(例如屏幕阅读器)。我们首选的字段渲染方式可能看起来是这样的:
<div class="form-field">
{{ field.label_tag }}
{% if field.help_text %}
<p class="help" id="{{ field.auto_id }}_helptext">
{{ field.help_text|safe }}
</p>
{% endif %}
{{ field.errors }}
{{ field }}
</div>
它与 Django 新增加的 .as_field_group() 方法的默认模板完全相同,因此现在我们可以将其简化为:
<div class="form-field">{{ field.as_field_group }}</div>
.as_field_group() 默认使用 "django/forms/field.html "模板,该模板可按项目、字段或请求进行自定义。这给了我们很大的灵活性。
总结
除了本文介绍的三项重大新特性外,Django 5.0 还在可访问性和异步方面进行了改进。建议大家直接访问官网了解详情。
原文:https://fly.io/django-beats/new-goodies-in-django-50/
***
- EOF -
推荐阅读 点击标题可跳转
4、高效的终极秘诀