FieldError(f"Field {field.model._meta.object_name}.{field.name} cannot be both ""deferred and traversed using select_related at the same time.")的处理方案
报错的原因
FieldError: "Field 'X' cannot be both deferred and traversed using select_related at the same time." 表明在使用 select_related() 方法查询时,与之相关的字段已经被排除在延迟加载字段之外(deferred)了。
Django 提供了一种延迟加载字段的机制,它可以通过使用 only() 或 defer() 方法来排除在查询中不需要使用的字段,这样可以减少数据库查询的次数,提高查询性能。
而 select_related() 方法则是查询时预先加载关联表中的数据,可以避免使用关联表时产生多余的数据库查询。
但是,你使用了 select_related() 方法时,若之前已经使用了 defer() 或者 only()方法排除了需要关联表中的字段,会导致这个错误。
解决方法就是取消 defer() 或 only()方法的使用,或者在 select_related() 中加入需要使用的字段。
还有一种解决方法就是使用 prefetch_related() 代替 select_related(),因为它是支持与defer()和only() 一起使用的。
如何解决
解决这个问题有以下几种方法:
1. 取消 defer() 或 only() 方法的使用, 使用 select_related() 时加入需要关联表中的字段
MyModel.objects.select_related('related_field').filter(...)
2. 使用 prefetch_related() 代替 select_related()
MyModel.objects.prefetch_related('related_field').filter(...)
3. 如果你需要更灵活的选择哪些字段需要延迟加载,你可以使用 Subquery() 或者 OuterRef(),这两个类都是在 Django 2.0 中引入的,用来解决这个问题
from django.db.models import Subquery, OuterRef
MyModel.objects.filter(pk=OuterRef('related_field__pk')).only('pk')
MyModel.objects.filter(pk=Subquery(MyModel.objects.filter(related_field__pk=OuterRef('pk')).values('pk')))
这几种方法任选其一即可解决此问题。注意使用 prefetch_related() 时会单独发一次数据库询问,如果数据量比较大可能会影响性能。
使用例子
第一种解决方法的例子:
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
#之前
# Author.objects.defer('name').select_related('book')
# 会报错
#FieldError: Field 'name' cannot be both deferred and traversed using select_related at the same time
#可以改成
Author.objects.select_related('book')
# 或
Author.objects.only('id').select_related('book')
第二种解决方法的例子:
#之前
# Author.objects.defer('name').select_related('book')
# 会报错
#FieldError: Field 'name' cannot be both deferred and traversed using select_related at the same time
#可以改成
Author.objects.prefetch_related('book')
第三种解决方法的例子:
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
from django.db.models import Subquery, OuterRef
books = Book.objects.filter(
author=Subquery(Author.objects.filter(name='John Smith').values('id'))
)
books2 = Book.objects.filter(author__name='John Smith')
这只是为了给出大致的例子, 请根据实际情况进行调整,选择一种合适的方法解决问题。