4

How do I extend the django-oscar customer models fields? I have extended the registration form to include more fields, in apps/customer/forms.py

class EmailUserCreationForm(forms.ModelForm):
    email = forms.EmailField(label=_('Email address'))
    password1 = forms.CharField(
        label=_('Password'), widget=forms.PasswordInput,
        validators=password_validators)
    password2 = forms.CharField(
        label=_('Confirm password'), widget=forms.PasswordInput)

    #### The extra fields I want to add #####

    first_name = forms.CharField(label=_('First name'))
    last_name = forms.CharField(label=_('Last name'))
    business_name = forms.CharField(label=_('Business name'))
    business_address = forms.CharField(label=_('Business address'))
    city = forms.CharField(label=_('City'))

I have also extended the [AbstractUser][1]'s fields in apps/customer/abstract_models.py.

class AbstractUser(auth_models.AbstractBaseUser,
                   auth_models.PermissionsMixin):
    """
    An abstract base user suitable for use in Oscar projects.

    This is basically a copy of the core AbstractUser model but without a
    username field
    """
    email = models.EmailField(_('email address'), unique=True)
    first_name = models.CharField(
        _('First name'), max_length=255, blank=True)
    last_name = models.CharField(
        _('Last name'), max_length=255, blank=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)
    #######################################
    # Additional user fields I have added #
    #######################################
    business_name = models.CharField(
        _('Business name'), max_length=255, blank=True)
    business_address = models.CharField(
        _('Business address'), max_length=255, blank=True)
    city = models.CharField(

However, when a user is created, the additional fields are not getting saved to the database. Is there a better way of extending the customer model to include additional fields I'm not aware of?

When I try to debug in the shell, I run into the issue where the model is not callable:

>>> from apps.customer.abstract_models import *
>>> mg = UserManager()
>>> mg.create_user('testemail@test.com', 'testpassword', buisness_name='test_business')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "<my_working_dir>/apps/customer/abstract_models.py", line 34, in create_user
    last_login=now, date_joined=now, **extra_fields)
TypeError: 'NoneType' object is not callable

I'm not sure the instructions given in django oscar's docs will work, as that is for customizing methods, not fields on the model.

Any help would be appreciated.

Edit:

INSTALLED_APPS = INSTALLED_APPS + get_core_apps(
    ['apps.shipping',
     'apps.checkout',
     'apps.partner',
     'apps.catalogue',
     'apps.customer',
     ])


AUTH_USER_MODEL = 'customer.User'
Tui Popenoe
  • 2,098
  • 2
  • 23
  • 44
  • It's perfectly possible to add fields to the model. Your issue is that your app isn't being loaded. To help us identify the issue, please post: details of where you have create this new code (what files/directories relative to your project root), `INSTALLED_APPS` and `AUTH_USER_MODEL` settings. – solarissmoke Jul 10 '16 at 03:12
  • I would extend an address model instead of user model. Address will have is_default attribute and foreign key to user. – Meph- Mar 17 '17 at 22:31

1 Answers1

5

There are a few issues that I can see, resolving which will hopefully fix your problem:

  1. Move your subclass of AbstractUser into apps/customer/models.py which is where Django looks for models. You've put it in apps/customer/abstract_models.py which is a non-standard location for storing models (Oscar does this for abstract models only - you are not supposed to mirror this location yourself). Django will not find them there.

  2. Change your class name to User instead of AbstractUser, because your final model is not abstract. You are also specifying customer.User in your AUTH_USER_MODEL - these two need to match.

  3. The model class you have posted above is incomplete so we cannot tell - but make sure that it doesn't contain abstract = True in the Meta class.

  4. Run manage.py makemigrations which should create migrations for your new user model (if it doesn't then there's still something wrong with your app structure). (Next run manage.py migrate).

  5. Don't forget to import the rest of the (core) customer models at the bottom of your models.py: from oscar.apps.customer.models import *. Without these you will lose all the other models that live in the core customer app.

You should also take note of the warning in the documentation regarding changing the user model (emphasis mine):

Changing AUTH_USER_MODEL has a big effect on your database structure. It changes the tables that are available, and it will affect the construction of foreign keys and many-to-many relationships. If you intend to set AUTH_USER_MODEL, you should set it before creating any migrations or running manage.py migrate for the first time.

Changing this setting after you have tables created is not supported by makemigrations and will result in you having to manually fix your schema, port your data from the old user table, and possibly manually reapply some migrations.

solarissmoke
  • 30,039
  • 14
  • 71
  • 73
  • I have performed these changes, but now I am having issues with the auth | user model conflicting with my customized customer user model. There had been accounts created previously to updating the AUTH_USER_MODEL attribute to customer.User – Tui Popenoe Jul 11 '16 at 05:16
  • This is the inherent risk with changing the user model in place - it is not easy or recommended on an existing database. I have added to the answer some warnings from the documentation - can't really help you beyond that. – solarissmoke Jul 11 '16 at 05:23
  • can I just set the model using = get_user_model() in the class, and then adding the fields on top of it? – Tui Popenoe Jul 11 '16 at 05:50
  • No, I don't think so. You could try setting your model's `Meta` class to use the same table name as before (`db_table = 'auth_user'`) and see if the migrations work then. If not then you either need to manually fix up the table, or consider an alternative approach (e.g., a [profile model](https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#extending-the-existing-user-model)). – solarissmoke Jul 11 '16 at 05:52
  • alright, I manually fixed up the tables, and the users have transferred to the new auth model now. – Tui Popenoe Jul 12 '16 at 14:16
  • Hmm, it looks like all the tables were not fixed by migrations, there are still server errors regarding ProgrammingError: column partner_partner_users._id does not exist LINE 1: ...tner_users" INNER JOIN "user_" ON ( "partner_p... To solve this, will I have to manually edit the table names in psql with ALTER TABLE? or is there a way to do it through migrations/python shell? – Tui Popenoe Jul 13 '16 at 17:52
  • I think you will have to manually alter the table names. You could conceivably write a migration to do it but I don't think it would be any quicker/easier. – solarissmoke Jul 14 '16 at 05:41