Advertisement
Guest User

Untitled

a guest
Jan 19th, 2017
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.48 KB | None | 0 0
  1. from rest_framework import serializers
  2. from rest_framework.utils import model_meta
  3.  
  4.  
  5. class ManyToManySerializer(serializers.ModelSerializer):
  6. """
  7. Many-to-many serializer that will loop through an infinite depth
  8. and create, update and delete related entities.
  9.  
  10. Usage:
  11. This class is required in the parent serializer.
  12.  
  13. The pk field is required implicitly on the child serializer to make sure it exists in the
  14. validated data in the create and update methods. The name of this must match that of the
  15. actual field on the model.
  16.  
  17. Example depth one level:
  18.  
  19. class ChildSerializer(serializers.ModelSerializer):
  20. id = serializers.ModelField(model_field=Child()._meta.get_field('id'), required=False)
  21.  
  22. class Meta:
  23. model = Child
  24. fields = ('id', 'name')
  25.  
  26.  
  27. class ParentSerializer(ManyToManySerializer):
  28. children = ChildSerializer(many=True)
  29.  
  30. class Meta:
  31. model = Parent
  32. fields = ('id', 'name', 'children')
  33.  
  34. Here the ChildSerializer can also inherit from ManyToManySerializer to include a further depth and so on.
  35. """
  36.  
  37. @staticmethod
  38. def _delete_obsolete_many(instance, attr, value):
  39. related_manager = getattr(instance, attr)
  40.  
  41. # find the primary key for the joining table
  42. primary_key = related_manager.model._meta.pk
  43.  
  44. # get a list of primary keys in validated data that we want to keep
  45. pks = [item.get(primary_key.name) for item in value if item.get(primary_key.name)]
  46.  
  47. # delete any existing many-to-many that are not wanted
  48. related_manager.exclude(pk__in=pks).delete()
  49.  
  50. def _update_or_create_many(self, instance, attr, value):
  51. related_manager = getattr(instance, attr)
  52. info = model_meta.get_field_info(related_manager.model)
  53.  
  54. # find the primary key for the joining table
  55. primary_key = related_manager.model._meta.pk
  56.  
  57. # loop data and either update if we have been passed a primary key or create one if we have not
  58. for item in value:
  59.  
  60. # remove many-to-many relationships from validated_data as they
  61. # need to be created once the instance is saved
  62. many_to_many = {}
  63. for field_name, relation_info in info.relations.items():
  64. if relation_info.to_many and (field_name in item):
  65. many_to_many[field_name] = item.pop(field_name)
  66.  
  67. # id of the item if there is one
  68. item_pk = item.pop(primary_key.name) if item.get(primary_key.name) else None
  69.  
  70. # if we have an id we need to update it otherwise create it
  71. if item_pk:
  72. related_object = related_manager.get(pk=item_pk)
  73. for m_attr, m_value in item.items():
  74. setattr(related_object, m_attr, m_value)
  75. related_object.save()
  76. else:
  77. related_object = related_manager.create(**item)
  78.  
  79. # save or delete many-to-many relationships after the instance is created.
  80. if many_to_many:
  81. for field_name, value in many_to_many.items():
  82. self.set_many(related_object, field_name, value)
  83.  
  84. def set_many(self, instance, attr, value):
  85. self._delete_obsolete_many(instance, attr, value)
  86. self._update_or_create_many(instance, attr, value)
  87.  
  88. def create(self, validated_data):
  89. model_class = self.Meta.model
  90. info = model_meta.get_field_info(model_class)
  91.  
  92. # remove many-to-many relationships from validated_data as they
  93. # need to be created once the instance is saved
  94. many_to_many = {}
  95. for field_name, relation_info in info.relations.items():
  96. if relation_info.to_many and (field_name in validated_data):
  97. many_to_many[field_name] = validated_data.pop(field_name)
  98.  
  99. instance = model_class.objects.create(**validated_data)
  100.  
  101. # save or delete many-to-many relationships after the instance is created.
  102. if many_to_many:
  103. for field_name, value in many_to_many.items():
  104. self.set_many(instance, field_name, value)
  105.  
  106. return instance
  107.  
  108. def update(self, instance, validated_data):
  109. info = model_meta.get_field_info(instance)
  110.  
  111. for attr, value in validated_data.items():
  112. if attr in info.relations and info.relations[attr].to_many:
  113. self.set_many(instance, attr, value)
  114. else:
  115. setattr(instance, attr, value)
  116.  
  117. instance.save()
  118.  
  119. return instance
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement