Django 从所有查询中排除字段

Django exclude field from all queries

我是 运行 Heroku 上的 Django,具有零停机时间功能。这意味着在部署期间,同一数据库上有两个版本的代码 运行(旧的和新的)。这就是为什么我们需要避免任何向后不兼容的迁移。

是否可以从给定模型的 Django 查询中排除字段?

假设我们有一个模型(版本 1):

class Person(models.Model):
    name = models.CharField()
    address = models.TextField()

在未来的某个时候,我们希望将地址移动到单独的 table。我们知道我们不应该删除旧代码的字段,因此 Person 模型可能看起来像(版本 2):

class Person(models.Model):
    name = models.CharField()
    address = models.ForeignKey(Address)
    _address = models.TextField(db_name='address')

这样,如果旧代码将查询地址,即使数据库已迁移,它也会从 Person table 获取地址(这将是一个旧值,但假设这不是一个大问题)。

我现在如何安全地删除 _address 字段?如果我们将部署版本 3 并删除 _address 字段,那么版本 2 的代码仍将尝试在 select 上获取 _address,即使它没有在任何地方使用并且会失败 "No such column" 例外。

有没有办法防止这种情况发生并在版本 2 的代码中将某些字段标记为 "non-fetchable"?所以版本 2 不会删除字段,但不会再获取它,版本 3 会删除字段。

是的,你可以做到:

QuerySet.defer():

"在一些复杂的数据建模情况下,您的模型可能包含很多字段,其中一些可能包含大量数据(例如,文本字段),或者需要昂贵的处理才能将它们转换为 Python 对象。如果您在某些情况下使用查询集的结果,而您在最初获取数据时不知道是否需要这些特定字段,则可以告诉 Django 不要从中检索它们数据库。 - docs

Entry.objects.defer("headline", "body")

从 django 1.8 开始:使用 values_list. You can only include fields that you want. You can also use Queryset.only() and Queryset.defer() 优化您的查询集查询。您也可以链接 defer() 调用

Entry.objects.values_list('id', 'headline')

您可以为 defer 您的所有查询集的特定 field/fields 使用自定义对象管理器。

class CustomManager(models.Manager):
    def get_queryset(self):
        return super(CustomManager, self).get_queryset().defer('_address',)
class Person(models.Model):
    name = models.CharField()
    address = models.ForeignKey(Address)
    _address = models.TextField(db_name='address')

    objects = CustomManager()

之后,在您针对 Person 模型的任何查询集中,默认情况下将不会在查询中包含 _address 字段。