My email change form for users works, but I feel like my code is not written correctly. If I did it the way I have done below, I'd need a thousand else statements so that the page would return a response. Can someone tell me how I can make this more efficient/better? I'm not sure of the conventional way to do this
Views.py
def email_change(request):
form = Email_Change_Form()
if request.method=='POST':
form = Email_Change_Form(request.POST)
if form.is_valid():
if request.user.is_authenticated:
if form.cleaned_data['email1'] == form.cleaned_data['email2']:
user = request.user
u = User.objects.get(username=user)
# get the proper user
u.email = form.cleaned_data['email1']
u.save()
return HttpResponseRedirect("/accounts/profile/")
else:
return render_to_response("email_change.html", {'form':form}, context_instance=RequestContext(request))
I would suggest a complete change on how you looked at this. In my opinion, you should have all the implementation on the form side.
forms.py
I've implemented a class based on the SetPasswordForm that is more complete:
class EmailChangeForm(forms.Form):
"""
A form that lets a user change set their email while checking for a change in the
e-mail.
"""
error_messages = {
'email_mismatch': _("The two email addresses fields didn't match."),
'not_changed': _("The email address is the same as the one already defined."),
}
new_email1 = forms.EmailField(
label=_("New email address"),
widget=forms.EmailInput,
)
new_email2 = forms.EmailField(
label=_("New email address confirmation"),
widget=forms.EmailInput,
)
def __init__(self, user, *args, **kwargs):
self.user = user
super(EmailChangeForm, self).__init__(*args, **kwargs)
def clean_new_email1(self):
old_email = self.user.email
new_email1 = self.cleaned_data.get('new_email1')
if new_email1 and old_email:
if new_email1 == old_email:
raise forms.ValidationError(
self.error_messages['not_changed'],
code='not_changed',
)
return new_email1
def clean_new_email2(self):
new_email1 = self.cleaned_data.get('new_email1')
new_email2 = self.cleaned_data.get('new_email2')
if new_email1 and new_email2:
if new_email1 != new_email2:
raise forms.ValidationError(
self.error_messages['email_mismatch'],
code='email_mismatch',
)
return new_email2
def save(self, commit=True):
email = self.cleaned_data["new_email1"]
self.user.email = email
if commit:
self.user.save()
return self.user
This class checks both if the e-mail have in fact changed (very useful if you need to validate the e-mail or update mail chimp for example) and produce the appropriate errors, so they are helpful for the user in the form view.
views.py
Your code adapted to my class:
#login_required()
def email_change(request):
form = EmailChangeForm()
if request.method=='POST':
form = EmailChangeForm(user, request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect("/accounts/profile/")
else:
return render_to_response("email_change.html", {'form':form},
context_instance=RequestContext(request))
As you can see the view is simplified, assuring everything on the form level.
To ensure the login I set a decorator (See the docs).
Ps: I changed email1 and email2 to new_email1 and new_email2 to be consistent with the Django approach on passwords. I also changed the form Email_Change_Form to EmailChangeForm according to Python guidelines for classes.
I would suggest moving the validation to the form clean method:
#form
class EmailChangeForm():
..
..
def clean(self):
if self.cleaned_data.get('email1', None) != self.cleaned_data.get('email1', None):
raise forms.ValidationError('Validation Failed')
#login_required('/login/') //You can check the user is logged in using the decorator
def email_change(request):
form = Email_Change_Form()
if request.method=='POST':
form = Email_Change_Form(request.POST)
if form.is_valid():
user = request.user //Don't know why you want to get the object from database when you already have it
user.email = form.cleaned_data['email1']
user.save()
return HttpResponseRedirect("/accounts/profile/")
else:
return render_to_response("email_change.html", {'form':form}, context_instance=RequestContext(request))
Update:
Doing this is redundant:
user = request.user
u = User.objects.get(username=user.username)
Because user is going to be the same as u i.e. user = u
You will create more complicated code with nested if, if you write every bit of logic in your views. You need to break them in appropriate sections. Like, for every form related validations, do it in forms like -
if `email1` is same as `email2`,
and if email1 is valid
check it in your form. You should check that in clean or clean_FieldName methods. Refer here: https://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
Another check you applied for authentication - if the user is authenticated or not. In this case, can a Unauthorised user change his email - well no. So why should I let my code run for it. It would be better to check this condition as soon as possible and then send the user to login page. #login_required is used to check this condition as a decorator of your view. See here : https://docs.djangoproject.com/en/dev/topics/auth/#the-login-required-decorator
If you really want to check your user authentication in your view, I think the good approach would be -
def email_change(request):
if not request.user.is_authenticated:
// Do what you need to say to user or send them to login
// return HttpResponse object / HttpResponseRedirect
form = Email_Change_Form(request.POST)
if request.method=='POST':
if form.is_valid():
...
else:
... // Display form.
Related
after long time passed at searched a solution, i not advanced for a reason.
I try of make operate the functionality "if exists():" for "User.username" of the user, and so avoid the duplicate username in my webSite,but this is not work.
this is my code in views.py :
def edit_profil(request, pk=id):
error_name = reverse_lazy('profil_edit')
template_name="blog/edit_profil.html"
if request.method == "POST":
form = EditProfilForm(data=request.POST, instance=request.user)
if form.is_valid():
usernameDatas = form.cleaned_data['username']
if User.objects.filter(username=usernameDatas).exists():
messages.error(request, 'This is Username already exists !')
return redirect(error_name)
else:
user = form.save(commit=False)
user.email = form.cleaned_data['email']
user.username=usernameDatas
user.save()
return redirect('/blog/profil')
else:
form = EditProfilForm(instance=request.user)
return render(request, template_name, locals())
The error received is:
"local variable 'usernameDatas' referenced before assignment"
Where is my error ? thanks all.
.exists() is not the origin of the problem. This is a rather generic error in Python. It means that you use a variable before you assigned a value to it. Now in the .exists() line, only one local variable is used: usernameDatas. So it is likely that this variable is not assigned.
If we now look at possible scenario's where it is not valid, we see that this can happen if the form.is_valid() fails (returns False). The most straightforward way to handle this is by inlining the part:
def edit_profil(request, pk=id):
error_name = reverse_lazy('profil_edit')
template_name="blog/edit_profil.html"
if request.method == "POST":
form = EditProfilForm(data=request.POST, instance=request.user)
if form.is_valid():
usernameDatas = form.cleaned_data['username']
# inline relevant parts
if User.objects.filter(username=usernameDatas).exists():
messages.error(request, 'This is Username already exists !')
return redirect(error_name)
else:
user = form.save(commit=False)
user.email = form.cleaned_data['email']
user.username=usernameDatas
user.save()
return redirect('/blog/profil')
else:
form = EditProfilForm(instance=request.user)
return render(request, template_name, locals())
Note however that the above is an elegant way to tackle the problem. If you specify unique=True for the username field of the related Django model, then the form will automatically validate this. Furthermore you specify paths in the redirct(..) instead of the name of a view, which makes it senstive to URL changes. Finally using locals() is a severe anti-pattern: it is better to pass a dictionary with the variables you use.
In django is it the only way to have only one view for one whole page/url. And whatever functions(upload/post/update/log-in) that page contains just needs to pass inside that view. I found this is the only way as i can only return one url with one view.
I am wondering if there has any way where i can make different view(may be classed base or normal) for each function and at last add all of them on one single view(that view return that url also). If it is possible than how ? Because having all the functions of a url inside one view is looking weird and messy to me.
##################
def logInRegisterUser(request):
###################login##################
loginForm = UserLoginForm(request.POST or None)
if loginForm.is_valid() and 'log-in' in request.POST:
username = loginForm.cleaned_data.get("username")
password = loginForm.cleaned_data.get("password")
user = authenticate(username = username, password = password)
# if not user or not user.check_password(password):
# raise validation error
login(request, user)
print(request.user.is_authenticated())
###################registration###################
registrationForm = RegistrationForm(request.POST or None)
if registrationForm.is_valid() and 'sign-up' in request.POST:
user2 = registrationForm.save(commit = False)
password2 = registrationForm.cleaned_data.get('password')
user2.set_password(password2)
user2.save()
new_user = authenticate(username = user2.username, password = password2)
login(request, new_user)
###################log-out###################
###################search-post###################
####################voting-post##################
context = {
"loginForm":loginForm,
"registrationForm":registrationForm,
"re":request.POST
}
###################return###################
return render(request,"enter.html",context)
you can merge the response from another class. You can merge multiple class response into to a single view.
Django - Having two views in same url
Django's authenticate function seems to always be returning none, ive already checked out this thread and updated the authentication backends in the settigns.py file.
This is the code that i am using to save the accounts being created:
if request.method == 'POST':
# Getting the information from the filled in UserCreationForm
user_form = UserCreationForm(data=request.POST)
# If the the form is valid then save the users data to the database
# hash the password using set_password and save the user again
# set registered to True
if user_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
registered = True
# Invalid form or forms - mistakes or something else?
# Print problems to the terminal.
# They'll also be shown to the user.
else:
print user_form.errors
# Not a HTTP POST, so these forms will be blank, ready for user input.
else:
user_form = UserCreationForm()
# Render the template depending on the context.
return render_to_response(
'contracts/register.html',
{'user_form': user_form, 'registered': registered}, context)
This seems to be working, since after i create a new user on the site, the access database its linked to updates with that username and the encrypted password.
The user_from is created from UserCreationForm which is:
class UserCreationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username','password','first_name','last_name','email','is_staff','is_active')
Here is the code in my user_login method which should be grabbing the username and password from the request and authenticating the combo
def user_login(request):
# context for the user's request.
context = RequestContext(request)
# form = AuthenticationForm()
# If the request is a HTTP POST
if request.method == 'POST':
# Gather the username and password provided by the user.
# This information is obtained from the login form.
user = request.user
userSubmit = user.username
passSubmit = user.password
#username = request.POST.get['username']
#password = request.POST.get['password']
user = authenticate(username = userSubmit, password=passSubmit)
#try:
# user = authenticate(username=userSubmit, password=passSubmit)
#except LockedOut:
# messages.error(request, 'You have been locked out because of too many login attempts. Please try again in 10 minutes.')
# If we have a User object, the details are correct.
# If None (Python's way of representing the absence of a value), no user
# with matching credentials was found.
else:
if user:
# Is the account active? It could have been disabled.
if user.is_active:
# If the account is valid and active, we can log the user in.
# We'll send the user back to the homepage.
login(request, user)
return redirect('home')
else:
# An inactive account was used - no logging in!
messages.error(request, 'Your account is disabled.')
else:
messages.error(request, 'The credentials you entered are invalid.')
# Bad login details were provided. So we can't log the user in.
# The request is not a HTTP POST, so display the login form.
# This scenario would most likely be a HTTP GET.
# No context variables to pass to the template system, hence the
# blank dictionary object...
return render_to_response('administrative/login.html', {'form': form}, context)
I have a login form in django, where I need to do some extra checks in my clean method:
class LoginForm(BootstrapFormMixin, forms.Form):
email = forms.EmailField(required=True, max_length=30)
password = forms.CharField(required=True, widget=forms.PasswordInput)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = self.__class__.__name__.lower()
self.helper.form_action = ''
self.helper.layout = Layout(
Field('email'),
Field('password'),
Div(
Submit('submit', _('Login'),
css_class="btn btn-block btn-success"),
css_class=''
)
)
def clean(self):
email = self.cleaned_data.get('email')
password = self.cleaned_data.get('password')
user = authenticate(email=email, password=password)
if user:
company = user.company
if not company.is_active:
# here I want to make a redirect; if is it possible to add a flash message it would be perfect!
raise forms.ValidationError(_('Account activation is not finished yet'))
else:
raise forms.ValidationError(_('Invalid credentials'))
return self.cleaned_data
It works properly, but when credentials are correct, but user's related object named company is not active (is_active=False) I want to redirect user to another view and add some flash message (using django.contrib.messages maybe).
Is it possible to do such redirection?
Thank you!
You could just add a boolean redirect attribute to the form to know when to do the redirect:
class LoginForm(BootstrapFormMixin, forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.redirect = false
. . .
def clean(self):
email = self.cleaned_data.get('email')
password = self.cleaned_data.get('password')
user = authenticate(email=email, password=password)
if user:
company = user.company
if not company.is_active:
self.redirect = True
raise forms.ValidationError()
else:
raise forms.ValidationError(_('Invalid credentials'))
return self.cleaned_data
from django.contrib import messages
from django.shortcuts import redirect
def your_view(request):
form = LoginForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
# whatever
else:
if form.redirect:
messages.add_message(request, messages.ERROR,
_('Account activation is not finished yet'))
return redirect('wherever')
return render(request, 'your-template.html', {'form': form})
You could specify a particular error code when you raise the validation error in your form.
def clean(self):
...
if not company.is_active:
# here I want to make a redirect; if is it possible to add a flash message it would be perfect!
raise forms.ValidationError(_('Account activation is not finished yet'), code='inactive')
Then, in the view, you can check the error codes, and redirect if appropriate. You can check the error codes using form.errors.as_data(). Since you raise the ValidationError in the clean method, the error doesn't belong to a particular field so you can access it using the __all__ key.
if form.is_valid():
# login user then redirect
else:
for error in form.errors.as_data()['__all__']:
if error.code == 'inactive':
messages.warning(request, 'Account is inactive')
return redirect('/other-url/')
# handle other errors as normal
So I am going to risk a guess that you still want to log the user in FIRST before redirecting the user.
If the above is true, accomplish the PRIMARY functionality of the form first.
The redirect can run in "views" where the user login function needs to run first BEFORE you can redirect the user. Prior to this point there is no need to run additional verification.
Here is how I would write the snippet for the view - only to show the redirect related steps (not the whole view). Assume 'home:index' routes the user to the normal redirect page after login and 'company:add_company_info' routes the user to the aberrant page with the message.
if form.is_valid():
user = form.login(request) # assume this form function calls django authenticate and will return the user if successful
if user:
login(request, user)
if user.company.is_active: # this is assuming the user.company relationship exists
return redirect('home:index')
else:
messages.add_message(request, messages.INFO, "Please fill in your company information")
return redirect('company:add_company_info')
Im trying to modify the current user's data but with no sucess, need some help.
def account_admin(request):
if request.method == 'POST':
mod_form = ModificationForm(request.POST)
if mod_form.is_valid():
user = User.objects.get(request.user)
user.set_password(form.cleaned_data['password1'])
user.email = form.cleaned_data['email']
user.save
return HttpResponseRedirect('/register/success/')
else:
mod_form = ModificationForm()
variables = RequestContext(request, {
'mod_form': mod_form
})
return render_to_response('registration/account.html', variables)
request.user is already an instance of User, there's no point in doing another query.
Plus, you actually need to call save().
Your issue is here:
user = User.objects.get(request.user)
Ideally, it would have been
user = User.objects.get(id=request.user.id)
You dont need a query to retrieve the user object here, since request.user evaluates to an instance of the logged in user object.
user = request.user
user.set_password(form.cleaned_data['password1'])
user.email = form.cleaned_data['email']
user.save()
Should work
Also, make sure you have the #login_required decorator to the account_admin method
You seem to have you answer from the above posts but I notice that you did not authenticate the user (you may have done it elsewhere that I don't know about). In case you want to do so, try the login_reqired decorator https://docs.djangoproject.com/en/1.5/topics/auth/default/#django.contrib.auth.decorators.login_required
Thanks so much karthikr and Daniel! Heres how i got it to work:
def account_admin(request):
if request.method == 'POST':
mod_form = ModificationForm(request.POST)
if mod_form.is_valid():
user = User.objects.get(id=request.user.id)
user.set_password(mod_form.cleaned_data['password1'])
user.email = mod_form.cleaned_data['email']
user.save()
return HttpResponseRedirect('/register/success/')
else:
mod_form = ModificationForm()
variables = RequestContext(request, {
'mod_form': mod_form
})
return render_to_response('registration/account.html', variables)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
karthikr, iam still learning django, i dont get your sugestion of the decorator #login_required. What does it do?