I have a form for changing password:
class PasswordChangeForm(forms.Form):
old_password = forms.CharField(max_length=20)
new_password1 = forms.CharField(max_length=20)
new_password2 = forms.CharField(max_length=20)
def __init__(self, user, *args, **kwargs):
self.user = user
super(PasswordChangeForm, self).__init__(*args, **kwargs)
def clean_old_password(self):
old_password = self.cleaned_data.get("old_password")
if not self.user.check_password(old_password):
raise forms.ValidationError("Your old password is wrong")
return old_password
def clean_new_password2(self):
new_password1 = self.cleaned_data.get("new_password1")
new_password2 = self.cleaned_data.get("new_password2")
if new_password1 and new_password2 and new_password1 != new_password2:
raise forms.ValidationError("Your new passwords didn't match")
return new_password2
In my view I have:
class PasswordChangeView(View):
form_class = PasswordChangeForm
template_name = 'registration/password_change.html'
def get(self, request, *args, **kwargs):
form = self.form_class(user=self.request.user)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
print("wow")
if form.is_valid():
#My logic here
return redirect("password_change_successful")
return render(request, self.template_name, {'form': form})
But my form.is_valid() function is not called.. Even if i enter wrong old password or wrong new passwords it doesnot raise error.
What is wrong in here ??
Thank you
I believe you are misusing class-based views.
Your view should inherit from FormView, not from simple View.
And it should have a method with signature form_valid(self, form), which will be called as soon as a valid form has been submitted.
Also, you don't need to override get and post methods. In fact, it is not very good idea to do so.
I apologize for the criticism.
Here is how your view should look like:
class PasswordChangeView(FormView):
form_class = PasswordChangeForm
success_url = reverse("password_change_successful") # if form had no errors, FormView redirects to this url
template_name = 'registration/password_change.html'
def form_valid(self, form, *args, **kwargs):
# print "wow"
logger.debug("Wow, some user has sent a valid form!")
return super(PasswordChangeView, self).form_valid(form, *args, **kwargs)
# def get(...)
# there is really no need to override get(), FormView will do everything for you
# def post(...)
# same story with the post, if your form was valid,
# form_valid() will be called, otherwise user will stay on the
# same page, with the same form displaying the validation errors
Related
I have a detail view with 2 forms and here I provide code for only one of them. The form is located in a modal on user detailed view and I need to redirect the client to that detail view in which the form is. In the post method the request.GET['user'] returns the user id so I have everything needed to achieve this. I have tried the reverse and redirect, nothing worked I guess because of wrong code.
Should I provide a get_success_url() to do that? I think it will cause some problems because I can get user id only from post method.
class UserDetailView(LoginRequiredMixin, DetailView):
model = TbUser
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['entrance_rights_form'] = TbPeopleEntranceRightForm(
user=self.object, initial={'user': self.object})
return context
class TbPeopleEntranceRightFormView(FormView):
form_class = TbPeopleEntranceRightForm
template_name = 'users/create_entrance_permission_modal.html'
def post(self, request, *args, **kwargs):
print(request.POST['user']) # returns user id
entrance_rights_form = self.form_class(
user=None, data=request.POST)
terminal_permissions_form = TbTerminalPermissionForm(user=None)
if entrance_rights_form.is_valid():
entrance_rights_form.save()
return redirect('user-detail', args=(request.POST['user'],))
else:
return redirect('users-list')
urlpatterns = [
path('users-list/', UsersListView.as_view(), name='users-list'),
path('user-detail/<str:pk>/',
UserDetailView.as_view(), name='user-detail'),
path('tb-entrance-right-form/submit',
TbPeopleEntranceRightFormView.as_view(), name='tb-entrance-right-form'),
]
You don't need to pass the user id in the args as a tuple with redirect.
This should work:
if entrance_rights_form.is_valid():
entrance_rights_form.save()
user_id = request.POST['user'] # i suppose this returns user id as you mentioned
return redirect('user-detail', user_id)
EDIT:
you are not rendering the template inside the post method.
def post(self, request, *args, **kwargs):
print(request.POST['user']) # returns user id
entrance_rights_form = self.form_class(
user=None, data=request.POST)
terminal_permissions_form = TbTerminalPermissionForm(user=None)
if entrance_rights_form.is_valid():
entrance_rights_form.save()
return redirect('user-detail', request.POST['user'])
else:
return redirect('users-list')
return render(request, self.template_name, {'form': entrance_rights_form})
I do not want the logged in user to show up on this ModelMultipleChoiceField in order to restrict themselves from creating a following relationship with themselves? So how do I exclude the logged in user from the queryset, probably an easy fix but I'm new to Django and it has eluded me for a few hours now.
forms.py
class Add_Profile(forms.ModelForm):
def __init__(self,*args, **kwargs): # initializing your form in other words loading it
super(Add_Profile, self).__init__(*args, **kwargs)
user_id = kwargs.pop('user_id') # taking user_id out of the querylist
self.fields['follows'] = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=UserProfile.objects.filter(~Q(id=user_id)))
class Meta:
model = UserProfile
fields = (
'bio',
'follows',
'theme',
'profile_picture',
)
Views.py
#login_required
def edit_profile(request, user_id):
userprofile = UserProfile.objects.get(pk=user_id)
if request.method == 'POST':
edit_profile = Add_Profile(request.POST, request.FILES, instance=userprofile, user_id=request.user.id)
if edit_profile.is_valid():
edit_profile.save()
return redirect('/home/user/{0}/'.format(request.user.username))
else:
print edit_profile.errors
else:
edit_profile = Add_Profile(instance=userprofile, user_id=request.user.id)
return render (request, 'edit.html', {'form': edit_profile,})
Error: init() got an unexpected keyword argument 'user_id'
You can definitely do it using forms.Form instead of forms.ModelForm with something along the lines of this example in the docs:
from django import forms
from django.contrib.auth import get_user_model
class Add_Profile(forms.Form):
follows = forms.ModelMultipleChoiceField(queryset=None)
def __init__(self, user=None, *args, **kwargs):
super(Add_Profile, self).__init__(*args, **kwargs)
if user is not None:
self.fields['follows'].queryset = get_user_model().objects.exclude(pk=user.pk)
else:
self.fields['follows'].queryset = get_user_model.objects.all()
Just pass in the user you wish to exclude when you instantiate the form:
form = Add_Profile() # all users will be present in the dropdown
some_guy = User.objects.get(pk=4)
form = Add_Profile(user=some_guy) # all users except some_guy will be present
Define an __init__ method for the form class. Pass the logged in userid to the form while initializing it, this will work with a model form.
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user_id')
super(Add_Profile, self).__init__(*args, **kwargs)
self.fields['follows'] = forms.ModelMultipleChoiceField(queryset=UserProfile.objects.filter(~Q(user_id=user_id)))
While initializing your form, you can pass user_id
address_form = Add_Profile(request.POST, user_id=request.user.id)
I am using Django 1.8 and have implemented a custom user model. The user registration piece is 100% functional; I can submit a form and verify that users are created. But I am struggling with the user login process.
The login form renders just fine, but when I enter a username and password that I have verified is registered (verified via the Django admin) I get to the HttpResponse('Form is invalid') message.
I have been stuck on this for a day or two. Any suggestions are greatly appreciated!
accounts/views.py
from django.views.generic import FormView
from django.contrib.auth import authenticate, login
from django.shortcuts import render
from accounts.forms import CustomUserCreationForm, CustomUserLoginForm
from accounts.models import CustomUser
class CustomUserCreateView(FormView):
form_class = CustomUserCreationForm
template_name = 'registration/registration_form.html'
success_url = '/connections/'
def form_valid(self, form):
form.save()
return super(CustomUserCreateView, self).form_valid(form)
class CustomUserLoginView(FormView):
form_class = CustomUserLoginForm
template_name = 'registration/login.html'
success_url = '/success/'
def get(self, request, *args, **kwargs):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {'form':form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
user = authenticate(
username=form.cleaned_data['email'],
password=form.cleaned_data['password'],
)
if user is not None:
if user.is_active:
login(request, user)
return HttpResponseRedirect(success_url)
else:
return HttpResponse('User is not active') # TEMP
else:
return HttpResponse('User does not exist') # TEMP
else:
return HttpResponse('Form is invalid') # TEMP
accounts/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from .models import CustomUser
class CustomUserLoginForm(AuthenticationForm):
model = CustomUser
# TODO - need to provide error message when no user is found
class CustomUserCreationForm(UserCreationForm):
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput)
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = ('first_name', 'last_name', 'email', 'mobile_number')
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
raise forms.ValidationError('Passwords do not match!')
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data['password1'])
if commit:
user.save()
return user
That error means that your "post" method in the "CustomUserLoginView" is not returning an HttpResponse, because you have few "pass" instead of return the correct response. Its because you do nothing in few cases then the bottom of the method is reached and by default python functions/methods return None. You are only returning the HttpResponse in one case (when the user.is_active). You should see for which branch of the "if-else"'s you are passing. You have to return an HttpResponse in all the cases (always).
Have fun!
This answer ultimately led me to the fix.
In the 'post' method I needed to change the line from:
form = self.form_class(request.POST)
to:
form = self.form_class(data=request.POST)
Finally, my CustomUserLoginView looks like this:
class CustomUserLoginView(FormView):
form_class = AuthenticationForm
template_name = 'registration/login.html'
success_url = '/connections/'
def get(self, request, *args, **kwargs):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {'form':form})
def post(self, request, *args, **kwargs):
form = self.form_class(data=request.POST)
if form.is_valid():
user = authenticate(
username=form.cleaned_data['username'],
password=form.cleaned_data['password'],
)
if user is not None:
if user.is_active:
login(request, user)
return HttpResponseRedirect(self.success_url)
else:
return HttpResponse('User is not active') # TEMP
else:
return HttpResponse('User does not exist') # TEMP
else:
return HttpResponse('Form is not valid') # TEMP
I have a update view:
class GeneralUserUpdateView(UpdateView):
model = GeneralUser
form_class = GeneralUserChangeForm
template_name = "general_user_change.html"
def dispatch(self, *args, **kwargs):
return super(GeneralUserUpdateView, self).dispatch(*args, **kwargs)
def post(self, request, pk, username):
self.pk = pk
self.username = username
self.gnu = GeneralUser.objects.get(pk=self.pk)
#form = self.form_class(request.POST, request.FILES)
return super(GeneralUserUpdateView, self).post(request, pk)
def form_valid(self, form, *args, **kwargs):
self.gnu.username = form.cleaned_data['username']
self.gnu.email = form.cleaned_data['email']
self.gnu.first_name = form.cleaned_data['first_name']
self.gnu.last_name = form.cleaned_data['last_name']
self.gnu.address = form.cleaned_data['address']
self.gnu.save()
return redirect("user_profile", self.pk, self.username)
Here in this view I want to pass a context like:
context['picture'] = GeneralUser.objects.get(pk=self.pk)
I did trying get_context_data but I cant access pk in there..
Am I doing the update right?? How can I pass that context in there??
You shouldn't be overriding post at all. All of that logic should happen in get_context_data.
In fact, none of your overrides are needed. Everything that you do in form_valid will be done already by the standard form save. And overriding dispatch just to call the superclass is pointless.
Your view should look like this only, with no overridden methods at all:
class GeneralUserUpdateView(UpdateView):
model = GeneralUser
form_class = GeneralUserChangeForm
template_name = "general_user_change.html"
context_object_name = 'picture'
(although it seems a little odd that you want to refer to an instance of GeneralUser as "picture").
Edit to redirect to a specific URL, you can define get_success_url:
def get_success_url(self):
return reverse("user_profile", self.kwargs['pk'], self.kwargs['username'])
I have a FormView that is basically a product page. You can view product details and request the product from a form on that page. I want the page to be set up so that anyone can view the page, but only people that are logged in can request the product. To do this, I added a login_required decorator to the post function in the FormView, but I get the error:
'QueryDict' object has no attribute 'user'
How can I code this view/form so it acts as I described?
View:
class RedeemReward(SingleObjectMixin, FormView):
template_name = 'reward.html'
slug_field = 'reward_slug'
form_class = Redeem_Reward
model = Reward
#method_decorator(login_required)
def post(self, request, *args, **kwargs):
return super(RedeemReward, self).post(request, *args, **kwargs)
def form_valid(self, form):
form_class = self.get_form_class()
form = self.get_form(form_class)
#form = self.get_form(self.get_form_class())
#form.save(self.request.POST)
form.save(self.request.POST)
return super(RedeemReward, self).form_valid(form)
def get_success_url(self):
return reverse('reward_confirmation', args=(self.object.reward_slug, self.object.reward_code))
def dispatch(self, *args, **kwargs):
self.object = self.get_object()
return super(RedeemReward, self).dispatch(*args, **kwargs)
Form:
class Redeem_Reward(forms.Form):
quantity = forms.IntegerField(label=_('Quantity'), error_messages={'invalid':'Must be a valid number'})
reward_name = forms.CharField(max_length=50, widget=forms.HiddenInput(), label=_('Reward Name'), error_messages={'invalid':'Invalid reward'})
def clean_quantity(self):
"""
Validate that the user entered a valid number.
"""
return self.cleaned_data['quantity']
def clean_reward_name(self):
"""
Validate that this reward code exists.
"""
try:
existing_reward = Reward.objects.get(reward_name=self.cleaned_data['reward_name'])
except ObjectDoesNotExist:
raise forms.ValidationError(_("The reward you requested does not exist."))
return self.cleaned_data['reward_name']
def save(self, request, *args, **kwargs):
"""
Save all of the required data.
"""
user = request.user
#user_points = Points.objects.filter(affiliate__id=user.id).annotate(total_points=Sum('points'))
user_points = Affiliate.objects.filter(points__affiliate__id=user.id).annotate(total_points=Sum('points'))
user_points = user_points[0].total_points
error_message = {'lookuperror':'You need to provide a valid quantity',
'insufficient_points':"You don't have enough points for this purchase."}
try:
quantity = self.cleaned_data['quantity']
reward_name = self.cleaned_data['reward_name']
rewards = Reward.objects.get(reward_name=reward_name)
except LookupError:
raise Http404
try:
points_cost = -(rewards.reward_cost * quantity)
except ArithmeticError:
raise Http404
quote_price = -(points_cost)
if user_points >= quote_price:
reward_order = Points.objects.create(affiliate=user, reward=rewards, points=points_cost, from_reward=True, from_offer=False, from_referral=False)
status_cost = Status_Code.objects.create(short_name="Pending", name="The order is currently being reviewed", description="The order is in queue")
redeem_order = Redeem.objects.create(affiliate=user, reward=rewards, status_code=status_code)
redeem_details = Redeem_Details.objects.create(redeem=redeem_order, quantity=quantity, quote_price=quote_price)
return HttpResponseRedirect(reverse('reward_confirmation', args=(redeem_details.redeem_code,)))
else:
return render(request, 'reward.html', {'error_message':error_message['insufficient_points']})
You're passing self.request.POST to your form's save method, which is set to expect a request as its first argument. Or at least something that has a user attribute. If you pass in self.request instead, you will no longer get that particular error.
It is odd that you're reinstantiating form in your form_valid method, which receives the bound form as an argument.
You're returning HttpResponse objects from your form's custom save method, which is nonstandard. But as long as you're doing that, you should return them from your form_valid method.
In summary, something like this:
def form_valid(self, form):
return form.save(self.request)