That's how I"ve extended User model:
class User(AbstractBaseUser):
email = models.EmailField(verbose_name='email',max_length=255,unique=True, db_index=True,)
username = models.CharField(verbose_name='username', max_length=255, unique=True)
first_name = models.CharField(verbose_name='first_name', max_length=255, blank=True)
last_name = models.CharField(verbose_name='last_name', max_length=255, blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
avatar = models.ImageField(upload_to='profile_images', blank=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['username']
def get_full_name(self):
return '%s %s' % (self.first_name, self.last_name,)
def get_short_name(self):
return self.username
def __unicode__(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
return self.is_admin
It works good , but after new user registration I can't log in with his username and password
and in my database I have now 2 tables with users
auth_user - old users
blog_user - new extended users
And I can log in only with users from auth_user.
That's what I have for login in views.py:
#csrf_protect
def loginn(request):
c = {}
c.update(csrf(request))
return render_to_response("login/login.html", c)
#csrf_protect
def auth_view(request):
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return render_to_response('login/loggedin.html',RequestContext(request))
else:
return HttpResponseRedirect('/posts/invalid')
So , how can I log in with user from my new , extended table ?
In order to allow a custom User model to be used within the authenticate() method, Django requires setting the AUTH_USER_MODEL setting.
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#substituting-a-custom-user-model
For your example code it would be AUTH_USER_MODEL = 'blog.User' within settings.py
The authenticate() method makes a call to get_user_model() which goes and finds if the AUTH_USER_MODEL setting is set, and then uses that as it's base for the model which to authenticate against.
Please be sure to read the documentation on substituting a custom user model within Django. There are a handful of caveats that you should be aware of before making a decision to switch the auth user model.
If it's possible, I would recommend just extending the base auth user model to include any application specific fields you would need because it appears you are still logging users in via Username.
https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-the-existing-user-model
Related
I have a custom user model in my Django project and when I create an instance of request.user in my views PyCharm doesn't seem to recognize the user instance correctly. The available methods/attributes suggested by PyCharm still point to the built-in Django user model but not to my new one.
Is there any way to set this up properly?
Example:
# settings.py
AUTH_USER_MODEL = 'user.UserProfile'
# models.py custom user model
class UserProfile(AbstractBaseUser, PermissionsMixin):
# Email and name of the user
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
# Privilege and security booleans
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
email_confirmed = models.BooleanField(default=False)
# Company on whose behalf the user acts on
company = models.ForeignKey('company.Company', on_delete=models.CASCADE, blank=True, null=True)
objects = UserProfileManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send mail to user - Copied from original class"""
send_mail(subject, message, from_email, [self.email], **kwargs)
def __str__(self):
return self.email
# views.py
def render_dashboard_benefits(request):
# Get current user instance
current_user = request.user
# Typing...
current_user.company
# Pycharm suggests 'first_name' and 'last_name' depending on the initial user model
# but not e.g. "email" or "company" according to the new user model
return render(request, 'test.html')
Re dudulus answer, this indeed works but raises:
current_user: UserProfile = request.user
so still I think this is an IDE bug?
You can use like this.
current_user: UserProfile = request.user
I have a problem with authenticate() method. It always returns None. I checked all my arguments, and they are not empty. In models, I have USERNAME_FIELD = 'username', in settings I have AUTH_USER_MODEL = 'account.User'. I really can't understand why it's not working.
models.py
class UserManager(BaseUserManager):
def _create(self, username, password):
if not username:
raise ValueError('Username cant be empty')
user = self.model(username=username)
user.set_password(raw_password=password)
user.save()
return user
def create_user(self, username, password):
return self._create(username=username, password=password)
class User(AbstractBaseUser):
id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='id')
email = models.EmailField()
username = models.CharField(max_length=25, unique=True)
is_active = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
education = models.CharField(max_length=100, blank=True)
date_of_birth = models.DateField(blank=True, null=True)
name = models.CharField(max_length=25, blank=True)
last_name = models.CharField(max_length=25, blank=True)
objects = UserManager()
USERNAME_FIELD = 'username'
views.py
class UserLoginView(ObtainAuthToken):
serializer_class = UserLoginSerializer
serializers.py
class UserLoginSerializer(serializers.Serializer):
username = serializers.CharField(required=True)
password = serializers.CharField(required=True)
def validate_username(self, username):
if not User.objects.filter(username=username).exists():
raise serializers.ValidationError('User wasn\'t found')
print(User.objects.get(username=username).password)
return username
def validate(self, data):
# authentications
request = self.context.get('request')
username = data.get('username') # test_name
password = data.get('password') # newpassword11
if username and password:
user = authenticate(username=username, password=password, request=request)
print(user)
if not user:
raise serializers.ValidationError('Invalid password')
else:
raise serializers.ValidationError('You have to type you\'re mail and password')
data['user'] = user
return data
I fixed it by changing the model to this:
class User(AbstractBaseUser, PermissionsMixin):
.....
after send post request to url routing to UserLoginView view with username and password at the post body got Json something like this at the response:
{"token":"30e0215a5f4cc521203a633452d63995d8f1fa77"}
When used AbstractBaseUser instead of AbstractUser be careful to implement all requirements of Django user model.
Tips: Do not forget to make the DRF Authentication settings correctly.
The problem was in users field, called is_active. Django allows to login only when user is active.
P.S. Thank to everyone, who tried to help :)
So i am pretty new to Django. Actually my first project.
I want to create a custom model "Logging" in which i want to log the admin login attempts and count the attempts. After 3 failed login attempts the user must me locked out. Ive already created a custom User model like this.
class UserManager(BaseUserManager):
def create_user(self,username,password,description):
if not username:
raise ValueError('Users must have an username ')
user = self.model(username=username)
user.set_password(password)
user.description = description
user.save(using=self._db)
return user
def create_staffuser(self, username, password, description):
user = self.create_user(
username,
password,
description
)
user.staff = True
user.save(using=self._db)
return user
def create_superuser(self, username, password, description):
user = self.model(username=username)
user.set_password(password)
user.description = description
user.staff = True
user.admin = True
user.save(using=self._db)
return user
class Users(AbstractBaseUser):
username = models.CharField(max_length=15,unique=True)
description = models.CharField(max_length=50,default="None")
is_active = models.BooleanField(default=True)
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False) # a superuser
# notice the absence of a "Password field", that is built in.
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['description']
def get_full_name(self):
return self.username
def get_short_name(self):
return self.username
def __str__(self):
return self.username
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
#property
def is_staff(self):
"Is the user a member of staff?"
return self.staff
#property
def is_admin(self):
"Is the user a admin member?"
return self.admin
objects = UserManager()
So how can i log custom admin attempts?
You can make signal receivers for the user_login_failed [Django-doc] and user_logged_in [Django-doc] signals. We can thus create a model that looks like:
from django.conf import settings
class FailedLogin(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
timestamp = models.DateTimeField(auto_now_add=True)
Then we define two signal receivers, one for a successful login that will remove FailedLogin records (if any):
from django.contrib.auth import get_user_model
from django.contrib.auth.signals import user_logged_in, user_login_failed
from django.dispatch import receiver
#receiver(user_logged_in)
def user_logged_recv(sender, request, user, **kwargs):
FailedLogin.objects.filter(user=user).delete()
#receiver(user_login_failed)
def user_login_failed_recv(sender, credentials, request):
User = get_user_model()
try:
u = User.objects.get(username=credentials.get('username'))
# there is a user with the given username
FailedLogin.objects.create(user=u)
if FailedLogin.objects.filter(user=u).count() >= 3:
# three tries or more, disactivate the user
u.is_active = False
u.save()
except User.DoesNotExist:
# user not found, we can not do anything
pass
When trying to authenticate a user created through a view in DRF Browsable API, I get
No active account found with the given credentials
The view:
class MyUserCreate(APIView):
def post(self, request, format='json'):
serializer = MyUserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The serializer:
class MyUserSerializer(serializers.ModelSerializer):
username = serializers.CharField(
required=True,
validators=[UniqueValidator(queryset=MyUser.objects.all())],
min_length=5,
max_length=20
),
password = serializers.CharField(
required=True,
max_length=256
)
class Meta:
model = MyUser
fields = ('username', 'password')
def create(self, validated_data):
password = make_password(validated_data['password'])
user = MyUser.objects.create_user(validated_data['username'], password)
return user
The password is being hashed. At this stage, went on to the admin page and tested to login there too with the created account and got
Made sure the custom user model had is_active, is_superuser and is_staff and checked if that would fix the issue but it didn't.
class MyUserManager(BaseUserManager):
def create_user(self, username, password, **extra_fields):
user = self.model(
username=username
)
user.is_staff=True
user.is_active=True
user.is_superuser=True
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self.create_user(username, password, **extra_fields)
class MyUser(AbstractBaseUser):
objects = MyUserManager()
class Meta:
# managed = False
db_table = 'user_entity'
user_id = models.AutoField(primary_key=True, db_column='userId')
username = models.CharField(db_column='username', unique=True, max_length=20)
password = models.CharField(db_column='userPassword', max_length=256)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=True)
USERNAME_FIELD = 'username'
def __str__(self):
return str(self.user_id) + " (%s)" % str(self.username)
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
Then, tried to create the user using python manage.py createsuperuser... doing so then I'm able to both login in the admin pannel and authenticate that user with the Token Obtain Pair view shown initially.
First of all. Thank you for providing all the details. It is always easier to debug. Coming to the issue, the problem is that you are using make_password explicitly.
If you would look through the Django's set_password documentation, you'd find that it takes care of hashing.
What you are doing is, first you are hashing your password, and providing it as a raw value to set_password method via create_user. The set_password would assume that as a raw value and would hash it again. Hence, your original password is not used at all.
You can just remove the use of make_password and instead change your serializer's create method to
class MyUserSerializer(serializers.ModelSerializer):
...
def create(self, validated_data):
user = MyUser.objects.create_user(validated_data['username'], validated_data["password"])
return user
This should resolve your query. I hope it helps! :)
I have custom model where login details have been stored:
class UserRegistration(models.Model):
# Auto updated when data is inserted
created_at = models.DateTimeField(auto_now_add=True, auto_now=False)
# Auto updated when data is altered
updated_at = models.DateTimeField(auto_now_add=False, auto_now=True)
username = models.CharField(max_length=255, null=True)
password = models.CharField(max_length=255, null=True)
first_name = models.CharField(max_length=255, null=True)
last_name = models.CharField(max_length=255, null=True)
def __str__(self):
return self.first_name
urls.py is
url(r'^login/$', view.login_page, name='login_page'),
views.py is
def login_page(request):
if request.method == 'GET':
return render(request, "login_page.html")
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
print username, password
credentials_matches = False # considering credentials are wrong
all_users = UserRegistration.objects.all()
for user in all_users:
if user.username == username and user.password == password:
credentials_matches = True
break
if credentials_matches:
return HttpResponse('success')
# return redirect("http://127.0.0.1:8000/lmtech")
else:
return HttpResponse('fail')
I have other views that I do not wish to show to user if they have not logged in. I though about #login_required() but in this case, user details are in custom model. How do I solve this problem?
You must not make a custom user model like this. Writing your own authentication system is always insecure.
You should use the standard built-in auth framework - you can substitute your own user model but it must inherit from AbstractBaseUser, see the docs. However there doesn't seem to be much reason to do this as you don't add any custom functionality.
On your custom model, you want the first name, last, name, username and password, in User model from django you only have the email more which is optional. So what you can do is to créate a User with the django's model and then use a relationship to have the two other attributes you want.
Here is an example
class User(models.Model):
username = models.CharField()
password
...
class UserMore(models.Model):
user = models.OneToOneField(User)
created_at = ...
updated_at = ...
def __str__(self):
return self.user.username
In this case you then can use the #login_required() of django.
Personnaly, it's how I do.
But if you really want to keep your custom model without the User model of Django, maybe you can keep the user in a session that you initiate in your login view and then in your logout you delete it. In this way you can easily check if the request.session['username'] is defined or not.