简体   繁体   English

序列化DRF中的相关字段

[英]Serializing related fields in DRF

I have two models, a parent model and a child model, that I want to exist on separate tables in the database but represent them on the same object (flat) when I return to the client. 我有两个模型,一个父模型和一个子模型,我希望它们存在于数据库的不同表中,但是当我返回客户端时,它们将它们表示在同一个对象(平面)上。 I've tried a few options with this including SerializerMethodFields (SMF) and ReadOnlyFields but they both result in too many db queries. 我已经尝试了一些选项,包括SerializerMethodFields(SMF)和ReadOnlyFields,但是它们都导致太多的数据库查询。

I've also tried nesting the object and then flattening it later which does work, but results in a lot more code due to the way Django's serializers work. 我还尝试过嵌套对象,然后稍后对其进行展平,这确实起作用,但是由于Django的序列化程序的工作方式,导致了更多的代码。 It left me feeling that there should be a better way to accomplish this. 这让我感到应该有一种更好的方法来完成此任务。

Here's an example of a call to the serializer using the SMF approach: 这是使用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
    )

Now that code above works but if I'm querying many Foo objects, it will query +1 bar each attribute I need to access from bar. 现在上面的代码可以工作了,但是如果我要查询许多Foo对象,它将查询+1 bar我需要从bar访问的每个属性。 I'm trying to find a way to have a global store/cache of data that I potentially query when the serializer initializes. 我正在尝试找到一种方法来对序列化程序进行初始化时可能查询的数据进行全局存储/缓存。

Any ideas on something more efficient or a better approach? 对更有效或更有效的方法有什么想法吗?

UPDATE: I have this mostly figured out and will post a solution tomorrow. 更新:我基本上已经弄清楚了,明天将发布解决方案。 I didn't realize how many ways there were to improperly operate on a queryset that was created with prefetch. 我不知道有多少种方法可以对使用预取创建的查询集进行不正确的操作。

The problem with the code above is that when I operated on the queryset with the last method 上面的代码的问题是,当我使用最后一种方法对queryset进行操作时

def get_employer(self, foo):
    # use related manager to access related object
    return foo.bar.last().employer

Django queried the database each time get_employer was called. 每次调用get_employer ,Django都会查询数据库。 After reading the documentation and several other answers , I realized that using the all() method is an important next step as it will return the queryset (which was prefeched in the view) for the related objects. 阅读文档其他几个答案之后 ,我意识到使用all()方法是下一步的重要步骤,因为它将返回相关对象的查询集(在视图中预先提供)。 It's strange to me that you can't operate directly on the related objects as you would normally with a method like last() or filter() but you can iterate over them and filter. 对我来说很奇怪,您不能像通常使用last()filter()这样的方法直接对相关对象进行操作,但是可以对其进行迭代和过滤。

All I needed to do to solve my problem was to return the last item in the related_manager's queryset and I accomplished this by returning the last object in the queryset using subscript. 解决我的问题所需要做的就是返回related_manager的queryset中的最后一个项目,我通过使用下标返回queryset中的最后一个对象来实现这一点。 I'm sure there's some ways to decrease code but it works. 我敢肯定,有一些方法可以减少代码,但是可以。

def get_employer(self, foo):
    qs = foo.bar.all()
    last = query.count()
    obj = model_to_dict(qs[last-1])

    return obj.get('employer')

Not quite sure, I understand, but it looks like you want the last employer for your Foo object. 不太清楚,我了解,但您似乎想要Foo对象的最后一位雇主。 So then your SerializerMethodField could be as follows ( assuming here that created-date descending reflects your most recent employer) 因此,您的SerializerMethodField可能如下所示(假设此处的创建日期降序反映了您的最新雇主)

def get_employer(self, foo):
   obj= foo.bar.all().order_by('-created')[0]
   return obj.employer

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM