Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from rest_framework import serializers
- from rest_framework.utils import model_meta
- class ManyToManySerializer(serializers.ModelSerializer):
- """
- Many-to-many serializer that will loop through an infinite depth
- and create, update and delete related entities.
- Usage:
- This class is required in the parent serializer.
- The pk field is required implicitly on the child serializer to make sure it exists in the
- validated data in the create and update methods. The name of this must match that of the
- actual field on the model.
- Example depth one level:
- class ChildSerializer(serializers.ModelSerializer):
- id = serializers.ModelField(model_field=Child()._meta.get_field('id'), required=False)
- class Meta:
- model = Child
- fields = ('id', 'name')
- class ParentSerializer(ManyToManySerializer):
- children = ChildSerializer(many=True)
- class Meta:
- model = Parent
- fields = ('id', 'name', 'children')
- Here the ChildSerializer can also inherit from ManyToManySerializer to include a further depth and so on.
- """
- @staticmethod
- def _delete_obsolete_many(instance, attr, value):
- related_manager = getattr(instance, attr)
- # find the primary key for the joining table
- primary_key = related_manager.model._meta.pk
- # get a list of primary keys in validated data that we want to keep
- pks = [item.get(primary_key.name) for item in value if item.get(primary_key.name)]
- # delete any existing many-to-many that are not wanted
- related_manager.exclude(pk__in=pks).delete()
- def _update_or_create_many(self, instance, attr, value):
- related_manager = getattr(instance, attr)
- info = model_meta.get_field_info(related_manager.model)
- # find the primary key for the joining table
- primary_key = related_manager.model._meta.pk
- # loop data and either update if we have been passed a primary key or create one if we have not
- for item in value:
- # remove many-to-many relationships from validated_data as they
- # need to be created once the instance is saved
- many_to_many = {}
- for field_name, relation_info in info.relations.items():
- if relation_info.to_many and (field_name in item):
- many_to_many[field_name] = item.pop(field_name)
- # id of the item if there is one
- item_pk = item.pop(primary_key.name) if item.get(primary_key.name) else None
- # if we have an id we need to update it otherwise create it
- if item_pk:
- related_object = related_manager.get(pk=item_pk)
- for m_attr, m_value in item.items():
- setattr(related_object, m_attr, m_value)
- related_object.save()
- else:
- related_object = related_manager.create(**item)
- # save or delete many-to-many relationships after the instance is created.
- if many_to_many:
- for field_name, value in many_to_many.items():
- self.set_many(related_object, field_name, value)
- def set_many(self, instance, attr, value):
- self._delete_obsolete_many(instance, attr, value)
- self._update_or_create_many(instance, attr, value)
- def create(self, validated_data):
- model_class = self.Meta.model
- info = model_meta.get_field_info(model_class)
- # remove many-to-many relationships from validated_data as they
- # need to be created once the instance is saved
- many_to_many = {}
- for field_name, relation_info in info.relations.items():
- if relation_info.to_many and (field_name in validated_data):
- many_to_many[field_name] = validated_data.pop(field_name)
- instance = model_class.objects.create(**validated_data)
- # save or delete many-to-many relationships after the instance is created.
- if many_to_many:
- for field_name, value in many_to_many.items():
- self.set_many(instance, field_name, value)
- return instance
- def update(self, instance, validated_data):
- info = model_meta.get_field_info(instance)
- for attr, value in validated_data.items():
- if attr in info.relations and info.relations[attr].to_many:
- self.set_many(instance, attr, value)
- else:
- setattr(instance, attr, value)
- instance.save()
- return instance
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement