Django Rest Framework From Many to Many Self-Related Fields

I have an AngularJS project that used Django as a platform through the Django Rest Framework (DRF).

I created a group model and set up a serializer class for it, however now I want to set a new field in this model called related_groups , which will refer to the same model (group) as an array of primary keys.

I donโ€™t know if self-esteem is possible in the serializer, and I donโ€™t know how else to get through the related groups from the front-end, which can be selected and selected by users who own the group. I want this field to refer to the primary keys of other lines in the group and repeat this collection of groups to establish related group relationships.

 class GroupSerializer(serializers.ModelSerializer): class Meta: model = mod.Group fields = ( 'group_id', 'group_name', 'category', 'related_groups', ) 

and the view seems to be exactly what I want:

 GroupSerializer(): group_id = IntegerField(read_only=True) group_name = CharField(max_length=100) category = CharField(max_length=256, required=False related_groups = PrimaryKeyRelatedField(many=True, queryset=Group.objects.all(), required=False) 

and the model is presented as such:

 class Group(models.Model): """ Model definition of a Group. Groups are a collection of users (ie Contacts) that share access to a collection of objects (eg Shipments). """ group_id = models.AutoField(primary_key=True) group_name = models.CharField(max_length=100) owner_id = models.ForeignKey('Owner', related_name='groups') category = models.CharField(max_length=256) related_groups = models.ManyToManyField('self', blank=True, null=True) history = HistoricalRecords() def __unicode__(self): return u'%s' % (self.group_name) def __str__(self): return '%s' % (self.group_name) 

View, access to this model, a fairly simple view of CRUD:

 @api_view(['GET', 'PUT', 'DELETE']) @authentication_classes((SessionAuthentication, BasicAuthentication)) @permission_classes((IsAuthenticated, HasGroupAccess)) def group_detail(request, pk, format=None): group, error = utils.get_by_pk(pk, mod.Group, request.user) if error is not None: return error if request.method == 'GET': serializer = ser.GroupSerializer(group) return Response(serializer.data) elif request.method == 'PUT': return utils.update_object_by_pk(request, pk, mod.Group, ser.GroupSerializer) elif request.method == 'DELETE': return utils.delete_object_by_pk(request.user, pk, mod.Group) 

What causes some disinfection and verification methods:

 def update_object_by_pk(request, pk, obj_type, serializer): try: with transaction.atomic(): obj, error = select_for_update_by_pk(pk, obj_type, request.user) if error is not None: return error obj_serializer = serializer(obj, data=request.data) if obj_serializer.is_valid(): obj_serializer.save() else: response = ("Attempt to serialize {} with id {} failed " "with errors {}").format(str(obj_type), str(pk), str(serializer.errors)) return Response(response, status=status.HTTP_400_BAD_REQUEST) except Exception as e: response = ("Error attempting to update {} with ID={}. user={}, " "error={}".format(str(obj_type), str(pk), str(request.user.email), str(e))) return Response(response, status=status.HTTP_400_BAD_REQUEST) else: resp_str = ("Successfully updated {} with ID={}".format(str(obj_type), str(pk))) return Response(resp_str, status=status.HTTP_200_OK) 

which causes:

 def select_for_update_by_pk(pk, mod_type, user): response = None obj = None try: obj = mod_type.objects.select_for_update().get(pk=pk) except mod_type.DoesNotExist: resp_str = ("{} could not be found with ID={}.". format(str(mod_type), str(pk))) response = Response(resp_str, status=status.HTTP_404_NOT_FOUND) return obj, response 

which is just a wrapper around the select_for_update() method of Django.

As a result of the migration, a new group_related_groups table was created with the identifier, from_group and to_group column, which is used by Django as a join / search to establish these relations.

I can write individually for this endpoint, but the GroupSerializer serializer doesn't seem to want to allow multiple defaults.

Thus, using the PUT request for PUT, the value '2' for grouping with PK of 1 is successful. However, attempts to put ['2','3'] , [2,3] , 2,3 and '2','3'

Tracking it back through the view to the utilitarian method, I see that it serializer.is_valid() does not fulfill the request, so it seems to me that this is a many=True problem, but I donโ€™t know which relationship serializer to use for this special ManyToManyField self-referential problem.

When debugging, I output serializer.is_valid () errors in syslog as follows:

  response = ("Attempt to serialize {} with id {} failed " "with errors {}").format(str(obj_type), str(pk), str(serializer.errors)) logger.exception(response) 

And I get this exception message as an answer:

Message: "Attempt to serialize <class 'bioapi.models.Group'> with id 1 failed with errors "

Debug error output for obj_serializer.error -

obj_serializer.error of {'related_groups': ['Incorrect type. Expected pk value, received str.']}

And here is the debug messasge on request.data:

{'group_name': 'Default Guest Group', 'related_groups': [1], 'group_id': 2, 'category': 'guest'}

which succeeds, and

<QueryDict: {'group_name': ['requestomatic'], 'related_groups':['2,2'], category': ['guest']}>

which fails. Looking at this now, I wonder if Postman data formatting is a problem. If in this case I will feel pretty stupid.

Can I imagine many-to-many relationships from a model back to myself using DRF, or do I need to have my own serializer just for the relationship table? The documentation for DRF does not use self-referenced models, and all the examples that I find on the Internet either use several models or several serializers.

Is it possible to use ManyToManyField in my model, which is self-referential using the Django Rest Framework (DRF) and its serializers? If so, how?

+6
source share
2 answers

Similar to nested serialization. The model should be -

 class Group(models.Model): group_id = models.IntegerField(read_only=True) group_name = models.CharField(max_length=100) category = models.CharField(max_length=256, required=False) related_groups = models.ForeignKey('self', required=False, related_name='children') 

And serializers -

 class GroupSerializer(serializers.ModelSerializer): class Meta: model = Group fields = ( 'group_id', 'group_name', 'category', 'related_groups', ) class NestedGroupSerializer(serializers.ModelSerializer): children = GroupSerializer(many=True, read_only=True) class Meta: model = Group fields = ('group_id', 'group_name', 'category', 'related_groups', 'children') 

You can then use the NestedGroupSerializer to access all related groups.

By default, nested serializers are read-only. If you want to support write operations in the nested serializer field, you will need to create the create () and / or update () methods to explicitly specify how the child relations should be saved.

Hope this helps.

+3
source

Try using ModelViewSet as:

 class GroupViewSet(viewsets.ModelViewSet): queryset = models.Group.objects.all() serializer_class = serializers.GroupSerializer authentication_classes = (SessionAuthentication, BasicAuthentication) permission_classes = (IsAuthenticated, HasGroupAccess) 

and in your urls.conf , something like: from import views from rest_framework import routers

 router = routers.SimpleRouter() router.register(r'group', views.GroupViewset) urlpatterns = router.urls 
+1
source

All Articles