[Solved]-Django-rest-framework Create object with relationship many-to many

4👍

You need to handle the nested serializers creation in the serializers create method;

As in here: http://www.django-rest-framework.org/api-guide/serializers/#writing-create-methods-for-nested-representations

4👍

If you’re not hung up on creating intermediate and other table records in one HTTP request. Otherwise you’ve got to handle nested representations.

models.py:

from django.db import models
import django.urls
import urllib

class Product(models.Model):
    name = models.CharField(max_length=255)

    def get_absolute_url(self):
        return django.urls.reverse('app:product', args=(self.pk,))

    def __str__(self):
        return self.name

class Size(models.Model):
    products = models.ManyToManyField(Product, through=ProductVariant,
        related_name='sizes', related_query_name='size')
    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name

class ProductVariant(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE,
        related_name='productvariants', related_query_name='productvariant')
    size = models.ForeignKey('Size', on_delete=models.CASCADE,
        related_name='productvariants', related_query_name='productvariant')

    class Meta:
        unique_together = ('product', 'size')

    def __str__(self):
        return str(self.product) + ': ' + str(self.size)

api.py:

from rest_framework import routers, serializers, viewsets
from app import models

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Product
        fields = ('id', 'name')
        read_only_fields = ('id',)

class ProductViewSet(viewsets.ModelViewSet):
    queryset = models.Product.objects.all()
    serializer_class = ProductSerializer

class SizeSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Size
        fields = ('id', 'name')
        read_only_fields = ('id',)

class SizeViewSet(viewsets.ModelViewSet):
    queryset = models.Size.objects.all()
    serializer_class = SizeSerializer

class ProductVariantSerializer(serializers.ModelSerializer):
    product = serializers.PrimaryKeyRelatedField(
        queryset=models.Product.objects.all())
    size = serializers.PrimaryKeyRelatedField(
        queryset=models.Size.objects.all())
    class Meta:
        model = models.ProductVariant
        fields = ('id', 'product', 'size')
        read_only_fields = ('id',)

class ProductVariantViewSet(viewsets.ModelViewSet):
    queryset = models.ProductVariant.objects.all()
    serializer_class = ProductVariantSerializer

router = routers.DefaultRouter()
router.register(r'products', ProductViewSet)
router.register(r'sizes', SizeViewSet)
router.register(r'productvariants', ProductVariantViewSet)

api_urlpatterns = ([
    url('', include(router.urls)),
], 'api')
urlpatterns += [
    url(r'^api/', include(api_urlpatterns)),
]

After that you can

POST /api/products/ {name: ...}
POST /api/sizes/ {name: ...}
POST /api/productvariants/ {product: productId, size: sizeId}
👤x-yuri

0👍

Hi I will be showing many to many for update and create. The context is the event can have many dances and dances can have many event.

The request will be as followed.

 {
     "competition": 2,
     "title": "the title",
     "dances":[ {"id":1},{"id":2}],
     "description": "the desc"            
 }

The Create Function will be as followed.

def create(self, validated_data):
    try:
        dance_ids = []
        for dance in self.initial_data['dances']:
            if 'id' not in dance:
                raise serializers.ValidationError({'detail': 'key error'})
            dance_ids.append(dance['id'])

        new_event = models.Event.objects.create(**validated_data)
        
        if dance_ids:
            for dance_id in dance_ids:
                new_event.dances.add(dance_id)
        new_event.save()
        return new_event

    except Exception as e:
        raise serializers.ValidationError({'detail': e})

The Update Function will be as followed.

def update(self, instance, validated_data):
    # Delete all records of genres.
    try:
        for current_genre in instance.dances.all():
            instance.dances.remove(current_genre)

        # Repopulate genres into instance.
        for dance in self.initial_data['dances']:
            if 'id' not in dance:
                raise serializers.ValidationError({'detail': 'key error'})
            dance_obj = models.Dance.objects.get(pk=dance['id'])
            instance.dances.add(dance_obj)

            event_updated = super().update(instance, validated_data)

        return event_updated
    except Exception as e:
        raise serializers.ValidationError({'detail': e})

If you want to just do "dances":[1,2] instead, just make some amendments to the

for dance in self.initial_data['dances']:
        if 'id' not in dance:
            raise serializers.ValidationError({'detail': 'key error'})
        dance_ids.append(dance['id'])

part.

Leave a comment