我正在处理类似于prefetch_related
的QuerySet类,但允许查询链接未连接数据库中的数据(基本上,链接来自django应用程序的记录'使用共享唯一密钥在遗留系统中记录的数据库,其中包含以下链接:
class UserFoo(models.Model):
''' Uses the django database & can link to User model '''
user = models.OneToOneField(User, related_name='userfoo')
foo_record = models.CharField(
max_length=32,
db_column="foo",
unique=True
) # uuid pointing to legacy db table
@property
def foo(self):
if not hasattr(self, '_foo'):
self._foo = Foo.objects.get(uuid=self.foo_record)
return self._foo
@foo.setter
def foo(self, foo_obj):
self._foo = foo_obj
然后
class Foo(models.Model):
'''Uses legacy database'''
id = models.AutoField(primary_key=True)
uuid = models.CharField(max_length=32) # uuid for Foo legacy db table
…
@property
def user(self):
if not hasattr(self, '_user'):
self._user = User.objects.get(userfoo__foo_record=self.uuid)
return self._user
@user.setter
def user(self, user_obj):
self._user = user_obj
正常运行,一个匹配100个foos的查询(每个用例如1个用户记录)将最终需要101个查询:一个用于获取foos,一个用于每个用户记录(通过查找通过调用每种食物的user
属性来记录用户记录。
为了解决这个问题,我正在制作类似到prefetch_related
的东西,它通过密钥提取查询的所有匹配记录,这意味着我只需要一个额外的查询来获取剩下的记录。
我的代码看起来像这样:
class FooWithUserQuerySet(models.query.QuerySet):
def with_foo(self):
qs = self._clone()
foo_idx = {}
for record in self.all():
foo_idx.setdefault(record.uuid, []).append(record)
users = User.objects.filter(
userfoo__foo_record__in=foo_idx.keys()
).select_related('django','relations','here')
user_idx = {}
for user in users:
user_idx[user.userfoo.foo_record] = user
for fid, frecords in foo_idx.items():
user = user_idx.get(fid)
for frecord in frecords:
if user:
setattr(frecord, 'user', user)
return qs
此有效,但如果稍后修改了查询,则会丢失保存到foo
的任何额外数据 - 也就是说,如果以任何方式重新排序或过滤查询集。
我想创建一种方法来完成我现在正在做的事情,但是等到评估查询时调整的时刻,以便foo
记录始终具有用户记录。
一些注意事项:
基本上,我不太了解查询集的惰性评估如何工作以进行必要的编码。我在source code for django.db.models.query上来回跳跃,但它确实是一个相当密集的阅读,我希望有人在那里工作已经可以提供一些指示。