首页 > 解决方案 > 如何在 django rest 框架中使用不同的序列化器类序列化每个外键对象

问题描述

所以我想知道是否可以在 django rest 框架中用不同的序列化器序列化每个外键对象。

我的意思是:

我有我的模型

class KingdomModel(models.Model):
    kingdom_name = models.CharField(max_length=32)
    owner = models.OneToOneField(User, on_delete=models.CASCADE)
    faction = models.CharField(max_length=10)
    

class CityModel(models.Model):
    kingdom = models.ForeignKey(KingdomModel, on_delete=models.CASCADE, related_name="cities")
    city_name = models.CharField(max_length=32)
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    """
    ... other fields aswell
    """


class ArmyModel(models.Model):
    home_city = models.ForeignKey(CityModel, on_delete=models.CASCADE, null=True, related_name="own_troops")
    current_city = models.ForeignKey(CityModel, on_delete=models.CASCADE, null=True, related_name="all_troops", blank=True)
    status = models.CharField(max_length=32)
    action_done_time = models.DateTimeField(default=None, null=True, blank=True)
    target_city = models.ForeignKey(CityModel, on_delete=models.CASCADE, null=True, related_name="incoming_troops", default=None, blank=True)

    # Shared troops
    settlers = models.IntegerField(default=0)

    # Gaul troops
    pikemen = models.IntegerField(default=0)
    swordmen = models.IntegerField(default=0)
    riders = models.IntegerField(default=0)

    # Roman troops
    legionaries = models.IntegerField(default=0)
    praetorian = models.IntegerField(default=0)


我正在尝试根据王国派系对军队进行序列化。在谈论own_troops时效果很好,因为它们总是会使用相同的序列化程序进行序列化,就像这样。

class CitySerializer(serializers.ModelSerializer):

    own_troops = serializers.SerializerMethodField()
    incoming_troops = serializers.SerializerMethodField()

    def get_own_troops(self, city_obj):
        if(KingdomModel.objects.get(owner=city_obj.owner).faction == "Gaul"):
            return GaulTroopsSerializer(instance=city_obj.own_troops, context=self.context, many=True, required=False, read_only=False).data
        elif(KingdomModel.objects.get(owner=city_obj.owner).faction == "Roman"):
            return RomanTroopsSerializer(instance=city_obj.own_troops, context=self.context, many=True, required=False, read_only=False).data
class RomanTroopsSerializer(serializers.ModelSerializer):
    class Meta:
        model = ArmyModel
        fields = ['id', 'home_city', 'current_city', 'target_city', 'status', 'action_done_time', 'settlers', 'legionaries', 'praetorian']

class GaulTroopsSerializer(serializers.ModelSerializer):
    class Meta:
        model = ArmyModel
        fields = ['id', 'home_city', 'current_city', 'target_city', 'status', 'action_done_time', 'settlers', 'pikemen', 'swordmen', 'riders']

但是,如果我尝试将相同的逻辑应用于序列化incoming_troops,它将始终使用第一个序列化程序序列化列表中的所有对象。这是我根据关系中的数据用不同的序列化器序列化每个外键的绝望尝试。


    def get_incoming_troops(self, city_obj):
        for data in GaulTroopsSerializer(instance=city_obj.incoming_troops, context=self.context, many=True, required=False, read_only=False).data:
            print(data)
            home_city_obj = CityModel.objects.get(id=data['home_city'])
            if(KingdomModel.objects.get(owner=home_city_obj.owner).faction == "Gaul"):
                return GaulTroopsSerializer(instance=city_obj.incoming_troops, context=self.context, many=True, required=False, read_only=False).data
            else:
                return RomanTroopsSerializer(instance=city_obj.incoming_troops, context=self.context, many=True, required=False, read_only=False).data

    class Meta:
        model = CityModel
        fields = ['id', 'owner', 'city_name', 'x_coordinate', 'y_coordinate', 'last_updated', 'max_warehouse_capacity', 'max_grain_silo_capacity', 'wood_ammount', 'wheat_ammount', 'stone_ammount', 'iron_ammount', 'resource_fields', 'buildings','incoming_troops', 'own_troops',  'all_troops']
        read_only_fields = ['id', 'max_warehouse_capacity', 'max_grain_silo_capacity']

我知道我可以为所有不同派系的军队提供多个模型,但现在我只是想知道这在 django / drf 中是否可行?

标签: pythondjangodjango-rest-frameworkdjango-serializer

解决方案


回答我自己的问题,因为我得到了它的工作,我所做的如下:

首先,我刮掉了多个部队序列化器。并且只有一个军队序列化器,我可以根据派系切换字段。

这是我的 ArmySerializer 现在

class ArmySerializer(serializers.ModelSerializer):
class Meta:
    model = ArmyModel
    fields = ['id', 'home_city', 'current_city', 'target_city', 'status', 'action_done_time']  
    

def to_representation(self, instance):
    try:
        del self.fields # Clear the cache
    except AttributeError:
        pass

    if("faction" in self.context and len(self.context['faction']) > 0):
        print(self.context['faction'])
        self.fields['settlers'] = serializers.IntegerField()
        if(self.context['faction'][0] == "Gaul"):
            self.fields['pikemen'] = serializers.IntegerField()
            self.fields['swordmen'] = serializers.IntegerField()
            self.fields['riders'] = serializers.IntegerField()
        elif(self.context['faction'][0] == "Roman"):
            self.fields['legionaries'] = serializers.IntegerField()
            self.fields['praetorian'] = serializers.IntegerField()
        if(len(self.context['faction']) > 1):
            self.context['faction'].pop(0)
    
    
    return super().to_representation(instance)

在 CitySerializer 中,我正在填充 context['faction'] 列表,如下所示:

class CitySerializer(serializers.ModelSerializer):

own_troops = serializers.SerializerMethodField()
incoming_troops = serializers.SerializerMethodField()


def get_own_troops(self, instance):
    if(KingdomModel.objects.get(owner=instance.owner).faction == "Gaul"):
        self.context["faction"] = ["Gaul"]
        return ArmySerializer(instance=instance.own_troops, context=self.context, many=True, required=False, read_only=False).data
    elif(KingdomModel.objects.get(owner=instance.owner).faction == "Roman"):
        self.context["faction"] = ["Roman"]
        return ArmySerializer(instance=instance.own_troops, context=self.context, many=True, required=False, read_only=False).data

def get_incoming_troops(self, city_obj):
    self.context['faction'] = []
    for data in ArmySerializer(instance=city_obj.incoming_troops, context=self.context, many=True, required=False, read_only=False).data:
        home_city = CityModel.objects.get(id=data['home_city'])
        sender_faction = KingdomModel.objects.get(owner=home_city.owner).faction
        if(sender_faction == "Gaul"):
            self.context['faction'] += ["Gaul"]
        else:
            self.context['faction'] += ["Roman"]
    return ArmySerializer(instance=city_obj.incoming_troops, context=self.context, many=True, required=False, read_only=False).data

另外应该说,这在使用 POST 请求创建军队时引入了一个新问题。在 to_representation 方法中动态添加的字段默认不验证,因此它们不存在于 valid_data 中。可能有一种方法可以覆盖验证,但我现在只是从原始请求数据中获取它们,它似乎工作正常。


推荐阅读