I want to extend the Django user class in Django 1.7.1 to drop the first name and last name and to put a unique constraint on the e-mail address. So far I have this custom model to change the constraints/fields:
class ExtUser(AbstractBaseUser, PermissionsMixin):
class Meta:
# Django 1.7.2?
#default_related_name = 'user'
db_table = 'auth_user'
verbose_name = _('user')
verbose_name_plural = _('users')
abstract = False
username = models.CharField(_('username'), max_length=30, unique=True,
help_text=_('Required. 30 characters or fewer. Letters, digits and '
'#/./+/-/_ only.'),
validators=[
validators.RegexValidator(r'^[\w.#+-]+$', _('Enter a valid username.'), 'invalid')
])
email = models.EmailField(_('email address'), max_length=75, blank=False, null=False, unique=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)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
def get_short_name(self):
return self.username
def email_user(self, subject, message, from_email=None, **kwargs):
send_mail(subject, message, from_email, [self.email], **kwargs)
And I modified the settings file:
AUTH_USER_MODEL = 'ext.ExtUser'
I can login to the Django admin, but when I try to edit a user, I get the following exception:
ProgrammingError at /api/extuser/1/
column auth_user_groups.extuser_id does not exist
LINE 1: ...oup"."id" = "auth_user_groups"."group_id" ) WHERE "auth_user...
I guess the user foreign key in other tables is formed based upon the class name... How do I fix this? I tried using the default_related_name field in the model meta class, but that had no positive effect.
Thanks in advance for any help!
Kind regards,
K.
OK, so I found a solution for my problem: make sure that the custom user model class is named "User" instead of "ExtUser" and everything will keep on working.
Easy! :-)
Related
I am new to django and set up my first project with one app in the beginning. Some time later I figured out that I need a custom user model so I've created a second app with the custom user model as I've read somewhere that you need the user model as first migration (I'm telling this, because I believe my project structure is causing my problem).
Right now I am working on an avatar upload for the user model.
I am able to upload an image via DRF and then open the image in my browser, but not with the saved url in my database.
Django saves the image like this in the database: http://localhost:8000/api/users/<user_id>/media/avatars/image.jpg.
But the correct url would be: http://localhost:8000/media/avatars/image.jpg
How do I make django save the correct url?
I've set MEDIA_ROOT and MEDIA_URL like this:
MEDIA_ROOT = BASE_DIR + 'media'
MEDIA_URL = 'media/'
My project structure:
my project
- backend (first app)
- - manage.py
- - settings.py
- users (second app)
- - models.py # here is my custom user model
The custom user model:
class User(AbstractBaseUser, PermissionsMixin):
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_('username'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.'),
validators=[username_validator],
error_messages={
'unique': _("A user with that username already exists."),
},
)
email = models.EmailField(_('email address'), unique=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
avatar = models.ImageField(
upload_to='avatars/',
null=True,
blank=True
)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
class Meta:
db_table = 'auth_user'
verbose_name = _('user')
verbose_name_plural = _('users')
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
Any help is appreciated
Try change your settings.py to absolute paths:
MEDIA_ROOT = '/var/www/your_project_path/media/'
MEDIA_URL = '/media/'
Remember about slashes / /
I've been trying to store value of one checkbox, promo_consent, sent from a form into a new column that I've just created in the DB.
Whatever I do it stores always TRUE no matter wheter the checkboxed was checked or not, or stores always FALSE no matter wheter the checkboxes was checked or not.
I have this model:
class User(AbstractBaseUser, PermissionsMixin, PagePermissionMixin):
"""User model for both staff and clients.
It consists of base AbstractBaseUser class and has 2 permissions mixins.
One of them is for standard django permissions and the second is
for Page object permissions.
Note:
This model is used for OAuth2 and Django authentication.
"""
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('email address'), unique=True, blank=True)
new_email = models.EmailField(_('new email'), 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)
promo_consent = models.BooleanField(
_('Promo consent'),
default=False,
help_text=_('Shows whether user has agreed to be notified about '
'news and promo sales'))
....
....
....
and this serializer:
class RegistrationSerializer(SerializerSchemaMixin, serializers.Serializer,
SerializerValidateMixin,
EmailUniquenessValidationMixin,
PasswordValidationMixin):
"""Registration serializer."""
first_name = serializers.CharField(max_length=30, default='')
last_name = serializers.CharField(max_length=30, default='')
email = serializers.EmailField(required=True)
password = serializers.CharField(max_length=100, required=True)
password2 = serializers.CharField(max_length=100, required=True)
rules = serializers.BooleanField(required=True)
promo_consent = serializers.BooleanField(required=False)
def validate_rules(self, value):
"""Checks if 'rules' is True."""
if not value:
raise serializers.ValidationError(_('Rules has to be checked'))
else:
return value
def promo_consent(self, value):
return true
def validate(self, data):
"""Override serializer.validate()."""
self.validate_passwords_uniformity(data)
return data
def save(self, **kwargs):
"""Register new user and send confirmation email."""
language = kwargs['language']
email = self.validated_data['email']
promo_consent = self.promo_consent
self.instance = User(first_name=self.validated_data['first_name'],
last_name=self.validated_data['last_name'],
email=email,
is_active=False,
email_confirmation=uuid.uuid4().hex)
self.instance.set_password(self.validated_data['password'])
self.instance.save()
self.instance.send_email_confirmation(language, email)
return self.instance
I've been working this the second day. What am I missing here?
I have a custom user model that extends an AbstractBaseUser, as well as my own user manager (GenericUserManager):
class GenericUserManager(BaseUserManager):
def create_user(self, username, email, password, key_expires):
if not email:
raise ValueError('Users must have an email address')
if not username:
raise ValueError("users must have a username")
if not password:
raise ValueError('users must have password')
user = self.model(
username=username,
email=self.normalize_email(email),
key_expires=key_expires,
)
user.set_password(password)
user.save(using=self._db)
return user
def get_by_natural_key(self, username):
return self.get(username=username)
class BaseRegistrationUser(AbstractBaseUser):
username = models.CharField(max_length=150, unique=True)
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=False,
)
activation_key = models.CharField(max_length=90)
key_expires = models.DateTimeField()
is_active = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
date_joined = models.DateTimeField(('date joined'),
default=datetime.datetime.now)
is_merchant_or_customer = models.CharField(max_length=20, null=False)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['username, email']
def __str__(self):
return self.username
...
def natural_key(self):
return (self.email,)
...
class Customer(BaseRegistrationUser):
first_name = models.CharField(max_length=150, default="", null=True)
last_name = models.CharField(max_length=150, default="", null=True)
slug = models.SlugField(max_length=175, default="", null=True)
objects = GenericUserManager()
...
These work fine for registration purposes, however, whenever I attempt to log a 'customer' user I get the following error:
File "C:\Users\OEM\Documents\repos\repo\ecommerce\myapp\views.py" in get_profile
126. user = authenticate(username=username, password=password)
File "C:\Users\OEM\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\django\contrib\auth\__init__.py" in authenticate
70. user = _authenticate_with_backend(backend, backend_path,
request, credentials)
File "C:\Users\OEM\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\django\contrib\auth\__init__.py" in _authenticate_with_backend
115. return backend.authenticate(*args, **credentials)
File "C:\Users\OEM\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\django\contrib\auth\backends.py" in authenticate
18. user =
UserModel._default_manager.get_by_natural_key(username)
Exception Type: AttributeError at /get-profile/
Exception Value: 'Manager' object has no attribute 'get_by_natural_key'
I don't know what causes this since the 'get_by_natural_key' attribute is defined in my UserManager.
I've tried new database migrations and spent a solid 3 days just combing through other questions on this site, but unfortunately, it hasn't helped. I would be extremely grateful someone could help me figure this out.
EDIT:Fixed the indenting, but the error persists
It looks like your indentation is wrong. get_by_natural_key() is a function, not a method of your manager class.
Ok, so the solution, in hindsight, should have been more obvious. I correctly assigned the customer my user manager class, however, I had not applied the same to my BaseRegistrationUser, which meant that it was missing the required methods to log in using the django.auth methods
I created an user app MyUser which is an extension of AbstractBaseUser. I was under the impression that the model MyUser will replace the standard Auth.User, as long as it is mentioned in settings.py
AUTH_PROFILE_MODULE = 'profile.MyUser'
My trouble is now that I can't set the permissions for users registered in the MyUser model. When I try to set the group memberships and permissions, I get the error User' instance expected, got <MyUser: username>.
How can I add users of my user model to the permissions and correct groups?
class MyUserManager(BaseUserManager):
def create_user(self, username, email, phone, password=None, company=False):
if not email:
raise ValueError('Users must have an email address')
if not username: username = email.split('#')[0]
user = self.model(
email=MyUserManager.normalize_email(email),
username=username,phone=phone,)
user.set_password(password)
user.save(using=self._db)
# add to user group and set permissions
if company:
g = Group.objects.get(name='company')
p = Permission.objects.get(codename='add_company')
else:
g = Group.objects.get(name='user')
p = Permission.objects.get(codename='add_user')
g.user_set.add(user)
user.user_permissions.add(p)
return user
class MyUser(AbstractBaseUser):
username = models.CharField(max_length=254, unique=True, blank=True, null=True)
email = models.EmailField(max_length=254, unique=True, db_index=True)
phone = models.CharField(_('Phone Number'), max_length=25, blank=True, null=True,)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['phone']
objects = MyUserManager()
def get_full_name(self):
return self.email
...
Just add PermissionsMixin to your model declaration! :)
class MyUser(AbstractBaseUser, PermissionsMixin):
...
Here is the relevant part of Django docs.
You've probably resolved your issue but for completeness did you mean to refer to setting AUTH_USER_MODEL vs AUTH_PROFILE_MODULE when creating a custom user model in Django 1.5.
ref: https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#substituting-a-custom-user-model
Am using a custom user model to maintain user details with django 1.5, below is my code. When using social auth, everything seems to be working fine accept first_name and last_name.
In my table table structure, I do not have first_name and last_name, instead, I have one field full name.
but, i did create a method in the manager to accept first and last names and combine them. but, still am receiving empty values, any advise on why this is happening?
class UserManager(BaseUserManager):
"""
UserManager()
"""
def create_user(self, username, email=None, password=None, first_name='', last_name='', **extra_fields):
if not username:
raise ValueError('Users must have a username')
user = self.model(
username=username,
email=UserManager.normalize_email(email),
full_name='%s %s' % (first_name, last_name),
)
user.set_password(password)
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
"""
Accounts/User
"""
GENDERS = (
(u'M', _('Male')),
(u'F', _('Female')),
)
username = models.CharField(_('username'), max_length=30, unique=True,
help_text=_('Required. 30 characters or fewer. Letters, numbers and '
'#/./+/-/_ characters'),
validators=[
validators.RegexValidator(re.compile('^[\w.#+-]+$'), _('Enter a valid username.'), 'invalid')
])
email = models.EmailField(_('email address'), 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)
# user details and information
full_name = models.CharField(_('full name'), max_length=30, )
date_of_birth = models.DateField(_('birthday'), null=True, blank=True)
bio = models.TextField(_('biography / about you'), null=True, blank=True, help_text=_('Biography / About you'))
gender = models.CharField(_('gender'), max_length=1, blank=True, null=True, choices=GENDERS)
# follows / followers
followings = models.ManyToManyField('self', through='Relationship', symmetrical=False, related_name='followers')
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email', 'date_of_birth', 'full_name',]
Django-social-auth doesn't work like that when creating user, instead when calling create_user in your manager, it just passes the username and the email as you can see here. Later in the pipeline more fields in your user model are update when this is called, but as the code says, it does a getattr(user, name, None) which in you case for first_name and last_name, returns None since those fields aren't defined in your model.
You can trick the system by defining some properties in your model, something like this:
class User(...):
#property
def first_name(self):
if not hasattr(self, '_first_name'):
self._first_name = self.full_name.split(' ', 1)[0]
return self._first_name
#first_name.setter
def first_name(self, value):
self._first_name = value
self.build_full_name()
#property
def last_name(self):
if not hasattr(self, '_last_name'):
self._last_name = self.full_name.split(' ', 1)[-1]
return self._last_name
#first_name.setter
def last_name(self, value):
self._last_name = value
self.build_full_name()
def build_full_name(self):
self.full_name = ' '.join([self._first_name, self._last_name])