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'
There are a few issues that I can see, resolving which will hopefully fix your problem:
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.
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.
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.
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).
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.
Related
I ran into a problem
I am making a job search site on Django, I have the following logic:
Authorization and authentication of ordinary job seekers using Django's built-in model - User
Also separate authorization and authentication for users who provide work, i.e. employers,
which are placed in my own model Employer
Here is my Employer model
class Employer(AbstractUser):
full_name = models.CharField(max_length=150, verbose_name="Ім'я")
main_office_city = models.ForeignKey(City, on_delete=models.CASCADE,
verbose_name='Місто головного офісу')
phone_number = models.ForeignKey(Phone, on_delete=models.CASCADE)
email = models.CharField(max_length=50, unique=True, verbose_name='Email')
hashed_password = models.CharField(max_length=120, default='')
date_joined = models.DateTimeField(verbose_name='Дата реєстрації',
default=timezone.now)
def __str__(self):
return self.full_name
class Meta:
verbose_name = 'Роботодавець'
verbose_name_plural = 'Роботодавці'
I read in the documentation that to create your own authentication system you can use the imitation from the AbstractUser class
But in my case this is not the best choice, because AbstractModel adds its own fields by default.
That is, I think that I need to either somehow make it so that the AbstractUser class does not add its fields, or think of some other authentication logic using another technology
Maybe someone has some ideas how it can be done?
I have custom user model:
class User(AbstractUser):
"""
Our own User model. Made by overriding Django's own AbstractUser model.
We need to define this as the main user model in settings.py with
variable AUTH_USER_MODEL *IMPORTANT*
"""
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(
max_length=255,
unique=True,
verbose_name="email address"
)
institute = models.ForeignKey(
Institute, on_delete=models.SET_NULL, null=True)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __str__(self):
return self.email
I have two users in my projects. One is the super admin and the other is the admin. I've specified the model for admin as such:
class AdminUser(User):
is_admin = False
is_staff = True
def __str__(self):
return self.first_name+" "+self.last_name
# Control model features
class Meta:
verbose_name = 'Admin User'
verbose_name_plural = 'Admin Users'
Now I want to revoke access of the entire user model from this Admin User. How could this be achieved? We are going to have many more user types in future. They will be specified through models.
You can achieve this by just using groups in Django.
Your superuser will be you, that will have all permissions for default using the field is_admin=True. The second user can be a simple Staff user that is member of the group "Admins", just create the group and assign all the permissions to that group except the permissions to Add, Change and Delete users.
That will do the trick.
Update: If you need more complex cases, you can use the app django-groups-manager or django-guardian So you can create groups in trees, there you can use more domain driven permission rules.**
I have a model class Department with a field name. I have another Model Student with a foreign key to Department. I want to control access to Student objects based on department. That is, a user with permission to edit the department with name "CS" can only edit that fields. How this can be achieved in Django? (I'm using django 1.8, python3)
Edit
class Department(models.Model):
name = models.CharField(_('department name'), max_length=255)
class Students(models.Model):
first_name = models.CharField(_('first name'), max_length=30)
last_name = models.CharField(_('last name'), max_length=30)
department = models.ForeignKey('Department')
Also I'm creating required permissions dynamically while adding new department.(eg: if department.name for new entry is 'CS', 2 permissions like 'view_CS' and 'edit_CS' will be created)
Based on http://django-guardian.readthedocs.org/en/v1.2/userguide/assign.html#for-group
class Department(models.Model):
name = models.CharField(_('department name'), max_length=255)
class Meta:
permissions = (
('view', 'View department'),
('edit', 'Edit department'),
)
Somewhere in views:
from django.contrib.auth.models import Group
cs_department = Department.objects.get(name='cs_department')
cs_department_group = Group.objects.create(name=cs_department.name)
assign_perm('view', cs_department_group, cs_department)
assign_perm('edit', cs_department_group, cs_department)
request.user.groups.add(cs_department_group)
print(request.user.has_perm('view', cs_department)) # True
print(request.user.has_perm('edit', cs_department)) # True
Since my application is pretty big, I cannot afford changing entire data references to accomodate the permissions as #madaohan's answer.
This kind of access control mechanisms can be easily used defining a custom model manager(docs) and a middleware to get logged in user object in models(Check this link),
I'm trying to set up an app that will handle reviews about registered users. So in my Review model, I want to have a ForeignKey to my User model.
I'm using a custom user profile that looks like this:
#In /profiles/models.py
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
company = models.CharField(default="", max_length=200)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['company']
I have included it with settings.py AUTH_USER_MODEL = "profiles.MyUser". It works fine with registration, creating users etc. So I know its working.
In my review model I write the following:
class Review(models.Model):
company = models.ForeignKey(settings.AUTH_USER_MODEL)
reviewer = models.ForeignKey(Reviewer)
rating = models.IntegerField(default=0)
review = models.TextField()
pub_date = models.DateTimeField('date published')
Instead of settings.AUTH_USER_MODEL I have also tried writing profiles.MyUser, 'profiles.MyUser' and MyUser.
I can successfully use the python manage.py makemigrations reviews command. But when I do python manage.py migrate I get errors no matter what version I use above.
The error I get is the following:
ValueError: Lookup failed for model referenced by field reviews.Review.company: profiles.MyUser
nejc92 comment was correct. I had migrated my database earlier before I set AUTH_USER_MODEL for the first time.
I removed my whole database and created new migrations for all apps and migrated everything again from scratch. It then worked.
Sounds like a bug(?) to me.
I'm working on an existing Django project in Django 1.4. I want to upgrade to Django 1.5 for implementing a custom user model. I have to ensure that my existing information is not deleted from the database(Postgres 9.1).
This post explains the steps to be done for data migration using South but only for AbstractUser. I have to use AbstractBaseUser. Is there any way?
My existing model is like :
class Individual(models.Model):
user = models.ForeignKey(User, unique=True, blank=True, null=True)
parent = models.ForeignKey('self', unique=True, blank=True, null=True)
....(some many to many and foreign key)
I want to change it to something like this.
class User(AbstractBaseUser, Individual, PermissionsMixin):
email = models.EmailFeild(max_length=50, unique=True)
is_admin = models.BooleanField(default=False)
username = models.CharField(max_length=50,unique=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
The individual class will contain all previous fields except the user. How can I go about doing this?
Document how to migrate from a built-in User model to a custom User model thread has some workaround discussions.