Zag*_*ags 7 python django django-models django-managers
如何使 Django 模型上的字段延迟该模型的所有查询,而不需要对每个查询进行延迟?
该功能于 2014 年被请求作为一项功能,但在 2022 年被拒绝。
除了 Django 原生的功能之外,显而易见的想法是创建一个如下所示的自定义管理器:
class DeferedFieldManager(models.Manager):
def __init__(self, defered_fields=[]):
super().__init__()
self.defered_fields = defered_fields
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs
).defer(*self.defered_fields)
class B(models.Model):
pass
class A(models.Model):
big_field = models.TextField(null=True)
b = models.ForeignKey(B, related_name="a_s")
objects = DeferedFieldManager(["big_field"])
class C(models.Model):
a = models.ForeignKey(A)
class D(models.Model):
a = models.OneToOneField(A)
class E(models.Model):
a_s = models.ManyToManyField(A)
Run Code Online (Sandbox Code Playgroud)
但是,虽然这适用于A.objects.first()(直接查找),但不适用于B.objects.first().a_s.all()(一对多)、C.objects.first().a(多对一)、D.objects.first().a(一对一)或E.objects.first().a_s.all()(多对多)。
我在这里发现特别令人困惑的是,这是我的对象的默认管理器,这意味着它也应该是反向查找(一对多和多对多)的默认管理器,但这不是在职的。根据Django 文档:
默认情况下,用于反向关系的RelatedManager 是该模型的默认管理器的子类。
测试这一点的一个简单方法是删除应该从数据库延迟的字段,并且OperationalError: no such column如果该字段未正确延迟,代码只会出现错误。要进行测试,请执行以下步骤:
b = B.objects.create()
a = A.objects.create(b=b)
c = C.objects.create(a=a)
d = D.objects.create(a=a)
e = E.objects.create()
e.a_s.add(a)
Run Code Online (Sandbox Code Playgroud)
big_fieldmanage.py makemigrationsmanage.py migratebig_fieldfrom django.db import OperationalError
def test(test_name, f, attr=None):
try:
if attr:
x = getattr(f(), attr)
else:
x = f()
assert isinstance(x, A)
print(f"{test_name}:\tpass")
except OperationalError:
print(f"{test_name}:\tFAIL!!!")
test("Direct Lookup", A.objects.first)
test("One-to-Many", B.objects.first().a_s.first)
test("Many-to-One", C.objects.first, "a")
test("One-to-One", D.objects.first, "a")
test("Many-to-Many", E.objects.first().a_s.first)
Run Code Online (Sandbox Code Playgroud)
如果上述测试均通过,则该场地已适当推迟。
我目前得到:
Direct Lookup: pass
One-to-Many: FAIL!!!
Many-to-One: FAIL!!!
One-to-One: FAIL!!!
Many-to-Many: FAIL!!!
Run Code Online (Sandbox Code Playgroud)
@aaron 的答案解决了一半的失败案例。
如果我改变A为:
class Meta:
base_manager_name = 'objects'
Run Code Online (Sandbox Code Playgroud)
我现在从测试中得到以下信息:
Direct Lookup: pass
One-to-Many: FAIL!!!
Many-to-One: pass
One-to-One: pass
Many-to-Many: FAIL!!!
Run Code Online (Sandbox Code Playgroud)
这对于尊敬的查找仍然不起作用。
设置。Meta.base_manager_name\'objects\'
class A(models.Model):\n big_field = models.TextField(null=True)\n b = models.ForeignKey(B, related_name="a_s")\n\n objects = DeferedFieldManager(["big_field"])\n\n class Meta:\n base_manager_name = \'objects\'\nRun Code Online (Sandbox Code Playgroud)\n来自https://docs.djangoproject.com/en/4.1/topics/db/managers/#using-managers-for-lated-object-access:
\n\n\n使用管理器进行相关对象访问
\n\n
Model._base_manager默认情况下,Django在访问相关对象(即)时使用管理器类的实例choice.question,而不是_default_manager相关对象上的。这是因为 Django 需要能够检索相关对象,即使它会被默认管理器过滤掉(因此无法访问)。如果普通的基本管理器类 (
\ndjango.db.models.Manager) 不适合您的情况,您可以通过设置 来告诉 Django 使用哪个类Meta.base_manager_name。
问题中的“一对多”情况是反向多对一。
\nDjango 对管理器类进行子类化以覆盖该行为,然后实例化它 \xe2\x80\x94 ,而无需将参数defered_fields传递给__init__since\n django.db.models.Manager,并且其子类不应具有参数。
因此,你需要类似的东西:
\ndef make_defered_field_manager(defered_fields):\n class DeferedFieldManager(models.Manager):\n def get_queryset(self, *args, **kwargs):\n return super().get_queryset(*args, **kwargs).defer(*defered_fields)\n return DeferedFieldManager()\nRun Code Online (Sandbox Code Playgroud)\n用法:
\n# objects = DeferedFieldManager(["big_field"])\nobjects = make_defered_field_manager(["big_field"])\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
1852 次 |
| 最近记录: |