Django: Check user name from two different models - django

I have two models: Faculty and Student. Both have a username and password.
I want to check if the username and password input provided is present in either of them. In the view, I can do a simple check on one model like this:
try:
user = Student.objects.get(username=username, password=password)
except Student.DoesNotExist:
error_message = "**Incorrect login. Please try again."
context = {'error_message' : error_message}
return render_to_response('myapp/login.html',context, context_instance=RequestContext(request))
Can you point out how to do the same for both Faculty and Student at the same time?

The simplest way is probably just to try the second query if the first fails:
context = {}
try:
user = Student.objects.get(username=username, password=password)
except Student.DoesNotExist:
try:
user = Faculty.objects.get(username=username, password=password)
except Faculty.DoesNotExist:
error_message = "**Incorrect login. Please try again."
context['error_message'] = error_message
# rest of your view here
return render_to_response('myapp/login.html', context, context_instance=RequestContext(request))
By the way, you can save yourself some effort if you use django.shortcuts.render instead of render_to_response:
from django.shortcuts import render
...
return render(request, 'myapp/login.html', context)

I suggest, you should use custom user model to join your models and classic django auth utils.

Related

accessing a different user's information in django

I know to access the logged-in user's data we use request.user. My goal is to list all the users in the table and have a link to their profile page. How do I make the link go to the user's profile page?
I have the following:
# app/views.py
def tutors_list(request):
user_list = CustomUser.objects.all()
context = {
'user_list': user_list
}
return render(request, 'tutors_list.html', context)
def show_profile(request, username):
user = CustomUser.objects.get(username = username) ### NOT DISPLAYING RIGHT USER
#user = CustomUser.objects.get(id=id)
context = {
'user': user
}
return render(request, 'show_profile.html', context)
# myproject/urls.py
url_patterns = [
# ...
path('show_profile/', views.show_profile, name='show_profile'),
# ...
I'm getting an error saying show_profile is expecting 1 more argument, username. How exactly would this model work if I need to pull data for a specific user in the database and not the logged-in user?
As your error says show_profile is expecting 1 more argument, username, so you need to pass username in your url pattern:
path('<str:username>/show_profile/', views.show_profile, name='show_profile'),

Form validation on insert vs. update

I am trying to make one form for both inserting and updating data. I have read these:
Model validation on update in django
django exclude self from queryset for validation
In my project, however, I am not using ModelForm.
forms.py:
This is the form the user sees when registering his/her username and first_name. It is also the form an existing user sees when trying to change his/her username and/or first_name.
from django import forms
from .models import User
class SettingsForm(forms.Form):
username = forms.CharField(max_length=16)
first_name = forms.CharField(max_length=32)
# ... and many more form fields
def clean_slug(self):
"""Make sure that the username entered by the user will be unique in the database"""
username = self.cleaned_data['username']
try:
product = User.objects.get(username=username)
except User.DoesNotExist:
# Good, there is no one using this username
pass
else:
# There is alreaady a user with this username
raise forms.ValidationError('This username has been used. Try another.')
return username
The form cleaning works as intended for inserting data. However, when updating data, it complains that the username has been used (naturally, since the username already exists in the database).
How do I update the data without raising ValidationError when using a Form (and without using ModelForm)?
(The reasons for not using ModelForm in this case are: we may stop using the the orm, SettingsForm may contain a mix of fields from different models, some fields may be repeated hundreds of times in the form that is displayed to the user, we also need custom fields not tied to any model, ... and other scenarios that make using ModelForm quite challenging (impossible?). The purpose of this question is to find out if there are ways of achieving the desired result without using ModelForm.)
You have three cases:
The user is new
The user exists and doesn't change his/her username
The user exists and changes his/her username
You need to check if the username already exists only in the first two cases.
If it's an existing user you should pass the User instance to the form so you can use it in the clean_slug function, let's say in self.user variable.
Then you could just add two lines in the clean_slug function and it should work as you wish:
def clean_slug(self):
"""Make sure that the username entered by the user will be unique in the database"""
username = self.cleaned_data['username']
# If the user exists and the username has not been changed,
# just return the username
if self.user and self.user.username == username:
return username
try:
product = User.objects.get(username=username)
except User.DoesNotExist:
# Good, there is no one using this username
pass
else:
# There is alreaady a user with this username
raise forms.ValidationError('This username has been used. Try another.')
return username
The ValidationError is obviously because you're instantiating the SettingsForm when the username already exists, as you've already stated.
So if you want to add a form that can do double-duty, I would add an __init__ to SettingsForm that takes an is_update and saves it as a member variable, like so...
def __init__(self, is_update=False, **kwargs):
self.is_update = is_update
return super(SettingsForm, self).__init__(**kwargs)
then modify your clean_slug() to be:
def clean_slug(self):
username = self.cleaned_data['username']
try:
product = User.objects.get(username=username)
except User.DoesNotExist:
# Good, there is no one using this username
pass
else:
if not self.is_update: # for 'create' usage
# There is already a user with this username
raise forms.ValidationError('This username has been used. Try another.')
else: # for 'update' usage
pass
return username
You actually want your form to do two different things depending if it is a create or an update.
So either have two forms with a different clean_slug method or pass in an argument telling the form if it is an update or a create (there is another answer from neomanic showing this way).
Personally I think the easiest way would be to subclass your form and change the clean slug method. The use the new form for updates and your original form for creates.
class UpdateSettingsForm(settingsForm):
def clean_slug(self):
username = self.cleaned_data['username']
return username

Show recaptcha after 3 wrong attemps and process it in Django?

I am using Class-based view with Django: I want to show the captcha only after 3 times and process captcha only after it is shown. Till now, I can only show the captcha after one wrong attempt:
def post(self, request):
response = captcha.submit(
request.POST.get('recaptcha_challenge_field'),
request.POST.get('recaptcha_response_field'),
'[[ MY PRIVATE KEY ]]',
request.META['REMOTE_ADDR'],)
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
state = "The username or password is incorrect."
if user is not None:
login(request, user)
return HttpResponseRedirect('/index/')
else:
#captcha = CaptchaField()
public_key = settings.RECAPTCHA_PUBLIC_KEY
script = displayhtml(public_key=public_key)
return render_to_response('index.html', {'state':state, 'captcha':'captcha', 'script':script}, context_instance=RequestContext(request))
I want to show the captcha after three times and process it using response.is_valid. How can I do this?
The simplest solution is probably to store the number of attempts in a cookie, incrementing it on each failed attempt. Obviously this can be tampered with, which brings me to signed cookies:
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.get_signed_cookie
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpResponse.set_signed_cookie
Essentially a signed cookie will raise an exception if the value has been tampered with.
You can also store the counter in the session, but I don't see any point in prematurely creating a session object when a cookie will do the trick.

pass data from database in httpresponse redirect

Am new to django framework,am just trying to create seperate login form other than administrator login.I need to authenticate a user using username and password, fetch the details of that user from database and pass that data to a templae (home page) to display it.
My code is as follows:-
view.py:-
def login_user(request):
user = ''
passw = ''
username1 = ''
if request.POST:
user = request.POST.get('username')
passw = request.POST.get('password')
#password1 = ''
try:
userdata = Employee.objects.get(username = user, password = passw)
user_id = request.session["user_id"] = userdata.id
employee_details = Employee.objects.get(id=user_id)
request.session['user_id'] = employee_details.id
return HttpResponseRedirect('/home/', kwargs={'user_id': employee_details.id}))
except Employee.DoesNotExist:
state = "Username or password incorrect !"
return render_to_response('login.html',
{'username' : username1,'state' : state},
context_instance = RequestContext(request))
else:
state = "Please login here:"
return render_to_response('login.html' , {'state' : state} ,
context_instance = RequestContext(request))
i tried using kwargs={'user_id': employee_details.id} but it is not working.How can i pass datas to home page after redirecting?
Thanks
You can not redirect user using POST request. Use GET parameter instead.
In your case the requested data is already stored in session. Just try to read from the session on next request. Django contrib.auth uses user.is_authenticated method for the job.
i tried using kwargs={'user_id': employee_details.id} but it is not working.How can i pass datas to home page after redirecting?
You can:
use session variables
use "get" variables (pass variables encoded in the URL like '/home/?a=1&b=2', see urllib.urlencode)
However you should be using the standard Django user related functions and methods. See "How to log a user in". TLDR:
check credentials: user = authenticate(username=username, password=password)
test if user is not None and any other tests you want (is he active, has some privilege, etc)
mark the request.user as authenticated: login(request, user)

