Python django rest框架、多任务模型继承、模型序列化程序和嵌套序列化程序

Python django rest框架、多任务模型继承、模型序列化程序和嵌套序列化程序,python,django,django-models,django-rest-framework,Python,Django,Django Models,Django Rest Framework,我在文档或互联网站上找不到此信息。 最新的django rest框架,django 1.6.5 如何创建可以处理嵌套序列化程序的ModelSerializer,其中嵌套模型是使用多表继承实现的 e、 g 这显然不起作用,但说明了我在这里要做的事情。 在OtherModelSerializer中,我希望mybasemodel_设置为序列化ModelA或ModelB的特定表示,具体取决于我们所拥有的 如果重要的话,我还将使用django.model_utils和inheritancemanager,

我在文档或互联网站上找不到此信息。
最新的django rest框架,django 1.6.5

如何创建可以处理嵌套序列化程序的ModelSerializer,其中嵌套模型是使用多表继承实现的

e、 g

这显然不起作用,但说明了我在这里要做的事情。
在OtherModelSerializer中,我希望mybasemodel_设置为序列化ModelA或ModelB的特定表示,具体取决于我们所拥有的

如果重要的话,我还将使用django.model_utils和inheritancemanager,这样我就可以检索一个queryset,其中每个实例都已经是相应子类的实例


谢谢

我可以通过创建一个自定义的relatedfield来做到这一点

class MyBaseModelField(serializers.RelatedField):
    def to_native(self, value):
        if isinstance(value, ModelA):
            a_s = ModelASerializer(instance=value)
            return a_s.data
        if isinstance(value, ModelB):
            b_s = ModelBSerializer(instance=value)
            return b_s.data

        raise NotImplementedError


class OtherModelSerializer(serializer.ModelSerializer):
    mybasemodel_set = MyBaseModelField(many=True)

    class Meta:
        model = OtherModel
        fields = # make sure we manually include the reverse relation (mybasemodel_set, )
我确实担心为每个对象实例化一个序列化程序是一种非常昂贵的反向关系查询集,所以我想知道是否有更好的方法来实现这一点

我尝试的另一种方法是动态更改uuu init_uuuuuuu中MyBaseModelSerializer上的model字段,但我遇到了这里描述的问题:

我正在尝试使用一种解决方案,该解决方案涉及不同模型子类的不同序列化器子类:

class MyBaseModelSerializer(serializers.ModelSerializer):

    @staticmethod
    def _get_alt_class(cls, args, kwargs):
        if (cls != MyBaseModel):
            # we're instantiating a subclass already, use that class
            return cls

        # < logic to choose an alternative class to use >
        # in my case, I'm inspecting kwargs["data"] to make a decision
        # alt_cls = SomeSubClass

        return alt_cls

    def __new__(cls, *args, **kwargs):
        alt_cls = MyBaseModel.get_alt_class(cls, args, kwargs)
        return super(MyBaseModel, alt_cls).__new__(alt_cls, *args, **kwargs)

    class Meta:
        model=MyBaseModel

class ModelASerializer(MyBaseModelSerializer):
    class Meta:
        model=ModelA

class ModelBSerializer(MyBaseModelSerializer):
    class Meta:
        model=ModelB
类MyBaseModelSerializer(serializers.ModelSerializer):
@静力学方法
定义获取替换类(cls、ARG、kwargs):
如果(cls!=MyBaseModel):
#我们已经实例化了一个子类,请使用该类
返回cls
#<选择要使用的替代类的逻辑>
#在我的例子中,我正在检查kwargs[“数据”]以做出决定
#alt_cls=SomeSubClass
返回alt_cls
定义(cls,*ARG,**kwargs):
alt_cls=MyBaseModel.get_alt_类(cls、args、kwargs)
返回super(MyBaseModel,alt_cls)。\uu新建(alt_cls,*args,**kwargs)
类元:
model=MyBaseModel
类ModelASerializer(MyBaseModelSerializer):
类元:
model=ModelA
类ModelBSerializer(MyBaseModelSerializer):
类元:
模型=模型B
也就是说,当您尝试实例化类型为
MyBaseModelSerializer
的对象时,实际上会得到其中一个子类的对象,该子类可以正确地序列化(对我来说至关重要的是反序列化)


我刚刚开始使用它,所以可能有一些问题我还没有遇到。

我用了稍微不同的方法解决了这个问题

使用:

  • DRF 3.5.x
  • django模型utils 2.5.x
我的
models.py
如下所示:

class Person(models.Model):
    first_name = models.CharField(max_length=40, blank=False, null=False)
    middle_name = models.CharField(max_length=80, blank=True, null=True)
    last_name = models.CharField(max_length=80, blank=False, null=False)
    family = models.ForeignKey(Family, blank=True, null=True)


