Amelse Etomer
Amelse Etomer

Reputation: 1253

creating model while setting slug and user in Django Rest Framework

I have a model where I create an instance using django rest framework. The serializer complains about a missing slug and an empty ForeignKey-Field that is supposed to point to the User.

I tried to override perform_create in my serializer to set these values, but I realized that in the standard CreateModelMixin:

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

the method is_valid() is called before perform_create(). Therefore populating the foreign key for user and adding a slug does not work.

Is there a proper way to solve this problem or should I override create?

Update:

I was asked by @Rahul Gupta to add Model and Serializer. Here they are:

class ProductSerializer(serializers.ModelSerializer):

    class Meta:
        model = Product
        fields=('id','name','slug',..., 'shop','category')

and

class Product(models.Model):
    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255)
    shop = models.ForeignKey(Shop)
    category = models.ForeignKey("Category")        

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        super(Product, self).save(*args, **kwargs)

I should probably make myself a bit clearer. In the browser the user fills in a form to create a new Product instance. However the user does not enter shop or slug, as these are determined by automatically. Slug is derived from name and shop from a different model, that is linked to request.user.

Upvotes: 2

Views: 1508

Answers (3)

Erfan Rafezi
Erfan Rafezi

Reputation: 11

If you using the rest framework and serializer after add this code in the model:

def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        super(Product, self).save(*args, **kwargs)

you need also add this code to the serializer function of the model:

slug = serializers.SlugField(required=False)

and you good to go.

Upvotes: 0

Max Heiber
Max Heiber

Reputation: 15532

You don't have to copy and paste code from the parent class, you can just call super in order to borrow functionality from the parent's create method:

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all().order_by('name')
    serializer_class = ProductSerializer

    def create(self, request, *args, **kwargs):
        request.data['shop'] = drequest.user.shop.id
        # Everything above is from Sebastian Langer's answer
        # difference is below: just call parent's `create`:
        return super(ProductViewSet, self).create(request, *args, **kwargs) 

Upvotes: 2

Amelse Etomer
Amelse Etomer

Reputation: 1253

So I'll answer this myself. I've done something rather silly - I thought that I have to override the create method in the serializer when it really seems to be the method in the ViewSet that I have to override. Here is the code that did it for me:

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all().order_by('name')
    serializer_class = ProductSerializer

    def create(self, request, *args, **kwargs):
        request.data['shop']=request.user.shop.id # <-- I ADDED THIS LINE
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

The comment "I ADDED THIS LINE" is in the line where I add functionality to the original method. It does the job, but there might be a more elegant solution. Something similar can be done for the slug.

Upvotes: 1

Related Questions