8

I have django custom user model MyUser with one extra field:

# models.py
from django.contrib.auth.models import AbstractUser

class MyUser(AbstractUser):
    age = models.PositiveIntegerField(_("age"))

# settings.py
AUTH_USER_MODEL = "web.MyUser"

I also have according to these instructions custom all-auth Signup form class:

# forms.py
class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30)
    last_name = forms.CharField(max_length=30)
    age = forms.IntegerField(max_value=100)

    class Meta:
        model = MyUser

    def save(self, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.age = self.cleaned_data['age']
        user.save()

# settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'web.forms.SignupForm'

After submitting SignupForm (field for property MyUser.age is rendered corectly), I get this error:

IntegrityError at /accounts/signup/
(1048, "Column 'age' cannot be null")

What is the proper way to store Custom user model?

django-allauth: 0.12.0; django: 1.5.1; Python 2.7.2

Myzel394
  • 1,155
  • 3
  • 16
  • 40
illagrenan
  • 6,033
  • 2
  • 54
  • 66
  • My guess is `user.age = self.cleaned_data['age']` is evaluating to `None` somehow.. Can you verify the value ? and what is `user` parameter being sent into the `save` method ? – karthikr Jul 14 '13 at 00:35
  • You must return `user` from `save` method also `return user` – Aamir Rind Jul 14 '13 at 01:00
  • @karthikr I found that method `save()` is not executed. The exception is thrown, when new user is created in [`django-allauth/allauth/account/forms.py#269`](https://github.com/pennersr/django-allauth/blob/master/allauth/account/forms.py#L269). – illagrenan Jul 14 '13 at 10:03
  • [`IntegrityError`](https://code.djangoproject.com/wiki/IntegrityError) "Column 'age' cannot be null" – illagrenan Jul 14 '13 at 13:47
  • @illagrenan I would recommend overriding the form's `clean` rather than the `save` method. That way, the error could be caught before the save. I still feel `def save(self, user)` is not right – karthikr Jul 14 '13 at 13:55
  • @karthikr It does not work, my `clean` method is not executed. Django all-auth internal class `SignupForm` is **inheriting from my custom** `SignupForm` ([see code on Github](https://github.com/pennersr/django-allauth/blob/master/allauth/account/forms.py#L235-L279)). Method `save(self, user)` is from [all-auth documentation](http://django-allauth.readthedocs.org/en/latest/): "(...) This class should implement a ‘save’ method, accepting the newly signed up user as its only parameter." – illagrenan Jul 14 '13 at 14:30

3 Answers3

14

Though it is a bit late but in case it helps someone.

You need to create your own Custom AccountAdapter by subclassing DefaultAccountAdapter and setting the

class UserAccountAdapter(DefaultAccountAdapter):

    def save_user(self, request, user, form, commit=True):
        """
        This is called when saving user via allauth registration.
        We override this to set additional data on user object.
        """
        # Do not persist the user yet so we pass commit=False
        # (last argument)
        user = super(UserAccountAdapter, self).save_user(request, user, form, commit=False)
        user.age = form.cleaned_data.get('age')
        user.save()

and you also need to define the following in settings:

ACCOUNT_ADAPTER = 'api.adapter.UserAccountAdapter'

This is also useful, if you have a custom SignupForm to create other models during user registration and you need to make an atomic transaction that would prevent any data from saving to the database unless all of them succeed.

The DefaultAdapter for django-allauth saves the user, so if you have an error in the save method of your custom SignupForm the user would still be persisted to the database.

So for anyone facing this issue, your CustomAdpater would look like this

class UserAccountAdapter(DefaultAccountAdapter):

    def save_user(self, request, user, form, commit=False):
        """
        This is called when saving user via allauth registration.
        We override this to set additional data on user object.
        """
        # Do not persist the user yet so we pass commit=False
        # (last argument)
        user = super(UserAccountAdapter, self).save_user(request, user, form, commit=commit)
        user.age = form.cleaned_data.get('age')
        # user.save() This would be called later in your custom SignupForm

Then you can decorate your custom SignupForm's with @transaction.atomic

@transaction.atomic
def save(self, request, user):
    user.save() #save the user object first so you can use it for relationships
    ...
Yahya Yahyaoui
  • 2,833
  • 23
  • 30
Divick
  • 1,213
  • 1
  • 20
  • 44
1

Side note

With Django 1.5 custom user model, the best practice is to use the get_user_model function:

from django.contrib.auth import get_user_model

# forms.py
class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30)
    last_name = forms.CharField(max_length=30)
    age = forms.IntegerField(max_value=100)

    class Meta:
        model = get_user_model() # use this function for swapping user model

    def save(self, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.age = self.cleaned_data['age']
        user.save()

# settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'web.forms.SignupForm'

Maybe it's not related, but I thought it would be worth noticing.

Benjamin Toueg
  • 10,511
  • 7
  • 48
  • 79
  • I want to make the property `age` mandatory, but django-allauth is [trying to save `MyUser`](https://github.com/pennersr/django-allauth/blob/master/allauth/account/forms.py#L269) only with default fields. And thanks for `get_user_model` function, I didn't know that. – illagrenan Jul 14 '13 at 09:48
0

i think you should define fields property in class Meta in SignupForm and set list of fields that contains age, like this :

class SignupForm(forms.Form):
...
   class Meta:
      model = MyUser
      fields = ['first_name', 'last_name', 'age']

and if it's not worked, look at this

Community
  • 1
  • 1
KeyOne
  • 183
  • 6