class Clergy(Person):
    category = models.IntegerField(choices=CATEGORY, blank=True, null=True)
    external = models.NullBooleanField(default=False, null=True)
    clergy_status = models.ForeignKey(ClergyStatus, related_name="%(class)s_status", blank=True, null=True)


class Religious(Person):
    religious_order = models.ForeignKey(ReligiousOrder, blank=True, null=True)
    major_superior = models.ForeignKey(Person, blank=True, null=True, related_name="%(class)s_superior")


class ReligiousOrder(models.Model):
    name = models.CharField(max_length=255, blank=False, null=False)
    initials = models.CharField(max_length=20, blank=False, null=False)


class ClergyStatus(models.Model):
    display_name = models.CharField(max_length=255, blank=True, null=True)
    description = models.CharField(max_length=255, blank=True, null=True)
基本上-基本模型是“人”模型-人可以是神职人员、宗教人员,也可以两者都不是,只是“人”。而继承
Person
的模型也有特殊的关系

在我的
views.py
中,我使用mixin将子类“注入”到queryset中,如下所示:

class PersonSubClassFieldsMixin(object):

    def get_queryset(self):
        return Person.objects.select_subclasses()

class RetrievePersonAPIView(PersonSubClassFieldsMixin, generics.RetrieveDestroyAPIView):
    serializer_class = PersonListSerializer
    ...
然后真正的“unDRY”部分出现在
serializers.py
中,在这里我声明了“base”PersonListSerializer,但重写了
to_表示法
方法,根据实例类型返回特殊的序列化程序,如下所示:

class PersonListSerializer(serializers.ModelSerializer):

    def to_representation(self, instance):
        if isinstance(instance, Clergy):
            return ClergySerializer(instance=instance).data
        elif isinstance(instance, Religious):
            return ReligiousSerializer(instance=instance).data
        else:
            return LaySerializer(instance=instance).data

    class Meta:
        model = Person
        fields = '__all__'


class ReligiousSerializer(serializers.ModelSerializer):
    class Meta:
        model = Religious
        fields = '__all__'
        depth = 2


class LaySerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = '__all__'


class ClergySerializer(serializers.ModelSerializer):
    class Meta:
        model = Clergy
        fields = '__all__'
        depth = 2
“切换”发生在主序列化程序(
PersonListSerializer
)的
to_表示法中。它查看实例类型,然后“注入”所需的序列化程序。由于
神职人员
宗教
都是从
继承而来的,取回同样是
神职人员
成员的
人,返回所有
字段和所有
神职人员
字段。宗教也一样。如果
个人
既不是
神职人员
也不是
宗教人士
,则只返回基本模型字段


不确定这是否是正确的方法-但它似乎非常灵活,适合我的用例。请注意,我通过不同的视图/序列化程序保存/更新/创建
Person
,因此我不必担心这种类型的设置。

使用Django 3.1,我发现可以覆盖
get\u serializer
而不是
get\u serializer\u class
,在这种情况下,您可以访问实例以及
self.action

默认情况下,
get\u serializer
将调用
get\u serializer\u class
,但此行为可以根据需要进行调整

这比上面提出的解决方案更干净、更简单,所以我将它添加到线程中

例如:

class MySubclassViewSet(viewsets.ModelViewSet):
    # add your normal fields and methods ...

    def get_serializer(self, *args, **kwargs):
        if self.action in ('list', 'destroy'):
            return MyListSerializer(args[0], **kwargs)
        if self.action in ('retrieve', ):
            instance = args[0]
            if instance.name.contains("really?"):  # or check if instance of a certain Model...
                return MyReallyCoolSerializer(instance)
            else return MyNotCoolSerializer(instance)
        # ... 
        return MyListSerializer(*args, **kwargs)  # default

您找到更好的解决方案了吗?更新了注释#1中断开链接的链接:为什么要将get#alt#类设为staticmethod,并最终向其传递cls?有什么原因吗?您在哪里使用django model utils 2.5。?在中,我们可以看到,
Person.objects.select_subclass()
是由Jeff可能忘记在示例中使用的
InheritanceManager
添加的。因此,在
Person
类中,您可能会发现一行包含
objects=InheritanceManager()
。理想情况下,您还需要处理
to\u internal\u value()
class MySubclassViewSet(viewsets.ModelViewSet):
    # add your normal fields and methods ...

    def get_serializer(self, *args, **kwargs):
        if self.action in ('list', 'destroy'):
            return MyListSerializer(args[0], **kwargs)
        if self.action in ('retrieve', ):
            instance = args[0]
            if instance.name.contains("really?"):  # or check if instance of a certain Model...
                return MyReallyCoolSerializer(instance)
            else return MyNotCoolSerializer(instance)
        # ... 
        return MyListSerializer(*args, **kwargs)  # default