Dmytro Popoilyk
Dmytro Popoilyk

Reputation: 3

Django REST: TypeError: Object of type 'Language' is not JSON serializable

When I tried to add value of language python3 returns error that this object is not JSON serializible.

models:

from django.db import models
from django.contrib.auth.models import AbstractUser, AbstractBaseUser

class Admin(AbstractUser):

    class Meta(AbstractUser.Meta):
        pass

class HahaUser(AbstractBaseUser):
    is_admin = models.BooleanField(default=False, verbose_name='is administrator?')
    born = models.PositiveSmallIntegerField(verbose_name='born year')
    rating = models.PositiveIntegerField(default=0, verbose_name='user rating')
    email = models.EmailField(verbose_name='email')
    nickname = models.CharField(max_length=32, verbose_name='useraname')
    password = models.CharField(max_length=100, verbose_name='password') # on forms add widget=forms.PasswordInput
    language = models.ForeignKey('Language', on_delete=models.PROTECT)
    country = models.ForeignKey('Country', on_delete=models.PROTECT)

    def __str__(self):
        return self.nickname

    class Meta:
        verbose_name = 'User'
        verbose_name_plural = 'Users'
        ordering = ['nickname']

class Language(models.Model):
    name = models.CharField(max_length=20, verbose_name='language name')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = 'Language'
        verbose_name_plural = 'Languages'


class Country(models.Model):
    name_ua = models.CharField(max_length=20, verbose_name='country name in Ukranian')
    name_en = models.CharField(max_length=20, verbose_name='country name in English')
    name_ru = models.CharField(max_length=20, verbose_name='country name in Russian')

    def __str__(self):
        return self.name_en

    class Meta:
        verbose_name = 'Country'
        verbose_name_plural = 'Countries'

Serializers:

from rest_framework import serializers

from main import models

class RegistrationSerializer(serializers.ModelSerializer):

    password2 = serializers.CharField(style={'input_type': 'password'},
        write_only=True, required=True)

    class Meta:
        model = models.HahaUser
        fields = ['nickname', 'password', 'password2', 'language', 'country',
            'email', 'born']
        extra_kwargs = {
            'password': {'write_only': True}
        }

    def save(self):
        account = models.HahaUser.objects.create(
            email=self.validated_data['email'],
            nickname=self.validated_data['nickname'],
            language=self.validated_data['language'],
            born=self.validated_data['born'],
            country=self.validated_data['country']
        )

        password = self.validated_data['password']
        password2 = self.validated_data['password2']
        if password != password2:
            raise serializers.ValidationError({'password': 'Passwords must match.'})

        account.set_password(password)
        account.save()

        return account

views:

from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view

from .serializers import RegistrationSerializer

@api_view(['POST',])
def registration_view(request):
    if request.method == 'POST':
        serializer = RegistrationSerializer(data=request.data)
        data = {}
        if serializer.is_valid():
            account = serializer.save()
            data['response'] = 'Successfully registrated a new user.'
            data['email'] = account.email
            data['nickname'] = account.nickname
            data['language'] = account.language
            data['born'] = account.born
            data['country'] = account.country
        else:
            data = serializer.errors

        return Response(data)

Full text of error:

Internal Server Error: /api/account/register/
Traceback (most recent call last):
  File "/home/dima/Стільниця/hahachat/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/dima/Стільниця/hahachat/lib/python3.6/site-packages/django/core/handlers/base.py", line 145, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/dima/Стільниця/hahachat/lib/python3.6/site-packages/django/core/handlers/base.py", line 143, in _get_response
    response = response.render()
  File "/home/dima/Стільниця/hahachat/lib/python3.6/site-packages/django/template/response.py", line 105, in render
    self.content = self.rendered_content
  File "/home/dima/Стільниця/hahachat/lib/python3.6/site-packages/rest_framework/response.py", line 70, in rendered_content
    ret = renderer.render(self.data, accepted_media_type, context)
  File "/home/dima/Стільниця/hahachat/lib/python3.6/site-packages/rest_framework/renderers.py", line 103, in render
    allow_nan=not self.strict, separators=separators
  File "/home/dima/Стільниця/hahachat/lib/python3.6/site-packages/rest_framework/utils/json.py", line 25, in dumps
    return json.dumps(*args, **kwargs)
  File "/usr/lib/python3.6/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/usr/lib/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/home/dima/Стільниця/hahachat/lib/python3.6/site-packages/rest_framework/utils/encoders.py", line 67, in default
    return super().default(obj)
  File "/usr/lib/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'Language' is not JSON serializable

I tried a lot of things: - add json method to language model - post data to field language_id - create LanguageSerializer and work using it but nothing work

Hope to your help))

Upvotes: 0

Views: 1427

Answers (3)

Dmytro Popoilyk
Dmytro Popoilyk

Reputation: 3

Thank you for help, but I'd solve it myself.

I just add str() to foreign keys on my view where I generate JSON response.

    from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view

from .serializers import RegistrationSerializer

@api_view(['POST',])
def registration_view(request):
    if request.method == 'POST':
        serializer = RegistrationSerializer(data=request.data)
        data = {}
        if serializer.is_valid():
            account = serializer.save()
            data['response'] = 'Successfully registrated a new user.'
            data['email'] = account.email
            data['nickname'] = account.nickname
            data['language'] = str(account.language)
            data['born'] = account.born
            data['country'] = str(account.country)
        else:
            data = serializer.errors

        return Response(data)

Upvotes: 0

HelloWorld
HelloWorld

Reputation: 85

The result of account.language is an instance. So, in your registration_view, data['language'] got an instance rather than string or number. That's the reason why data's language is not JSON serializable.

Based on your requirements, you can change it to

data['language'] = account.language.name

Upvotes: 1

Phoenix
Phoenix

Reputation: 4274

As exception says, language is an object of Language model and it's not a primitive type. So you should use some attributes of Language model like language_id or language_name instead of language object.

from rest_framework import serializers

from main import models

class RegistrationSerializer(serializers.ModelSerializer):

    password2 = serializers.CharField(style={'input_type': 'password'},
        write_only=True, required=True)
    language_name = serializers.CharField(source='language.name')

    class Meta:
        model = models.HahaUser
        fields = ['nickname', 'password', 'password2', 'language_name', 'country',
            'email', 'born']
        extra_kwargs = {
            'password': {'write_only': True}
        }

    def save(self):
        account = models.HahaUser.objects.create(
            email=self.validated_data['email'],
            nickname=self.validated_data['nickname'],
            language=self.validated_data['language_name'],
            born=self.validated_data['born'],
            country=self.validated_data['country']
        )

        password = self.validated_data['password']
        password2 = self.validated_data['password2']
        if password != password2:
            raise serializers.ValidationError({'password': 'Passwords must match.'})

        account.set_password(password)
        account.save()

        return account

NOTE: If you fix Language serializable error, You'll get another exception for Country too.

Upvotes: 1

Related Questions