Anuj TBE
Anuj TBE

Reputation: 9800

Django REST Framework - int() argument must be a string, a bytes-like object or a number, not 'SimpleLazyObject'

I'm using Django 2.0 and Django REST Framework to write REST API.

my settings.py contains settings for DRF

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated'
    ]
}

and every request must be signed using any of the DEFAULT_AUTHENTICATION_CLASSES method.

In contacts/serializers.py

class ContactSerializer(serializers.HyperlinkedModelSerializer):
    phone_numbers = ContactPhoneNumberSerializer(source='contactphonenumber_set', many=True)

    class Meta:
        model = Contact
        fields = ('url', 'first_name', 'last_name', 'full_name', 'date_of_birth', 'phone_numbers')

    def create(self, validated_data):
        phone_numbers = validated_data.pop('contactphonenumber_set')
        emails = validated_data.pop('contactemail_set')
        instance = Contact.objects.create(**validated_data)
        for phone_data in phone_numbers:
            ContactPhoneNumber.objects.create(contact=instance, **phone_data)
        return instance

print(validated_data) gives following data

{'first_name': 'Anshuman', 'last_name': 'Upadhyay', 'date_of_birth': datetime.date(2018, 5, 15), 'contactphonenumber_set': [], 'user_id': <SimpleLazyObject: <User: anuj>>}

The user_id is SimpleLazyObject thus giving error on save

TypeError: int() argument must be a string, a bytes-like object or a number, not 'SimpleLazyObject'

The error is on the line instance = Contact.objects.create(**validated_data)

How can I pass authenticated user to user field?

Edit 2: contacts/models.py

class Contact(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100, blank=True, null=True)
    date_of_birth = models.DateField(blank=True, null=True)

contacts/views.py

class ContactViewSet(viewsets.ModelViewSet):
    serializer_class = ContactSerializer
    permission_classes = (IsAuthenticated, AdminAuthenticationPermission,)

    def get_queryset(self):
        return Contact.objects.filter(user=self.request.user)

    def perform_create(self, serializer):
        serializer.save(user_id=self.request.user)

Upvotes: 0

Views: 3921

Answers (2)

Sardorbek Imomaliev
Sardorbek Imomaliev

Reputation: 15390

You could use self.context for this http://www.django-rest-framework.org/api-guide/serializers/#including-extra-context

If you are using GenericAPIView context is passed automatically

First of all you don't need to pass user_id to your serializer and then you need to update your ContactSerializer to look like this.

class ContactSerializer(serializers.HyperlinkedModelSerializer):
    phone_numbers = ContactPhoneNumberSerializer(source='contactphonenumber_set', many=True)

    class Meta:
        model = Contact
        fields = ('url', 'first_name', 'last_name', 'full_name', 'date_of_birth', 'phone_numbers')

    def create(self, validated_data):
        phone_numbers = validated_data.pop('contactphonenumber_set')
        emails = validated_data.pop('contactemail_set')
        instance = Contact.objects.create(user=self.context['request'].user, **validated_data)
        for phone_data in phone_numbers:
            ContactPhoneNumber.objects.create(contact=instance, **phone_data)
        return instance

You shouldn't overwrite perform_create

class ContactViewSet(viewsets.ModelViewSet):
    serializer_class = ContactSerializer
    permission_classes = (IsAuthenticated, AdminAuthenticationPermission,)

    def get_queryset(self):
        return Contact.objects.filter(user=self.request.user)

Upvotes: 1

JPG
JPG

Reputation: 88549

Assuming your Contact model contains an FK relation with AuthUserModel. So, your instance creation statement must be like this,

user = get some user instance from `validated` data
instance = Contact.objects.create(user=user,**validated_data)


This is the general solution for your question, If you add your Contact model, we could help you more

UPDATE-1
Change your create() as below,

def create(self, validated_data):
    phone_numbers = validated_data.pop('contactphonenumber_set')
    emails = validated_data.pop('contactemail_set')
    user = validated_data.pop('user') # change 1
    instance = Contact.objects.create(user=user, **validated_data) # # change 2
    for phone_data in phone_numbers:
        ContactPhoneNumber.objects.create(contact=instance, **phone_data)
    return instance


Update-2
You could use user = serializers.CurrentUserDefault() in serializer as below,

class ContactSerializer(serializers.HyperlinkedModelSerializer):
    phone_numbers = ContactPhoneNumberSerializer(source='contactphonenumber_set', many=True)
    user = serializers.CurrentUserDefault()

    class Meta:
        model = Contact
        fields = ('url', 'first_name', 'last_name', 'full_name', 'date_of_birth', 'phone_numbers')

    def create(self, validated_data):
        phone_numbers = validated_data.pop('contactphonenumber_set')
        emails = validated_data.pop('contactemail_set')
        user = validated_data.pop('user_id') 
        instance = Contact.objects.create(user=user, **validated_data) 
        for phone_data in phone_numbers:
            ContactPhoneNumber.objects.create(contact=instance, **phone_data)
        return instance


CurrentUserDefault() is do the same job as self.context.get('request').user

Upvotes: 1

Related Questions