Blending shared fields between serializers in the Django Rest Framework

I have it:

class GenericCharacterFieldMixin():
    attributes = serializers.SerializerMethodField('character_attribute')
    skills = serializers.SerializerMethodField('character_skill')

    def character_attribute(self, obj):
        character_attribute_fields = {}
        character_attribute_fields['mental'] = {str(trait_item.get()): trait_item.get().current_value
                                                for trait_item in obj.mental_attributes}
        character_attribute_fields['physical'] = {str(trait_item.get()): trait_item.get().current_value
                                                  for trait_item in obj.physical_attributes}
        character_attribute_fields['social'] = {str(trait_item.get()): trait_item.get().current_value
                                                for trait_item in obj.social_attributes}
        return character_attribute_fields

    def character_skill(self, obj):
        character_skill_fields = {}
        character_skill_fields['mental'] = {str(trait_item.get()): trait_item.get().current_value
                                            for trait_item in obj.mental_skills}
        character_skill_fields['physical'] = {str(trait_item.get()): trait_item.get().current_value
                                              for trait_item in obj.physical_skills}
        character_skill_fields['social'] = {str(trait_item.get()): trait_item.get().current_value
                                            for trait_item in obj.social_skills}
        return character_skill_fields


class MageSerializer(GenericCharacterFieldMixin, serializers.ModelSerializer):
    player = serializers.ReadOnlyField(source='player.username')
    arcana = serializers.SerializerMethodField()

    def get_arcana(self, obj):
        if obj:
            return {str(arcana): arcana.current_value for arcana in obj.linked_arcana.all()}

    class Meta:
        model = Mage
        fields = ('id', 'player', 'name', 'sub_race', 'faction', 'is_published',
                  'power_level', 'energy_trait', 'virtue', 'vice', 'morality', 'size',
                  'arcana', 'attributes', 'skills')
        depth = 1

GenericCharacterFieldMixin - these are Mixin fields for characters that are common, that is, common to all types of characters.

I would like my Mage Serializer to have these “mixed”, not c / p, and then between all types of characters (Mage is a type of character), I hope this will increase DRYness in my webapp.

The problem in the model I have is:

class NWODCharacter(models.Model):

    class Meta:
        abstract = True
        ordering = ['updated_date', 'created_date']

    name = models.CharField(max_length=200)
    player = models.ForeignKey('auth.User', related_name="%(class)s_by_user")
    ....

    def save(self, *args, **kwargs):
        ...

    attributes = GenericRelation('CharacterAttributeLink')
    skills = GenericRelation('CharacterSkillLink')

This means that I get this error:

TypeError at /characters/api/mages
<django.contrib.contenttypes.fields.create_generic_related_manager.<locals>.GenericRelatedObjectManager object at 0x00000000051CBD30> is not JSON serializable

The Django Rest Framework thinks I want to serialize my general relationship.

If I rename the fields in the model ( s/attributes/foos/g, s/skills/bars/g), then I get another (less clear?) Error:

ImproperlyConfigured at /characters/api/mages
Field name `attributes` is not valid for model `ModelBase`.

How do I put these methods and fields in a mixin without confusing DRF?

+8
3

,

class GenericCharacterFieldMixin():

to

class GenericCharacterFieldMixin(serializers.Serializer):
+18

, google . .   , .

class MageSerializer(GenericCharacterFieldMixin, serializers.ModelSerializer):
    player = serializers.ReadOnlyField(source='player.username')
    arcana = serializers.SerializerMethodField()


    attributes = serializers.PrimaryKeyRelatedField(many=True, 
                                read_only= True)
    skills = serializers.PrimaryKeyRelatedField(many=True, 
                                read_only= True)


    def get_arcana(self, obj):
      if obj:
        return {str(arcana): arcana.current_value for arcana in obj.linked_arcana.all()}

    class Meta:
        model = Mage
        fields = ('id', 'player', 'name', 'sub_race', 'faction', 'is_published',
                  'power_level', 'energy_trait', 'virtue', 'vice', 'morality', 'size',
                  'arcana', 'attributes', 'skills')
        depth = 1
+3

SerializerMetaclass:

from rest_framework import serializers

class GenericCharacterFieldMixin(metaclass=serializers.SerializerMetaclass):
    # ...

, DRF.

, , :

  1. user1376455 DRF, _declared_fields, . .
  2. Nikolai Fominykh’s decision changes the mixin to a full-fledged serializer (note that because of this, the name is GenericCharacterFieldMixinvery unsuccessful for a class that is not a mixin, but a serializer!). This is problematic because it takes the full class Serializerto multiple inheritance, see the DRF problem for examples demonstrating why this is a bad idea.
+2
source

All Articles