我有两个模型,一个父模型和一个子模型,我希望它们存在于数据库的不同表中,但是当我返回客户端时,它们将它们表示在同一个对象(平面)上。我已经尝试了一些选项,包括SerializerMethodFields(SMF)和ReadOnlyFields,但是它们都导致数据库查询过多。
我还尝试了嵌套对象,然后稍后对其进行展平,这确实起作用,但是由于Django的序列化程序的工作方式,导致了更多的代码。这让我感到应该有一种更好的方法来完成此任务。
以下是使用SMF方法调用序列化程序的示例:
# in the view
class ListFoo(APIView):
def get(self, request):
foo = Foo.objects.prefetch_related('bar').all()
serializer = FooSerializer(foo, may=True)
Response(serializer.data, status=status.HTTP_200_OK)
# in the serializer
class FooSerializer(ModelSerializer):
employer = SerializerMethodField()
position = SerializerMethodField()
class Meta:
model = Foo
fields('id', 'name', 'employer', 'position')
def get_employer(self, foo):
# use related manager to access related object
return foo.bar.last().employer
def get_position(self, foo):
# use related manager to access related object
return foo.bar.last().position
# in the models
class Foo(models.Model):
name = models.CharField(max_length=150)
class Bar(models.Model):
employer = models.CharField(max_length=150)
position = models.CharField(max_length=150)
foo = models.ForeignKey(
Foo,
related_name='bar',
unique=False,
on_delete=models.CASCADE
)
现在上面的代码可以工作了,但是如果我查询许多Foo对象,它将查询+1 bar我需要从bar访问的每个属性。我正在尝试找到一种在全局化存储/缓存数据的方法,当序列化程序初始化时,我可能会查询该数据。
对更有效或更有效的方法有何想法?
更新:我基本上已经弄清楚了,明天将发布解决方案。我不知道有多少种方法可以对通过预取创建的查询集进行不正确的操作。
答案 0 :(得分:0)
上面的代码的问题是,当我使用最后一种方法对queryset进行操作时
def get_employer(self, foo):
# use related manager to access related object
return foo.bar.last().employer
每次调用get_employer
时,Django都会查询数据库。读完documentation和几个other answers之后,我意识到使用all()
方法是重要的下一步,因为它将返回相关对象的查询集(在视图中预先提供) 。对我来说很奇怪,您不能像通常使用last()
或filter()
这样的方法直接对相关对象进行操作,但是可以对其进行迭代和过滤。
解决我的问题所需要做的就是返回related_manager的queryset中的最后一个项目,我通过使用下标返回queryset中的最后一个对象来实现这一点。我敢肯定,有一些方法可以减少代码,但是可以。
def get_employer(self, foo):
qs = foo.bar.all()
last = query.count()
obj = model_to_dict(qs[last-1])
return obj.get('employer')
答案 1 :(得分:0)
不太清楚,我了解,但是您似乎想要Foo对象的最后一位雇主。因此,您的SerializerMethodField可能如下所示(假设此处的创建日期降序反映了您的最新雇主)
def get_employer(self, foo):
obj= foo.bar.all().order_by('-created')[0]
return obj.employer