I have created an app that allows users to create online products - basically a structured form fill with permissions. I'm using Django's basic auth for sign up and login. When a user signs up I want the account to be approved by an admin before they can log in. I know there are modules I can use to do this, but I wondered if there is something I can do to enable this without installing more stuff.
Here's the signup view:
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=username, password=raw_password)
login(request, user)
return redirect('home')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
If it's not that simple and the best way is to use a module, which one is the best? I've seen a couple but am not sure which one to use, if I have to use one.
If you don't want to install third party module, I think you should extend Django's user model.
This is an official Django documentation about this subject.
https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#extending-the-existing-user-model
Design to record approve status on extended models. When the user logs in, it is likely that the approval status of the extended model is checked together with the Django login result, and the user is allowed to log in or not.
If you want to be notified about new users, you might want to use Django's "post_save" signal feature.
https://docs.djangoproject.com/en/1.11/ref/signals/
Related
I am trying as hard as I can to learn to concept of authentication within the Django framework. I am reading the documentation and trying to code similar which will help me get the subject. At the moment I am trying to implement a simple login which redirects to a page. If the user is logged in he should see a message other wise he should see a different message. This is my code.
I have made a simple login form
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput())
this is my login view
def login_view(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect('/users/success/')
else:
return redirect('/users/success/')
else:
form = LoginForm()
return render(request, 'testauth/login.html', {'form': form})
(I know the above code is a little bit redundant ... at the moment this is not so important)
and this is the success view
def success_view(request):
print(request.user)
if request.user.is_authenticated:
return HttpResponse("logged in")
else:
return HttpResponse("you are not logged in")
The problem is, I always get the "logged in" message even with users which do not exist. I tried restarting the server my cache is disabled ... I have no idea why is this happening. Why is this happening?
(p.s. I do not want to set in the settings.py a login success url. I want to do everything manually because I am struggling a lot with this topic and I want to make sure that I truly understand everything)
This is my views.py
I've created user manually. Not by using django form.
def login(request):
if request.method == 'POST':
username = request.POST['username'],
password = request.POST['password']
user = auth.authenticate(request, username=username, password=password)
if User is not None:
auth.login(request, user)
return redirect('/')
else:
messages.info(request, 'Invalid Credentials!')
return redirect('login')
else:
return render(request, 'login.html'
If you're trying to set up authentication using the built-in User object or a custom User model that is identical to the built-in, I highly recommend you use Django's built-in class views to handle your authentication (logging in and out).
It requires much less setting up in your project and is super simple once you get familiar with using class views (as you should use class views for most generic things such as list and detail views).
https://docs.djangoproject.com/en/3.1/topics/auth/default/#django.contrib.auth.views.LoginView
A client requires the ability for managers to add users to a company (with a random one time password) where the user must change their password before accessing anything. I am developing the app in Django 2.2
I made a custom user, replacing username with an email address and I added a change_password bool flag to the user. My change_password form/function works properly, but redirecting does not.
urls.py
path('change-password/', views.change_password, name='change-password'),
views.py
class Login(LoginView):
def form_valid(self, form):
# form is valid (= correct password), now check if user requires to set own password
if form.get_user().change_password:
return HttpResponseRedirect(reverse('change-password'))
else:
auth_login(self.request, form.get_user())
return HttpResponseRedirect(self.get_success_url())
def change_password(request):
if request.method == 'POST':
form = PasswordChangeForm(data=request.POST, user=request.user)
if form.is_valid():
form.save()
request.user.change_password = False
request.user.save()
update_session_auth_hash(request, request.user)
return redirect(reverse('user_view'))
else:
return redirect(reverse('change-password'))
else:
form = PasswordChangeForm(user=request.user)
args = {'form': form}
return render(request, 'users/change_password.html', args)
The expected behavior is to redirect to change-password if the change_password flag is True, however, while the app does redirect to change-password, upon Submission the following error is thrown:
NotImplementedError: Django doesn't provide a DB representation for AnonymousUser.
If I add the decorator #login_required to my change_password function this error goes away, however, I am redirected back to the login page with the URL: users/login/?next=/users/change-password/
The problem is that in form_valid method you are calling form.get_user() which authenticates/gets the user and checks for the change_password correctly, but it does not log the user in, meaning that the user making the requests is still anonymous to the system. So while the user gets redirected they are not authenticated, which means that the request.user objects is of type AnonymousUser which does not live in the database hence the Django doesn't provide a DB representation for AnonymousUser error.
And when you use the #login_required decorator the user gets redirected to the login page because it is not a logged in user and the decorator requires the user to be logged in to see the view it is decorating.
The URL that you see users/login/?next=/users/change-password/ is basically how the login_required decorator works and it is doing two things:
1. redirect anonymous user to the login page (the users/login part of the URL)
2. once they have successfully logged in redirect them back from where they came from (?next=/users/change-password/)
My suggestion is that you pass the username of the user that tried to log in but has to change their password to the change_password view and have a form waiting for the user there that asks for the current password, new one and a confirmation of the new password. It is the simplest way to do what you want to do, but you will have to confirm that the users current password is correct again though.
Sorry for the confusing first answer, I didn't read the question right the first time, hopefully this makes more sense :)
Currently I have different Signup and Login Views both implemented as class based view, and are used at diffrent urls. Both use the same form which has two fields email and password.
These two views have different form_valid and thus different logic. So, signup view creates a user, sends verification mail etc. Login view only logs the user in.
The use case has changed to allow both signup and login at the same url using a single form.
I want to have the same view, handle both these conditions. So, when form is submitted, I will check if a user exists in db with the submitted email. If yes then use LoginView logic. If not then use SignupView logic. How can I handle both these conditions in a single view by reusing SignupView and LoginView.
In your case you can use one Form and one view.
This form check if email exist, if it exist, raise a ValidationsError
class LoginOrSignupForm(forms.ModelForm):
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput(render_value=False))
def clean_email(self):
existing = User.objects.filter(email__iexact=self.cleaned_data['email'], username__iexact=self.cleaned_data['email'])
if existing.exists():
raise forms.ValidationError(_("A user with this e-mail already exists."))
return self.cleaned_data['email']
And then in your view
if form.is_valid():
#signup
elif form['email'].errors and form['email'].errors[0] == _("A user with this e-mail already exists."):
#login
user = auth.authenticate(username=form['email'].value(), password=form['password'].value())
auth.login(request, user)
This solution work in function based view. So for your case you want to use CBV, we can just override form_invalid methods in your CBV:
def form_invalid(self, form):
if form['email'].errors and form['email'].errors[0] == _("A user with this e-mail already exists."):
user = auth.authenticate(username=form['email'].value(), password=form['password'].value())
auth.login(request, user)
return HttpRedirect('your page')
return super(YouViewName, self).form_invalid(form)
For the form_valid we don't need to override it, just keep in mind that if your form is valid, it should register a new user. In this case a django.views.generic.CreateView can help.
I am using django model forms, the form can be filled even by users who have not signed up, but the submission requires the user being signed up.
Here is my models:
class Study(model.Model):
marksobtained = models.CharField(max_length=5)
highestmarks = models.CharField(max_length=5)
teacher = models.CharField(max_length=300)
class StudyForm():
some customisation stuff.
and then the views.py
form = StudiesForm(request.POST or None,
instance=id and Studies.objects.get(id=id))
if form.is_valid():
form.save()
return render(request, 'calculate.html', {'detail': ret_dict, 'amt': amt})
else:
return render(request, 'forms.html', {'form':form})
else:
return render(request, 'forms.html', {'form':form})
Donot bother about the indentation and other stuff in views, this is just a model of what i am trying to do, as can be seen any anonymous user can submit the form as of now, i want it to further modify, as when an anonymous user submits the form, he should first be signed up and then his data should be added to the models.
How can this be implemented?
If the user is not authenticated then save the form data to session.
Then log the user in the system.
Then pull the form data out of the session and save the information taking the authenticated users information.
Make user FK not required. Save model.
If request.user.is_authenticated() get him a cookie with id of created model. Redirect him on login page.
For each user check if there is a cookie with model id, attach user to model, save.