miguel__frtt
miguel__frtt

Reputation: 73

"Incorrect type. Expected pk value, received User."

I have these two models: order and route. The route has a oneToMany relation with Order as you can see:

class Order(models.Model):
    customer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='customer')
    retailer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='retailer')
    date_publish = models.DateField(default=datetime.date.today)
    date_available = models.DateField()
    weight = models.DecimalField(decimal_places=2, max_digits=5)
    description = models.CharField(max_length=500, null=True)
    route = models.ForeignKey(Route, related_name='orders', null=True, on_delete=models.CASCADE)

class Route(models.Model):
    day = models.DateField()
    warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE) 
    start_time = models.TimeField()

When a route is created I want to associate orders with that route, so I've done the following serializer:

class routeSerializer(serializers.ModelSerializer):

    orders = OrderSerializer(many=True)

    class Meta:
        model = Route
        fields = ['day', 'warehouse', 'start_time', 'orders']

    def create(self, validated_data):
        orders_data = validated_data.pop('orders')
        route = Route.objects.create(**validated_data)

        for order_data in orders_data:
            order_serializer = OrderSerializer(data=order_data)
            order_serializer.is_valid(raise_exception=True)
            orders = order_serializer.save()
            orders.route = route
            orders.save()
        
        return route


class OrderSerializer(serializers.ModelSerializer):
    ordertimelocation = orderTimelocationSerializer(many=True)


    class Meta:
        model = Order
        fields = ['id', 'customer', 'retailer', 'date_available', 'weight', 'description', 'ordertimelocation']

    def create(self, validated_data):
        timelocations_data = validated_data.pop('ordertimelocation')
        order = Order.objects.create(**validated_data)
        
        for timelocation_data in timelocations_data:
            order_time_location_serializer = orderTimelocationSerializer(data=timelocation_data)
            order_time_location_serializer.is_valid(raise_exception=True)
            order_time_location = order_time_location_serializer.save()
            order_time_location.order = order
            order_time_location.save()

        return order

    def update(self, instance, validated_data):
        
        timelocations_data = validated_data.pop('ordertimelocation')
        ordertimelocation = instance.ordertimelocation

        for timelocation_data in timelocations_data:
            order_time_location_serializer = orderTimelocationSerializer(data=timelocation_data)
            order_time_location_serializer.is_valid(raise_exception=True)
            order_time_location = order_time_location_serializer.save()
            order_time_location.order = instance
            order_time_location.save()

        return instance

Views:

class GetRoutes(generics.ListAPIView):
    queryset = Route.objects.all()
    serializer_class = routeSerializer

class CreateRoute(generics.CreateAPIView):
    queryset = Route.objects.all()
    serializer_class = routeSerializer

class CreateOrder(generics.CreateAPIView):
 
    queryset = Order.objects.all()
    serializer_class = OrderSerializer

class GetOrders(generics.ListAPIView):

    serializer_class = OrderSerializer

    def get_queryset(self):
        us = self.kwargs.get('us')
        return Order.objects.filter(customer_id=us) 
    
class GetOrder(generics.RetrieveAPIView):

    serializer_class = OrderSerializer

    def get_object(self, queryset=None, **kwargs):
        item = self.kwargs.get('order')
        return get_object_or_404(Order, id=item)
    
class UpdateOrder(generics.UpdateAPIView):

    serializer_class = OrderSerializer
    queryset = Order.objects.all()


Edit: I also customised the default user model like this: User models:

class UserManager(BaseUserManager):

    def create_superuser(self, email, user_name, first_name, password, **other_fields):

        other_fields.setdefault('is_staff', True)
        other_fields.setdefault('is_superuser', True)
        other_fields.setdefault('is_active', True)

        if other_fields.get('is_staff') is not True:
            raise ValueError(
                'Superuser must be assigned to is_staff=True.')
        if other_fields.get('is_superuser') is not True:
            raise ValueError(
                'Superuser must be assigned to is_superuser=True.')

        return self.create_user(email, user_name, first_name, password, **other_fields)
    
    def create_user(self, email, user_name, first_name, password, **other_fields):

        if not email:
            raise ValueError(_('You must provide an email address'))

        email = self.normalize_email(email)
        user = self.model(email=email, user_name=user_name, first_name=first_name, **other_fields)
        user.set_password(password)
        user.save()
        return user

class User(AbstractBaseUser, PermissionsMixin):
    GENDER_MALE = 0
    GENDER_FEMALE = 1
    GENDER_OTHER = 2
    GENDER_CHOICES = [(GENDER_MALE, 'Male'), (GENDER_FEMALE, 'Female'), (GENDER_OTHER, 'Other')]

    email = models.EmailField(_('email address'), unique=True)
    user_name = models.CharField(max_length=150, unique=True)
    first_name = models.CharField(max_length=150, blank=True)
    last_name = models.CharField(max_length=150, blank=True)
    start_date = models.DateTimeField(default=timezone.now)
    is_staff = models.BooleanField(default=False)
    is_retailer = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    gender = models.IntegerField(choices=GENDER_CHOICES)

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['user_name', 'first_name']

    def __str__(self):
        return self.user_name
    
    def isretailer(self):
        return self.is_retailer

User serizalizer:

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('id', 'email', 'user_name', 'first_name', 'last_name')

Views:

class CustomUserCreate(APIView):
    permission_classes = [AllowAny] #when a user create an account he isn't autenticated

    def post(self, request, format='json'):
        serializer = RegisterUserSerializer(data=request.data)
        if serializer.is_valid():
            user = serializer.save()
            if user:
                json = serializer.data
                return Response(json, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class ListUsers(generics.ListAPIView):

    serializer_class = UserSerializer
    queryset = User.objects.all()

class UserDetail(generics.RetrieveAPIView):

    serializer_class = UserSerializer

    def get_object(self, queryset=None, **kwargs):
        item = self.kwargs.get('id')
        return get_object_or_404(User, id=item)

I am sending a request like this:

{
    "day" : "2021-12-12",
    "warehouse": "1",
    "start_time": "7:00",
    "orders": [
        {
            "id": 15,
            "customer": 1,
            "retailer": 2,
            "date_available": "2020-12-12",
            "weight": "1.20",
            "description": null,
            "ordertimelocation": [
                {
                    "longitude": "12.1223000000000000",
                    "latitude": "12.1223000000000000",
                    "time_interval": [
                        {
                            "start": "2021-07-21T10:10:00Z",
                            "end": "2021-07-21T10:10:00Z"
                        }
                    ]
                }
            ]
        }
    ]
}

But the server returns a bad request:


{
    "customer": [
        "Incorrect type. Expected pk value, received User."
    ],
    "retailer": [
        "Incorrect type. Expected pk value, received User."
    ]
}

I'm new to django and I don't know what is a 'pk' value and why it is expecting it instead of User.

Upvotes: 0

Views: 2869

Answers (1)

Amin
Amin

Reputation: 2874

PK is primary key and in here is equal to id

For saving a record in db with it's relations, django needs PK of related object

But when you pass this pk to serializer, in validate() function of serializer, django if passed pk is existed in db and valid, if so, it would return it's model object

for example, you pass customer pk as 1, but after validation, there is a Customer object with id 1. and you are passing this object to order = Order.objects.create(**validated_data) but as i mentioned before, you should pass just PK

So one of solutions can be:

validated_data['customer'] = validated_data['customer'].id
validated_data['retailer'] = validated_data['retailer'].id
order = Order.objects.create(**validated_data)

And another solution is to overriding validate() function and control what to return

Upvotes: 2

Related Questions