So the django docs https://docs.djangoproject.com/en/dev/topics/auth/customizing/ are great but I am confused, it would be great to have a mentor right now.
I want to create a custom user model and authentication system
the user model will have
-name
-password
-email
-JWT (javascript web token)
a permissions and a venue (place of entertainment) will be connected to this user model via a many to many relation via an association table.
Here is the problem, even with the docs I am confused how I override the current out of box implementation for authorization.
Additionally I'd like to use https://github.com/GetBlimp/django-rest-framework-jwt
for the token auth but I have no idea how to hook it up. I guess I am looking for a walk through.
If you want to remove fields from the built in User, you should use this model:
from django.core import validators
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.models import UserManager
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(_('username'), max_length=75, unique=True,
help_text=_('Required. 30 characters or fewer. Letters, numbers and '
'underscores characters'),
validators=[
validators.RegexValidator(re.compile('^[\w]+$'),
_('Enter a valid username.'), 'invalid')
])
first_name = models.CharField(_('first name'), max_length=254, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
email = models.EmailField(_('email address'), max_length = 254, unique = True, null = 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 = ['first_name']
def get_full_name(self):
return self.name
def get_short_name(self):
return self.username
This allows you to customise your User model fully. You can remove the appropriate fields. Then follow the instructions for setting up djangorestframework-jwt.
First of all, django has built-in User,for convenience you can use it, if you need to add other fields to current django User model:
form django.contrib.auth.models import User
class MyUser(User):
# define your additional custom fields
after installing djangorestframework-jwt do this setting in your settings.py
REST_FRAMEWORK = {
...
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
...
),
}
JWT_AUTH = {
# JWT related settings, see the docs
}
In urls.py just add this:
urlpatterns = [
....
url(r'^api-token-auth/', obtain_jwt_token),
...
]
Now you should first browse for getting a token and then browse your other APIs:
Do a post request to http://127.0.0.1:8000/api-token-auth/ and provide username and password to obtain your token.
On other requests, you should send this token:
GET http://127.0.0.1:8000/yoururl
HEADER
Authorization JWT <YourToken>
You can go forward with your requests with curl, httpie, postman (chrome app) or any other HttpClient
Related
I am building an app with multiple roles defined through Django Groups.
I started with a custom user model, defined as below.
I am seeing a weird difference in the groups and permissions use when using a custom user model, like the inheritance is missing something.
I would like to use a custom user model so I don't use username but I also need multiple groups and permissions in my application.
from django.db import models
from django.contrib.auth.models import AbstractUser, AbstractBaseUser, BaseUserManager, PermissionsMixin
import random
import string
from slugify import slugify
# Create your models here.
class MyAccountManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError("You must have an email address")
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(
email=self.normalize_email(email),
)
user.set_password(password)
user.is_admin=True
user.is_staff=True
user.is_superuser=True
user.save(using=self._db)
return user
#custom user account model
class User_Account(AbstractUser):
email = models.EmailField(verbose_name='email', max_length=60, unique = True)
date_joined = models.DateTimeField(verbose_name="Date Joined", auto_now_add=True)
last_login = models.DateTimeField(verbose_name="Last Login", auto_now=True)
username = models.CharField(max_length=100, null=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = MyAccountManager()
def __str__(self):
return self.email
When I create a new group with the custom user model, the group gets automatically assigned to all created users. I reproduced this behavior both programmatically and through the Django Admin.
When I use the default user model the group creation doesn't assign groups to all users automatically.
I also discovered that when using a custom user model the Django Admin for the users is not the same (the group and permission assignment fields are incomplete - screenshots below)
Weird incomplete Django Admin interface with groups and permissions missing the available fields
Normal Django Admin interface with group and permission assignment as expected - default user model
I managed to fix the issue in the Admin panel. It seems that it's a visual rendering problem caused by a wrong Admin class.
The error was caused by the following:
filter_horizontal = ()
list_filter = ()
fieldsets = ()
I have actually added the proper parameters in the class above but forgot to comment out/remove these lines. Works properly after commenting them out.
Try removing
filter_horizontal = (),
It worked for me.
Thanks in advance, i'm learning Django and can't figure how to override all auth forms. Quick explanation first, I have a custom user model
class PersoUser(AbstractBaseUser):
email = models.EmailField(
verbose_name="Email Adress", max_length=200, unique=True)
username = models.CharField(
verbose_name="username", max_length=200, unique=True)
first_name = models.CharField(verbose_name="firstname", max_length=200)
last_name = models.CharField(verbose_name="lastname", max_length=200)
date_of_birth = models.DateField(verbose_name="birthday")
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
objects = PersoUserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["date_of_birth", "username"]
....
and I would want to add date_of_birth field to my signup page, so I followed the official doc to override the specif form used by all auth SignupView https://django-allauth.readthedocs.io/en/latest/forms.html#signup-allauth-account-forms-signupform
which leads to ( in Book_store/forms.py )
from all auth.account.forms import SignupForm from users. models import PersoUser
class PersoUserRegisterForm(SignupForm):
class Meta:
model = PersoUser
fields = ["username", "email", "first_name",
"last_name", "date_of_birth", "password1", "password2"]
def save(self, request):
# Ensure you call the parent class's save.
# .save() returns a User object.
user = super(PersoUserRegisterForm, self).save(request)
# Add your processing here.
# You must return the original result.
return user
in my settings/base.py
ACCOUNT_FORMS = {'signup': 'Book_store.forms.PersoUserRegisterForm'}
My account/signup.html template just refers to {{form.as_p}} and it doesn't display the extra fields specified in PersouserRegisterForm just the default ones
I don't see what I'm missing, Thanks for reading
EDIT: And Signing up fail because it violates not-null constraint for date_of_birth
You are overriding the save method but not saving user with the date_of_birth field .
def save(self, request):
user = super(PersoUserRegisterForm, self).save(request)
user.date_of_birth = self.cleaned_data['date_of_birth']
user.save()
return user
I am trying to allow all active user to login to admin site because as a default just staff and superusers are able to login to admin site.
I've tried to overwrite clean method in my custom crate/change forms but it didn't help
class User(GuardianUserMixin, AbstractUser):
name = models.CharField(_("Name of User"), blank=True, max_length=255)
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
#admin.register(User)
class UserAdmin(GuardedModelAdminMixin, auth_admin.UserAdmin):
form = UserChangeForm
add_form = UserCreationForm
fieldsets = FIELDSETS
list_display = ["username", "name", "is_active", "is_superuser", "is_staff"]
search_fields = ["name", "username"]
class UserChangeForm(forms.UserChangeForm):
class Meta(forms.UserChangeForm.Meta):
model = User
class UserCreationForm(forms.UserCreationForm):
error_message = forms.UserCreationForm.error_messages.update(
{"duplicate_username": _("This username has already been taken.")}
)
class Meta(forms.UserCreationForm.Meta):
model = User
def clean_username(self):
username = self.cleaned_data["username"]
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise
ValidationError(self.error_messages["duplicate_username"])```
There are 2 things that determine whether a user can access the Django Admin:
User.is_staff1 must be set to True
The user will need the appropriate permissions for each model
There are multiple ways to set is_staff to True for all new users. The easiest would probably be to redefine is_staff = models.BooleanField(default=True) on your User model.
If you are not going to use per-model permissions you can also redefine is_superuser = models.BooleanField(default=True) which will give the user all permissions on every model.
Note though that the Django Admin is primarily designed to be used by your developers and site administrators who know what they are doing, and not most regular users.
I'm using a CustomUser in my model. This is the User Manager.
class UserManager(BaseUserManager):
def create_user(self, email, username, password=None, is_staff=False, is_superuser=False, is_active=False,
is_bot=False, is_mobile_verified=False, is_online=True, is_logged_in=True):
logger = logging.getLogger(__name__)
logger.info("REGULAR user created!")
if not email:
raise ValueError('Email is required')
if not username:
raise ValueError('Username is required.')
email = self.normalize_email(email)
user = self.model(email=email, username=username, is_staff=is_staff, is_superuser=is_superuser,
is_active=is_active, is_bot=is_bot, is_mobile_verified=is_mobile_verified,
is_online=is_online, is_logged_in=is_logged_in)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
logger = logging.getLogger(__name__)
logger.info("SUPER user created!")
return self.create_user(email, username, password=password, is_staff=True, is_superuser=True, is_active=True,
is_bot=False, is_mobile_verified=False, is_online=True, is_logged_in=True)
This is my definition of the custom user model.
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, max_length=255)
mobile = PhoneNumberField(null=True)
username = models.CharField(null=False, unique=True, max_length=255)
full_name = models.CharField(max_length=255, blank=True, null=True)
birthday = models.DateField(null=True)
gender = models.CharField(max_length=255, null=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
is_mobile_verified = models.BooleanField(default=False)
is_online = models.BooleanField(default=False)
is_logged_in = models.BooleanField(default=True)
is_bot = models.BooleanField(default=False)
location = models.ForeignKey(Location, on_delete=models.SET_NULL, null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
#objects = UserManager()
get_user_model().objects.create_user(...)
If I uncomment the line objects = UserManager() then I can run the server but the super users created from the admin backend can't log in.
If I use get_user_model() the code breaks and I get the following error
"AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL
django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'bouncer.User' that has not been installed
But in my settings.py I've define auth user model
AUTH_USER_MODEL = 'bouncer.User'
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
What am I doing wrong here?
For anyone reading this in 2020, I suspect the problem is dependency related. I ran into the same error as OP.
Your first two checks should be:
1 - Is the app in the installed apps list in your settings.py?
2 - Is the AUTH_USER_MODEL = "app_name_from_apps_py.model_name" set in settings.py?
This was as far as most of the other responses I read go.
What I didn't realise on reading the docs is that to use get_user_model() you need to have established your model first. Of course, right?!
So above, where OP is using get_user_model(), they are creating a circular dependency.
You cannot use get_user_model() within the class that creates this model.
That error looks like bouncer isn't in your INSTALLED_APPS.
So to clarify, you have to have
bouncer/models.py that contains the User model (or models/__init__.py which imports the model from another file)
'bouncer' in the INSTALLED_APPS list in the settings
AUTH_USER_MODEL = 'bouncer.User' (as you do).
In my case I accidentally pasted in Meta class of my custom user the attribute abstract = True. So it raises this error.
For me this was because I had myapp/models/__init__.py and I tried putting the custom User model definition in myapp/models/user.py, and setting AUTH_USER_MODEL = myapp.User. Moving the custom User model definition into myapp/models/__init__.py fixed it. I was unable to import it in __init__.py; I had to move the definition there.
Make sure that you register your model in admin.py and not in models.py
# admin.py
from django.contrib.auth.admin import UserAdmin
admin.site.register(YourUser, UserAdmin)
This solved the problem for me.
Never put custom user model and additional models containing links to user model in one models.py file in one app. This can be a matter of that error.
Bad idea leading to that error:
# customUserModel/models.py
from django.contrib.auth.models import AbstractUser
from django.conf import settings
class User(AbstractUser):
pass
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
some_field = models.CharField(max_length=25)
This is a good idea:
# customUserModel/models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
# additionalUserProfile/models.py
from django.conf import settings
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
some_field = models.CharField(max_length=25)
#settings.py
AUTH_USER_MODEL = 'customUserModel.User'
Try shifting the position of the bouncer app in your INSTALLED APPS, this worked for me.
For some reason the import from django.contrib.auth.backends import BaseBackend caused this problem. I suspect that BaseBackend tries to use the user model, but can't because it's defined further down the file. However if you define your model BEFORE that import then it'll work just fine.
I'm aware this is an old question but I struggled with this issue for two days before finding my mistake, which was failing to follow the models organization in the Django Models docs.
I would have just commented on #5fec's answer, but I don't have the reputation yet. My answer is similar to his, except importing the model in models/__init__.py worked for me. I had neglected the __init__.py file entirely.
If you have the AUTH_USER_MODEL = <app_name>.<user_model> correctly written, and you have your '<app_name>', in your INSTALLED_APPS list, but you're still getting this error, it's possible that your <custom_user> model (e.g. User) is in the wrong place.
It needs to be defined in either:
<app_name>.models.py
OR
<app_name>/models/<arbitrary_name>.py AND there is an <app_name>/models/__init__.py that contains the line from .<arbitrary_name> import <custom_user>
try importing from django.db import models only after importing from django.contrib.auth.models import AbstractUser
I am using Django 1.7.1 with Python 3.4. I created a custom user model and now I have a need for users to be able to update their details. What I need is that, when users go to the form to update their details, the form is pre-populated with their data i.e. username, email and so on. So far, the form is showing but not with the current user data.
I have the following code:
models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
... # Some code left out for brevity
class AuthUser(AbstractBaseUser, PermissionsMixin):
"""
A fully featured User model with admin-compliant permissions that uses
a full-length email field as the username.
Email and password are required. Other fields are optional.
"""
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'), max_length=254, 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 = AuthUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username'] # Not needed since it has been mentioned in USERNAME_FIELD and is required by default and cannot be listed in REQUIRED_FIELDS
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_absolute_url(self):
return "/users/%s/" % urlquote(self.username)
def __str__(self):
return self.username
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their username
return self.username
def email_user(self, subject, message, from_email=None):
"""
Sends an email to this User.
"""
send_mail(subject, message, from_email, [self.email])
forms.py
from django.contrib.auth.forms import UserChangeForm
from .models import AuthUser
class AuthUserChangeForm(UserChangeForm):
"""
A form for updating users. Includes all the fields on the user, but
replaces the password field with admin's password hash display field.
"""
password = ReadOnlyPasswordHashField(label="password",
help_text="""Raw passwords are not stored, so there is no way to see this
user's password, but you can change the password using <a href=\"password/\">
this form</a>""")
class Meta:
model = AuthUser
fields = ('username', 'email', 'password', 'is_active', 'is_staff', 'is_superuser', 'user_permissions')
widgets = {
'email': TextInput(),
}
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the field does
# not have access to the initial value
return self.initial["password"]
views.py
class UpdateUserView(LoginRequiredMixin, FormView):
template_name = 'users/update_user.html'
form_class = AuthUserChangeForm
# get current user object
def get_object(self, queryset=None):
return self.request.user
urls.py
url(r'^profile/update/', UpdateUserView.as_view(), name='update_profile'),
What I'm I missing?
FormView is not the appropriate base class here: it doesn't know about model forms and doesn't define a get_object method. Use UpdateView instead.