Django-allauth: <ImageFieldFile: None> is not JSON serializable

Created on 23 Jan 2014  路  24Comments  路  Source: pennersr/django-allauth

Hello!

I have Django==1.6.1 and django-allauth==0.15.0.
I use custom user model

class User(AbstractBaseUser, PermissionsMixin):
    name = models.CharField('Name', max_length=255)
    email = models.EmailField(verbose_name="E-Mail", max_length=255, unique=True, db_index=True)
    photo = StdImageField(verbose_name="Photo", upload_to="photos", blank=True, size=(290, 290), thumbnail_size=(90, 90, True))
...
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["name", ]

After sign in with Twitter I got

TypeError at /accounts/twitter/login/callback/
<ImageFieldFile: None> is not JSON serializable

Traceback

Traceback:
File "/home/www/.virtualenvs/site/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  114.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/www/.virtualenvs/site/lib/python2.7/site-packages/allauth/socialaccount/providers/oauth/views.py" in view
  35.             return self.dispatch(request, *args, **kwargs)
File "/home/www/.virtualenvs/site/lib/python2.7/site-packages/allauth/socialaccount/providers/oauth/views.py" in dispatch
  91.             return complete_social_login(request, login)
File "/home/www/.virtualenvs/site/lib/python2.7/site-packages/allauth/socialaccount/helpers.py" in complete_social_login
  119.         return _complete_social_login(request, sociallogin)
File "/home/www/.virtualenvs/site/lib/python2.7/site-packages/allauth/socialaccount/helpers.py" in _complete_social_login
  130.         ret = _process_signup(request, sociallogin)
File "/home/www/.virtualenvs/site/lib/python2.7/site-packages/allauth/socialaccount/helpers.py" in _process_signup
  28.         request.session['socialaccount_sociallogin'] = sociallogin.serialize()
File "/home/www/.virtualenvs/site/lib/python2.7/site-packages/allauth/socialaccount/models.py" in serialize
  166.                    user=serialize_instance(self.account.user),
File "/home/www/.virtualenvs/site/lib/python2.7/site-packages/allauth/utils.py" in serialize_instance
  151.     return json.loads(json.dumps(ret, cls=DjangoJSONEncoder))
File "/opt/python2.7/lib/python2.7/json/__init__.py" in dumps
  250.         sort_keys=sort_keys, **kw).encode(obj)
File "/opt/python2.7/lib/python2.7/json/encoder.py" in encode
  207.         chunks = self.iterencode(o, _one_shot=True)
File "/opt/python2.7/lib/python2.7/json/encoder.py" in iterencode
  270.         return _iterencode(o, 0)
File "/home/www/.virtualenvs/site/lib/python2.7/site-packages/django/core/serializers/json.py" in default
  104.             return super(DjangoJSONEncoder, self).default(o)
File "/opt/python2.7/lib/python2.7/json/encoder.py" in default
  184.         raise TypeError(repr(o) + " is not JSON serializable")

Exception Type: TypeError at /accounts/twitter/login/callback/
Exception Value: <ImageFieldFile: None> is not JSON serializable

Advice with SESSION_SERIALIZER dont help me. Result is the same.

FIXME

Most helpful comment

This is a django-allauth issue because custom user models can have any Django model field. If your user model had a HandField from the docs, for example, this error would occur because DjangoJSONEncoder doesn't know how to encode that type.

Potential solutions include:

  • Not trying to serialise the User instance. Instead just serialise the natural key and fetch the user using that when needed (seems best to me)
  • Add another setting specifying which JSON encoder to use.

All 24 comments

If I invoke serialize_instance() with a standard Django ImageField, there seems to be no problem:

serialize_instance(User())
>>> {u'id': None, u'photo': u'', u'email': u'', u'name': u''}

So, I assume that your custom StdImageField is behaving a bit differently. Could you give it a shot with the normal ImageField to see if that is indeed the problem?

Closing until it is clear that this is an allauth issue...

I'm having the same issue on Django 1.6 and allauth 0.18

class MyUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(_('email address'), max_length=255,
                              unique=True, db_index=True)
    is_staff = models.BooleanField(
        _('staff status'), default=False, help_text=_(
            'Designates whether the user can log into this admin site.'))
    is_active = models.BooleanField(_('active'), default=True, help_text=_(
        'Designates whether this user should be treated as '
        'active. Unselect this instead of deleting accounts.'))
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)

    avatar = models.ImageField(upload_to='avatars', blank=True, null=True)
    avatar_thumbnail = ImageSpecField(source='avatar',
                                      processors=[ResizeToFill(300, 300)],
                                      format='JPEG',
                                      options={'quality': 80})

