How to manually change value of primary key in Django Model through serializers?

Viewed 80

Is it somehow possible to manually change ID value in AutoField (primary key) through serializer?
I found this in documentation of Django: https://docs.djangoproject.com/en/4.0/ref/models/instances/#auto-incrementing-primary-keys, but I have no idea how to do this including serializers.

My task is: If car with given ID does not exists, fetch car from external API and save it.

Model:

from django.db import models

class Car(models.Model):
    speed = models.IntegerField(default=1)
    color = models.CharField(max_length=200)
    wheels = models.CharField(max_length=500)

My serializer:

from rest_framework import serializers
from .models import Car


class CarSerializer(serializers.ModelSerializer):
    class Meta:
        model = Car
        fields = ['id', 'speed', 'color', 'wheels']

My view:

@api_view(['GET', 'PUT', 'DELETE'])
def car_detail_id(request, id):
    try:
        car = Car.objects.get(pk=id)
    except Car.DoesNotExist:
        #-----------------------------------------------------------#
        car = search_car(id) #fetch car from external API
        if request.method == "GET" and car is not None:
            serializer = CarSerializer(data=car)
            if serializer.is_valid():
                serializer.save()
                return Response(status=status.HTTP_201_CREATED)
        #-----------------------------------------------------------#
        return Response(status=status.HTTP_404_NOT_FOUND)
    return car_detail(request, "id", car) #method to handle CRUD

Car structure:

 {
    "speed": 1,
    "id": 1,
    "color": "Green",
    "wheels": "Black"
  },

This implementation fetch car with given ID from API, then saves it, but with different ID. Are there any better ways to do this? Let me know below!

3 Answers

The AutoField function to build a pk requires a unique identifier and it cannot/should not be changed in the DB (as far as I am aware).

I would focus on accomplishing the use-case another way. You could use try/except to check if the object exists:

try:
    ObjectName.objects.get(id=self.kwargs['id'])  # insert correct syntax for your use case
except ObjectName.DoesNotExist:
    # Make external call here

at first: You are dont need to create autokey. Django do it automatically.

class Post(models.Model):
    user_id = models.IntegerField(default=1)
    title = models.CharField(max_length=200)
    body = models.CharField(max_length=500)

at second: You are save model on GET, it's bad desigion:

  • GET for read.
  • POST for write.

At last, try to use view and viewsets from DRF, it is easier:

class CreatePostAPIView(CreatePostAPIView):
    model=Post

You can call this DRF-view everythere:

def post_detail_id(request, id):
    ....    
    except Post.DoesNotExist:
            #-----------------------------------------------------------#
            request.data = request.method == "GET" and search_post(id) #fetch 
            if request.data:
                return CreatePostAPIView().post(request, *args, **kwargs)
            #-----------------------------------------------------------#
            return Response(status=status.HTTP_404_NOT_FOUND)

My opinion, somewhere business-logic in post_detail_id is broken.

If you are using serializers.ModelSerializer like your above example class CarSerializer(serializers.ModelSerializer):. You can set extra properties while saving.

serializer = CarSerializer(data=car)
if serializer.is_valid():
    serializer.save(id=id)

You can add other properties as long as it is in the model. I can't find any other solution to do it.

I found this way from here Solution. Hope this will helps

Related