是否可以编写一个QuerySet方法来修改数据集但延迟评估(类似于prefetch_related)?

时间:2015-02-23 18:29:10

标签: django django-models django-queryset

我正在处理类似于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记录始终具有用户记录。

一些注意事项:

  • 这个例子已经高度简化了。实际上有很多表链接到遗留数据,因此,例如,尽管Foo和User之间存在一对一的关系,但在某些情况下,查询集将具有多个具有相同的Foo记录键。
  • 旧数据库位于不同的服务器和服务器平台上,因此我无法使用数据库服务器本身链接这两个表
  • 理想情况下,我希望缓存用户数据,这样即使对记录进行排序或切片,我也不必再次重新运行foo查询。

基本上,我不太了解查询集的惰性评估如何工作以进行必要的编码。我在source code for django.db.models.query上来回跳跃,但它确实是一个相当密集的阅读,我希望有人在那里工作已经可以提供一些指示。

0 个答案:

没有答案