I even created a test

from __future__ import absolute_import
from __future__ import unicode_literals

from django.test import TestCase

from allauth.socialaccount.models import SocialApp, SocialAccount, SocialToken, SocialLogin

from . import factories


class TestVarious(TestCase):
    def test_serializing_user(self):
        user = factories.UserFactory()

        sapp = SocialApp.objects.create(
            provider='facebook',
            name='facebook',
            client_id='fake',
            secret='fake',
            key='fake'
        )

        sa = SocialAccount.objects.create(
            user=user,
            provider='facebook',
            uid='1234'
        )

        st = SocialToken.objects.create(
            app=sapp,
            account=sa,
            token='fake',
            token_secret='fake'
        )

        sl = SocialLogin(account=sa, token=st)

        sl.serialize() # this throws TypeError: <ImageFieldFile: None> is not JSON serializable

Removing avatar = models.ImageField(upload_to='avatars', blank=True, null=True) fixes the issue

My temporary fix was this:

_avatar = models.ImageField(upload_to='avatars', blank=True, null=True)

If we cannot name it with a _ what do we do ? I have the same problem with a ImageField as well


ImageFieldFile: photos/default_avatar.png> is not JSON serializable

you can't serialize the object, because it's an Image. You have to serialize the string representation of it's path.
The easiest way of achiving it is to call it's str() method when you what to serialize it.
json.dumps(unicode(my_imagefield)) #py2
json.dumps(str(my_imagefield)) #py3
should work.

I was actually doing something wrong, I was saving the user into the database in my adapter methode "populate_user" and this was causing the error. If I set the data to the data dictionary and then pass it to the method "populate_user" of the parent class it works.

I'm running into this issue too and as @patrick91 pointed out. Removing the field or renaming it with _ prefix "fixes the error" but I'm sure this is not the best solution for this. Have some of you fixed this in the right way?

I fixed it changing the name of the fields, but I also added a proxy to the original name of the fields, so I could keep the rest of the code as it was.

Before the fix.

class User(AbstractUser):
    ...
    avatar = models.ImageField(upload_to='avatars', null=True, blank=True)
    cover = models.ImageField(upload_to='covers', null=True, blank=True)

With the fix.

class User(AbstractUser):
    ...
    _avatar = models.ImageField(upload_to='avatars', null=True, blank=True, db_column='avatar')
    _cover = models.ImageField(upload_to='covers', null=True, blank=True, db_column='cover')

    @property
    def avatar(self):
        return self._avatar

    @avatar.setter
    def avatar(self, value):
        self._avatar = value

    @property
    def cover(self):
        return self._cover

    @cover.setter
    def cover(self, value):
        self._cover = value

I am experiencing this issue with default django imagefield.

Below is my test.

from __future__ import absolute_import
from __future__ import unicode_literals

from django.test import TestCase
from django.db import models

from allauth.utils import serialize_instance


class ImageModel(models.Model):
    """
    An abstract base class model that provides image field
    that represents the model.
    """
    image = models.ImageField(upload_to='image')


class TestCase(TestCase):

    def test_serialization(self):
        image_model = ImageModel.objects.create()
        serialize_instance(image_model)

When you run it:

$ ./manage.py test custom_user
Creating test database for alias 'default'...
E
======================================================================
ERROR: test_serialization (app.tests.TestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/project/app/tests.py", line 22, in test_serialization
    serialize_instance(image_model)
  File "/project/allauth/utils.py", line 184, in serialize_instance
    return json.loads(json.dumps(ret, cls=DjangoJSONEncoder))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py", line 250, in dumps
    sort_keys=sort_keys, **kw).encode(obj)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 207, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 270, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/google_appengine/lib/django-1.5/django/core/serializers/json.py", line 103, in default
    return super(DjangoJSONEncoder, self).default(o)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 184, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <ImageFieldFile: None> is not JSON serializable

----------------------------------------------------------------------
Ran 1 test in 0.055s

FAILED (errors=1)
Destroying test database for alias 'default'...

