Working on making some unittests with Django, and trying to make some testing with the login process with the login form.
I am using modified User model just to make the email field unique; otherwise nothing drastically different.
account/views.py
def post(self, request):
# Retrieve the username and password
username = request.POST['username']
password = request.POST['password']
# Create a user object from authentication, or return None
user = authenticate(username=username, password=password)
# Check if user was created
if user is not None:
# Rest of the code, irrelevant...
account/test_views.py
from account.models import User as CustomUser
# Code snippit
def test_account_login_POST_successful_login(self):
# Create a user to test login
CustomUser.objects.create_user(
username='test_user',
email='test_user#intranet.com',
password='flibble'
)
response = self.client.post(self.login_url, {
'username': 'test_user',
'password': 'flibble'
})
self.assertEqual(response.status_code, 301)
account/models.py
class User(AbstractUser):
# Make the email field unique
email = models.EmailField(unique=True)
project/settings.py
# Authentication
AUTH_USER_MODEL = 'account.User'
Funny thing is that login works normally on the web app, but when testing it always returns None.
I've tried to check_password() with the created user, and it returns true in both the test method and the view method.
I've also tried putting in AUTHENTICATION_BACKEND = ['django.contrib.auth.backends.ModelBackend'], but no go.
I faced with the same problem. The problem was in is_active model field. In my case this field was False by default, so authenticate() returns None all the time.
Related
I am recently trying to develop a website using Django in which a person can make his/her registration in the system but he/she should not be allowed to log in to the system until the admin approves the request. I tried the user.is_active field but the only thing it does is to prevent the new user to have access as admin. In other words, the system gives him/her permission to log in the system. My code is as follows:
# create a new user but he will be inactive until admin's approval
user = User.objects.create_user(username=username, password=password)
user.is_active = False
After changing the is_active field of the user to False I try to log in the system using the new user's username and password and the system lets me in. This is my code for the login:
def login(request):
context = RequestContext(request)
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = auth.authenticate(username = username, password = password)
if user is not None and request.user.is_active:
auth.login(request, user)
print 'login'
print request.user.username
print request.user.is_active
return redirect('app.views.results')
else:
print 'no login'
return render(request, 'login.html')
When I print the user's username and is_active field, I see that the "is_active" field is True instead of False. Is something wrong with the code or am I missing something?
Any better approaches to solve the problem will also be welcome and appreciated.
After you perform value assignment for model objects' attributes, you should call object.save(), as so:
user = User.objects.create_user(username=username, password=password)
user.is_active = False
user.save()
This will save the changes you've introduced to the object.
From Django docs
This will be an easy question for someone out there. I searched around quite a bit and if there's a thread that addresses this perfectly, please direct me and close this out. I'm building a very simple web form within our organization's website using Django.
In forms.py I've got a class for the login page:
class userLogin(forms.Form):
user = forms.CharField(label = 'Username ', max_length = 25)
pwd = forms.CharField(label = 'Password ', widget = forms.PasswordInput)
def clean(self):
cleaned_data = super(userLogin, self).clean()
user = cleaned_data.get("user")
pwd = cleaned_data.get("pwd")
if not fs.authenticateUser(user, pwd):
raise forms.ValidationError("Invalid password!")
I have to authenticate against our website's API, which is what the fs.authenticate bit is about; it just returns True or False. In views.py I do this:
if request.method == 'POST':
user_form = userLogin(data = request.POST)
if user_form.is_valid():
return redirect('PickupAuthorization/main')
else:
pass
The authentication and redirection to the main page works splendidly, and the login form throws an invalid password message as expected when the credential is incorrect. What I want to know is how to make "user" available to views.py. If I try to reference user_form.user in the view, Python tells me that user is not an attribute of userLogin!
I haven't done much with OOP, so I'm imagining that this has a simple answer, but I can't freakin' find it. Thanks!
EDIT: I have had to shelve this project for the moment in favor of more pressing matters, but I will update it when I have a solution that works properly.
You need to use authenticate method provided by Django in order to get the user. The authenticate method returns a user if it finds one.
To authenticate a given username and password, use authenticate(). It takes two keyword arguments, username and password, and it returns a User object if the password is valid for the given username. If the password is invalid, authenticate() returns None:
Generally the flow using authenticate goes like this:
forms.py
class userLogin(forms.Form):
user = forms.CharField(label = 'Username ', max_length = 25)
pwd = forms.CharField(label = 'Password ', widget = forms.PasswordInput)
def __init__(self, *args, **kwargs):
self.user_cache = None # You need to make the user as None initially
super(LoginForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(userLogin, self).clean()
user = cleaned_data.get("user")
pwd = cleaned_data.get("pwd")
# Now you get the user
self.user_cache = authenticate(username=user, password=pwd)
# Do other stuff
return self.cleaned_data
# Function to return user in views
def get_user(self):
return self.user_cache
Views.py
if request.method == 'POST':
user_form = userLogin(data = request.POST)
# You can now get user using the get_user method
user = user_form.get_user()
# Do other stuff
Another thing I'd like to add is if your user has already logged in you can simply do request.user.username in your views.py to get the current user.
I'm using django's authentication to login users. But I have two models from where authenticate method would check the user credentials. One is ApplicationUser and the other is SystemUser I have made one of them and it works fine like so:
models.py
class UserManager(BaseUserManager):
def create_user(self, email, password=None):
"""
Creates and saves a User with the given username and password.
"""
....
return user
def create_superuser(self, email, password):
...
return user
class ApplicationUser(AbstractBaseUser):
application_user_id = models.AutoField(primary_key=True)
....
....
views.py
def login_view(request):
...
user = authenticate(username = email, password = password)
if user is not None:
...
login(request, user)
....
....
I came through this problem and got here but I couldn't work out a solution to this.
My Questions:
How do I specify two AUTH_USER_MODEL, as of yet I have set
ApplicationUser as AUTH_USER_MODEL.
And even if I somehow specify
the two AUTH_USER_MODEL, how do the authenticate or login function
know where (ApplicationUser or SystemUser) to match the credentials and create session for the user
accordingly
In your settings.py set
AUTH_USER_MODEL = 'yourapp.YouCustomUserModel'
Then let's django do the rest.
If you want to have others User Model, you need to extend them from your selected AUTH_USER_MODEL
I am using django's inbuilt authentication system. Everything seems to be working fine. There are two fields that the user is requested to input at the time of signup: username and email. While logging in they are required to enter username and password.
I'd like to change this behavior so that username field is gone. I want to treat the email as the users username. So while signing in user will be required to put email / password
Is this possible while still using django's inbuilt auth system? I'm on django 1.7
Update
I had the need to add additional fields so I added the following to models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
telephone_number = models.CharField(max_length=100)
website_url = models.CharField(max_length=100)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
The answer is directly in django documentation, in short words you should subclass AbstractBaseUser or AbstractUser (in second case you can't totally remove username field), create your own user manager based on BaseUserManager or UserManager and customize built-in auth forms if you're using it (or any app that you're using is using it).
This is not strictly cannon, but to avoid creating a new User class or Auth backend, I tend to let users log in with both username or email.
Anyways you'll want to ensure emails are unique as django does not check this by default.
You'll then have to override the default login view to support this. You can create something along the lines of:
class EmailUsernameLoginView(View):
def post(self, request):
next = request.POST.get('next', None)
username = request.POST.get('username', None)
password = request.POST.get('password', None)
error = ''
if username and password:
try:
usr = User.objects.get(email=username)
username = usr.username
except User.DoesNotExist:
pass # If the user doesn't exist, it's an username
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect(next)
else:
error = 'Your account is not active'
else:
error = 'The username / email - password comb is wrong'
else:
error = 'Please provide a username / email and password'
ctx = {'error': error} # Fill with needed feedback
return render(request, ctx, 'registration/login.html')
This is just a draft and you should probably include a form to help with validation / cleaning
I'm creating a registration form for my site.
I'm going with just the standard field entries of username, email, password in my User object.
The database tables are already created.
Models.py
class RegistrationForm(ModelForm):
class Meta:
model = User
views.py
def Registration(request):
RegForm = RegistrationForm(request.POST or None)
if request.method == 'POST':
if RegForm.is_valid():
newUser = User.objects.create_user(username, email, password)
RegForm.save()
try:
return HttpResponseRedirect('/Newuser/?userNm=' + clearUserName)
except:
raise ValidationError(('Invalid request'), code='300') ## [ TODO ]: add a custom error page here.
My question is, how do I represent these fields:
username, email, password
in this line (from the view):
newUser = User.objects.create_user(username, email, password)
that are part of the RegistrationForm() in models.py ?
Additionally: How do I properly represent this default model (User) in the modelform in forms.py?
To answer your second question:
Use UserCreationForm to represent registration process for User class, it handles validation and saving for you and you can always extend it (i.e. to add email field)
User class has a lot of related forms, depending of the use case: authentication, registration, password change etc. read the docs on authentication to find more.
https://docs.djangoproject.com/en/1.5/topics/auth/default/#module-django.contrib.auth.forms
You can get the validated data from the form using cleaned_date:
username = form.cleaned_data['username']
email = form.cleaned_data['email']
password = form.cleaned_data['password']
newUser = User.objects.create_user(username, email, password)
Now I think your approach has serious problems since you are saving the form again after creating the user object, also the User object expects the password field to be a hash of the password and some meta data, etc.
I highly recommend you use django-registration (or at least have a look at it) which handles all this stuff for you in addition to email validation, all you need is to provide the templates(and also you can find some templates for it like this )