Django design question: extending User to make users that can't log in

The site I'm working on involves teachers creating student objects. The teacher can choose to make it possible for a student to log into the site (to check calendars, etc) OR the teacher can choose to use the student object only for record keeping and not allow the student to log in. In the student creation form, if the teacher supplies a username and a password, it should create an object of the first kind - one that can log in, i.e. a regular User object. If the teacher does not supply a username/password, it should create the second type. The other requirement is that the teacher should be able to go in later and change a non-logging-in student to the other kind. What's the best way to design for this scenario? Subclass User and make username and password not required? What else would this affect?
Edit:
I ended up using User.set_unusable_password(). Here's the code - I've left out other forms, etc, that I'm also using in my view:
Form
class StudentForm(forms.ModelForm):
username = forms.RegexField(regex=r'^\w+$',
required=False,
max_length=30,
label=("Username"),
error_messages={ 'invalid': ("This value must contain only letters, numbers and underscores.") })
password = forms.CharField(widget=forms.PasswordInput(),
label="Password", required=False)
class Meta:
model = User
fields = ('first_name', 'last_name', 'username', 'email', 'password')
Note that username and password are not required in the form.
View
def create_student(request):
if request.method == "POST":
student_form = StudentForm(request.POST)
if student_form.is_valid():
user = student_form.save(commit=False)
if student_form.cleaned_data['username'] == '':
user.username = generate_random_username()
user.set_unusable_password()
else:
user.set_password(user.password)
user.save()
return HttpResponseRedirect(reverse('student_list', args=['active']))
#GET an empty form
else:
student_form = StudentForm()
return render_to_response('priviostudio/create_student.html', {
'student_form': student_form,
})
And in the view to edit a student (which will probably be combined with the create_student view) I have this for GET:
student_form_initial = {
'username': user_instance.username if user_instance.has_usable_password() else '',
'password': user_instance.password if user_instance.has_usable_password() else '',
}
student_form = StudentForm(instance=user_instance, initial=student_form_initial)
And in POST, if the teacher submits a new username and valid password, I'll just set those on the User instance.
Thanks for the ideas everyone.
The auth app's User model has a set_unusable_password method; this probably does what you want without requiring the model to be extended.
The Django default User model has an is_active field.
http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.models.User.is_active
you probably want to use that.
That way when a teacher decides they want the user to be able to log in, your code would just set the student's user to is_active=True and vice versa.
Also according to the documentation link above Django's default authentication form and permission methods check the is_active flag, so that's some of the work already done for you.
You'd probably need to generate a username for the student as well, but you can easily do that using a slug from the name if that is provided.
The most obvious way for me to differentiate students from teachers would be groups really and Django provides mechanisms for that as well.
You might consider making all the students one kind of object - not User - and then supplementing that model with user objects where the teacher has enabled the student to log in. The composition of these two model objects would solve you want fairly cleanly.
I woud avoid subclassing User. Instead, you might want to create a custom authenticator that allows you to check group membership for login capability.
"""
DummyBackend.py
"""
from django.contrib.auth.models import User, check_password
from django.contrib.auth.backends import RemoteUserBackend
from lecture_feedback.daily.models import Student
class DummyBackend(RemoteUserBackend):
"""
Dummy authentication module that takes a username and password. The password must match the username.
"""
def authenticate(self, username=None, password=None):
"""
The username passed as ``remote_user`` is considered trusted. This
method simply returns the ``User`` object with the given username,
creating a new ``User`` object if ``create_unknown_user`` is ``True``.
Returns None if ``create_unknown_user`` is ``False`` and a ``User``
object with the given username is not found in the database.
"""
try:
student = Student.objects.get(globalid=username)
except Student.DoesNotExist:
return None
if username != password:
return
user = None
# Note that this could be accomplished in one try-except clause, but
# instead we use get_or_create when creating unknown users since it has
# built-in safeguards for multiple threads.
if self.create_unknown_user:
user, created = User.objects.get_or_create(username=username)
if created:
user = self.configure_user(user)
else:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
pass
return user
def configure_user(self, user):
"""
Configures a user after creation and returns the updated user.
By default, returns the user unmodified.
"""
student = Student.objects.get(globalid=user.username)
user.first_name = student.first_name
user.last_name = student.last_name
return user
The Student model could contain a field that indicates if the student is allowed to log in.
Also take a look at http://docs.djangoproject.com/en/dev/howto/auth-remote-user/#attributes.