@andres-torres-marroquin 's solution would work, but I don't think we should be forced to use a monkey patch.

This is a django-allauth issue because custom user models can have any Django model field. If your user model had a HandField from the docs, for example, this error would occur because DjangoJSONEncoder doesn't know how to encode that type.

Potential solutions include:

  • Not trying to serialise the User instance. Instead just serialise the natural key and fetch the user using that when needed (seems best to me)
  • Add another setting specifying which JSON encoder to use.

@pennersr it seems @ryankask is right, I'm having the same issue with a custom user model with a type that DjangoJSONEncoder doesn't know how to encode.

Django: 1.10
Allauth: 0.27.0

Error: TypeError: <UTC> is not JSON serializable

Line on allauth/socialaccount/models.py ? in serialize:

    def serialize(self):
        ret = dict(account=serialize_instance(self.account),
                   user=serialize_instance(self.user),
                   state=self.state,
                   email_addresses=[serialize_instance(ea)
                                    for ea in self.email_addresses])
        ...

I'm having this same issue with a custom User model that has a ProcessedImageField from django-imagekit.

I think a setting to specify which JSONEncoder to use would work.

It seems the issue is confirmed for custom users models with custom fields, which are more prevalent nowadays. It would be good if this issue could be reopened and the label Unconfirmed removed.

Not trying to serialise the User instance. Instead just serialise the natural key and fetch the user using that when needed (seems best to me)

That is not an option. The user being serialized is actually a temporary user instance, not yet backed by a database record, hence, no key exists.

A bit of background: the issue is caused by the fact that after the provider handshake unsaved model instances are being assembled. So, we have an unsaved User instance, a SocialAccount instance and perhaps a SocialToken instance.

Now, it may be the case that after the handshake additional questions are to be asked to the user using the social signup form. What happens in this case is that the unsaved instances are stashed into the session, to be picked up at a later time once the social signup form is completed.

Making the serialization pluggable is an option, though it is quite a hassle having to setup your own serialization for something as simple as a TimeZoneField. Also, I am afraid people will easily forget about this scenario, as depending on the flows the serialization route may or may note be taken.

Adding support for more fields can be done. For example, the ImageField is a regular Django field that can be properly handled. But, that won't help you when you are using a 3rd party TimeZoneField.

All in all, I would really like to see a solution that "just works"... leaning towards something like this:

  • Add support for ImageField
  • For fields that are not serializable, store the DB prep value:
>>> instance._meta.get_field('timezone').get_prep_value(v)
'UTC'

And, use from_db_value when deserializing.

The above is likely to be sufficient to get things working out of the box for most use cases. But, just in case there you have uncommon models/data types, we can move the (de)serialize method into the adapter so that if all else fails one can still override and adjust things as needed.

I've moved ahead and added the above (cedad9f156a8c78bfbe43a0b3a723c1a0b840dbd). This approach is not pretty, but hopefully it is effective. Could you all let me know if this solves the issues with your custom user models...?

@pennersr I tried the commit and it fixed my issue.

@pennersr sorry for the delay. I just tried the new version of allauth which includes the mentioned commit and on my case the problem seems to be solved.

I have custom user model with ImageField. I'd got error Unable to auto serialize field 'origin', custom serialization override required, because 'ImageField' object has no attribute 'from_db_value'.

I'm also still get this error running 0.35.0, with a custom user model that has a django ImageField.

I have a User model that is a subclass of AbstractUser and that has a 'photo' field (ImageField).

The following error occurs:

'Internal Server Error: /accounts/social/signup / ImproperlyConfigured at /accounts/social/signup/ Unable to auto serialize field 'photo', custom serialization override required

django-allauth==0.40.0
Any idea to fix this???

I have a User model that is a subclass of AbstractUser and that has a 'photo' field (ImageField).

The following error occurs:

ImproperlyConfigured at /accounts/social/signup/
Unable to auto serialize field 'photo', custom serialization override required

django-allauth==0.40.0
Any idea to fix this???

For sharing. I ended up adding a validation in the deserialize_instance method

SOCIALACCOUNT_ADAPTER=CustomSocialAccountAdapter

class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
    def deserialize_instance(self, model, data):
        ...
        elif is_db_value and not isinstance(f, ImageField):
        ....

I copied the original method and added this line in previous if:
and not isinstance(f, ImageField)

Thx.

Was this page helpful?
0 / 5 - 0 ratings