Django:当嵌套目标继承子类型时,我如何序列化嵌套关系?

pcww981p  于 2023-08-08  发布在  Go
关注(0)|答案(1)|浏览(83)

如何使用事务为嵌套关系创建序列化程序,其中嵌套关系具有多个子类型?我已经通读了文档,似乎没有很好地涵盖。
我们有一个投保房主的应用程序,具有以下结构:

class Homeowner(models.Model):
    name = models.CharField(max_length = 100, null = True, blank = True)
    property = models.ForeignKey(Property, null = True)
    
class Property(models.Model):
    address = models.CharField(max_length = 500, null = True, blank = True)
    # lots more removed. Not an abstract model.

class House(Property):
    number_of_floors = models.IntegerField(default = 1)
    
class Condo(Property):
    has_doorman = models.BooleanField(default = False)

字符串
(This严格地说是作秀;不同的房屋类型有很多折扣或负债字段。)多年来,我们的方法是手工输入房产,然后将房主作为两个独立的阶段输入,并将它们与房主页面上的房产搜索对话框链接起来。一直都是零或一对一的关系:一个房产可能有零个或一个房主,一个房主可能有零个或一个房产。
订单已经下来,以创建一个统一的体验,房主可以自己填写:输入所有内容,房主和财产,并一起验证它们,如果有任何失败,则回滚所有内容。可悲的是,太多的依赖于现有的结构来重新安排模型。据我所知(我已经和Django搏斗了好几年了),如果我只是有一个属性,它会像这样简单:

class PolicySerializer(serializers.ModelSerializer):
    property = PropertySerializer()
    
    class Meta:
        model = Homeowner
        fields = ['name', 'property']

    def create(self, validated_data):
        property_data = validated_data.pop('property')
        property = Property.objects.create(**property_data)
        Homeowner.objects.create(property=property, **validated_data)
        return homeowner


检测房主填写了什么类型的属性表单是相当简单的,但是一旦我知道了,我如何编写create()方法来路由到正确类型的属性子类型模型?
如果Homeowner对象验证失败,如何确保Property对象不会被闲置?这是自动处理的吗?还是应该用@transaction.atomic修饰create()方法?

jhiyze9q

jhiyze9q1#

您应该为每个属性子类型(House和Condo)定义序列化程序,然后为属性模型创建序列化程序,该序列化程序将根据输入数据动态确定子类型,最后您应该创建Homeowner序列化程序,包括属性序列化程序。
你的属性序列化器应该是这样的:

class HouseSerializer(serializers.ModelSerializer):
    class Meta:
        model = House
        fields = ['number_of_floors']

class CondoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Condo
        fields = ['has_doorman']

字符串
现在我们可以创建属性序列化器来处理不同的sybtypes:

class PropertySerializer(serializers.ModelSerializer):
    # Custom field to hold the subtype serializer
    subtype_serializer = serializers.SerializerMethodField()

    class Meta:
        model = Property
        fields = ['address', 'subtype', 'subtype_serializer']

    def get_subtype_serializer(self, obj):
        # Determine the subtype of the property and return the corresponding serializer
        if isinstance(obj, House):
            return HouseSerializer(instance=obj).data
        elif isinstance(obj, Condo):
            return CondoSerializer(instance=obj).data
        else:
            return {}

    def create(self, validated_data):
        subtype_data = validated_data.pop('subtype', None)

        # Determine the subtype of the property based on the input data
        subtype = None
        if subtype_data:
            if subtype_data.get('number_of_floors'):
                subtype = 'house'
            elif subtype_data.get('has_doorman') is not None:
                subtype = 'condo'

        # Create the Property object
        property_obj = Property.objects.create(subtype=subtype, **validated_data)

        # Create the specific subtype object and associate it with the Property object
        if subtype == 'house':
            House.objects.create(property=property_obj, **subtype_data)
        elif subtype == 'condo':
            Condo.objects.create(property=property_obj, **subtype_data)

        return property_obj


最后是HomeOwner序列化器:

class HomeownerSerializer(serializers.ModelSerializer):
    property = PropertySerializer()

    class Meta:
        model = Homeowner
        fields = ['name', 'property']

    @transaction.atomic
    def create(self, validated_data):
        property_data = validated_data.pop('property')
        property_serializer = PropertySerializer(data=property_data)
        property_serializer.is_valid(raise_exception=True)
        homeowner = Homeowner.objects.create(**validated_data)

        # Create the Property object and its subtype
        property_obj = property_serializer.save()

        # Associate the Property object with the Homeowner object
        homeowner.property = property_obj
        homeowner.save()

        return homeowner


你应该使用@transaction.atomic decorator来确保如果创建过程的任何部分失败,整个事务将被回滚。
您可能存在一些验证错误(views.py),需要处理
您可以通过使用模型继承和序列化程序来自动处理子类型,使其更具动态性和可伸缩性。
我们可以像这样使用模型继承:

class Property(models.Model):
    address = models.CharField(max_length=500, null=True, blank=True)
    # you can add common fields and methods for all property types here.

class House(Property):
    number_of_floors = models.IntegerField(default=1)

class Condo(Property):
    has_doorman = models.BooleanField(default=False)


现在我们可以使用序列化器来处理不同的子类型:

class HouseSerializer(serializers.ModelSerializer):
    class Meta:
        model = House
        fields = ['number_of_floors']

class CondoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Condo
        fields = ['has_doorman']

class PropertySerializer(serializers.ModelSerializer):
    class Meta:
        model = Property
        fields = ['address']

    def to_representation(self, instance):
        # detect the subtype dynamically and use the appropriate serializer
        if isinstance(instance, House):
            serializer = HouseSerializer(instance=instance)
        elif isinstance(instance, Condo):
            serializer = CondoSerializer(instance=instance)
        else:
            serializer = super().to_representation(instance)
        return serializer.data

    def to_internal_value(self, data):
        # detect the subtype dynamically and create the appropriate instance
        property_type = data.get('type', None)
        if property_type == 'house':
            serializer = HouseSerializer(data=data)
        elif property_type == 'condo':
            serializer = CondoSerializer(data=data)
        else:
            serializer = super().to_internal_value(data)
        serializer.is_valid(raise_exception=True)
        return serializer.validated_data

class PolicySerializer(serializers.ModelSerializer):
    property = PropertySerializer()

    class Meta:
        model = Homeowner
        fields = ['name', 'property']

    @transaction.atomic
    def create(self, validated_data):
        property_data = validated_data.pop('property')
        property_instance = Property.objects.create(**property_data)
        homeowner = Homeowner.objects.create(property=property_instance, **validated_data)
        return homeowner


现在你不需要手动更新每个子类型的if子句,相反,序列化器会根据数据中提供的type字段自动检测子类型,序列化器会根据检测到的子类型处理其余的子类型。
注意,你应该给传入的数据分配一个type字段来确定子类型。
请记住,在向API发送数据时要包含type字段,以便序列化程序可以正确识别子类型,如下所示:

{
    "name": "Kiarash Gh",
    "property": {
        "type": "house",
        "number_of_floors": 1,
        "address": "256 Main St"
    }
}


我希望这对你有帮助。

相关问题