Django:可查询的计算字段
Django: queryable computed fields
是否有 "best" 处理可查询计算字段的方法?
例如:
from django.db import models
class Person(models.Model):
given_name = models.CharField(max_length=30)
family_name = models.CharField(max_length=30)
NAME_ORDER_CONVENTION_CHOICES = (
# "Eastern" name order: family name followed by given name
('E', 'Eastern'),
# "Western" name order: given name followed by family name
('W', 'Western')
)
name_order_convention = models.CharField(
length=1,
choices=NAME_ORDER_CONVENTION_CHOICES,
)
@property
def full_name(self):
"""
Return full name, calculated on the basis of given name,
family name and name order convention.
"""
template = "{} {}"
if self.name_order_convention == "W":
return template.format(self.given_name, self.family_name)
return template.format(self.family_name, self.given_name)
这使您能够为任何 Person
获取 full_name
,但是如果您想要基于全名进行数据库查询,则需要编写具有隐式知识的查询属性 是如何计算的。这似乎违反了 DRY,因为不再有任何中心位置可以更改计算字段的计算方式 - 您必须更改 full_name
属性,并且根据 full_name
工作原理的隐含知识进行查询的任何地方。
我能想到的主要替代方法是覆盖 save()
方法以在每次更新时更新这些字段。
from django.db import models
class Person(models.Model):
given_name = models.CharField(max_length=30)
family_name = models.CharField(max_length=30)
NAME_ORDER_CONVENTION_CHOICES = (
('E', 'Eastern'),
('W', 'Western')
)
name_order_convention = models.CharField(
length=1,
choices=NAME_ORDER_CONVENTION_CHOICES,
)
# Computed fields
full_name = models.CharField(max_length=60)
def _calculate_full_name(self):
template = "{} {}"
if self.name_order_convention == "W":
self.full_name = template.format(self.given_name, self.family_name)
else:
self.full_name = template.format(self.family_name, self.given_name)
def save(self, *args, **kwargs):
self._calculate_full_name()
super(Model, self).save(*args, **kwargs)
是否有一个常见的 "best-practice" 可查询计算字段的解决方案,我可能会丢失?
我可以看到的一种方式:在数据库中创建 view 并在 django 中使用连接的 full_name 而不是托管模型,将用于查询(Person
模型也应该存在于 crud 操作中):
查看 (postgresql)
CREATE OR REPLACE VIEW appname_viewperson AS
SELECT id,
given_name,
family_name,
given_name || ' ' || family_name AS full_name,
FROM appname_person
非托管模型:
class ViewPerson(models.Model):
given_name = models.CharField(max_length=30)
family_name = models.CharField(max_length=30)
full_name = models.CharField(max_length=60)
class Meta:
managed = False
答案可能在于 django 相当新的查询注释:
https://docs.djangoproject.com/en/1.11/ref/models/expressions/#using-f-with-annotations
company = Company.objects.annotate(
chairs_needed=F('num_employees') - F('num_chairs'))
是否有 "best" 处理可查询计算字段的方法?
例如:
from django.db import models
class Person(models.Model):
given_name = models.CharField(max_length=30)
family_name = models.CharField(max_length=30)
NAME_ORDER_CONVENTION_CHOICES = (
# "Eastern" name order: family name followed by given name
('E', 'Eastern'),
# "Western" name order: given name followed by family name
('W', 'Western')
)
name_order_convention = models.CharField(
length=1,
choices=NAME_ORDER_CONVENTION_CHOICES,
)
@property
def full_name(self):
"""
Return full name, calculated on the basis of given name,
family name and name order convention.
"""
template = "{} {}"
if self.name_order_convention == "W":
return template.format(self.given_name, self.family_name)
return template.format(self.family_name, self.given_name)
这使您能够为任何 Person
获取 full_name
,但是如果您想要基于全名进行数据库查询,则需要编写具有隐式知识的查询属性 是如何计算的。这似乎违反了 DRY,因为不再有任何中心位置可以更改计算字段的计算方式 - 您必须更改 full_name
属性,并且根据 full_name
工作原理的隐含知识进行查询的任何地方。
我能想到的主要替代方法是覆盖 save()
方法以在每次更新时更新这些字段。
from django.db import models
class Person(models.Model):
given_name = models.CharField(max_length=30)
family_name = models.CharField(max_length=30)
NAME_ORDER_CONVENTION_CHOICES = (
('E', 'Eastern'),
('W', 'Western')
)
name_order_convention = models.CharField(
length=1,
choices=NAME_ORDER_CONVENTION_CHOICES,
)
# Computed fields
full_name = models.CharField(max_length=60)
def _calculate_full_name(self):
template = "{} {}"
if self.name_order_convention == "W":
self.full_name = template.format(self.given_name, self.family_name)
else:
self.full_name = template.format(self.family_name, self.given_name)
def save(self, *args, **kwargs):
self._calculate_full_name()
super(Model, self).save(*args, **kwargs)
是否有一个常见的 "best-practice" 可查询计算字段的解决方案,我可能会丢失?
我可以看到的一种方式:在数据库中创建 view 并在 django 中使用连接的 full_name 而不是托管模型,将用于查询(Person
模型也应该存在于 crud 操作中):
查看 (postgresql)
CREATE OR REPLACE VIEW appname_viewperson AS
SELECT id,
given_name,
family_name,
given_name || ' ' || family_name AS full_name,
FROM appname_person
非托管模型:
class ViewPerson(models.Model):
given_name = models.CharField(max_length=30)
family_name = models.CharField(max_length=30)
full_name = models.CharField(max_length=60)
class Meta:
managed = False
答案可能在于 django 相当新的查询注释:
https://docs.djangoproject.com/en/1.11/ref/models/expressions/#using-f-with-annotations
company = Company.objects.annotate(
chairs_needed=F('num_employees') - F('num_chairs'))