I was having trouble with FormView recently and found that the way to go about doing it was to use get_form_kwargs.
Here is my code:
class InternalResetPasswordView(FormView):
template_name = 'reset_password.html'
form_class = forms.InternalPasswordResetForm
# success_message = "Password was reset successfully"
# To get request object
# http://notesondjango.wordpress.com/2012/12/18/modelform-formview-and-the-request-object/
# https://stackoverflow.com/questions/13383381/show-message-after-password-change
# http://pydanny.com/simple-django-email-form-using-cbv.html
# http://bubuzzz.wordpress.com/2012/05/01/class-based-generic-views-in-django-a-simple-sample/
def get_form_kwargs(self):
kwargs = super(InternalResetPasswordView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def get_success_url(self):
return reverse('user-detail', kwargs={'pk': self.request.user.id})
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(InternalResetPasswordView, self).dispatch(*args, **kwargs)
'''
def get_context_data(self, **kwargs):
context = super(InternalResetPasswordView, self).get_context_data(**kwargs)
context['InternalPasswordResetForm'] = context.get('form')
return context
def get_form_kwargs(self):
kwargs = super(InternalResetPasswordView, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
'''
# self.request.user method obtained from
# https://docs.djangoproject.com/en/dev/topics/class-based-views/generic-editing/
def form_valid(self, form):
current_password = form.cleaned_data['old_password']
new_password = form.cleaned_data['new_password1']
confirm_new_password = form.cleaned_data['new_password2']
user = self.request.user
if user.check_password(current_password) and new_password == confirm_new_password:
user.set_password(new_password)
user.save()
# form.valid() redirects to get_success_url
return super(InternalResetPasswordView, self).form_valid(form)
After looking at this post, I still don't understand why get_form_kwargs has to be used and why using self.request instead of self.request.user in this case gives __init__() got an unexpected keyword argument 'request'.
Could someone explain this to me?
Thanks for all the help :)
The get_form_kwargs method will return a dictionary with the kwargs that will be passed to the __init__ of your form. Now, if you have a form that expects a kwarg named user and pass it a kwarg named request it will complain with the error you see. If you want to pass request instead of user (this is what I usually do since the request contains the user) then you should define your form class like this:
class RequestForm(forms.Form):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(RequestForm, self).__init__(*args, **kwargs)
Related
How can I access request in __init__ form?
forms.py
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(MyForm, self).__init__(*args, **kwargs)
def clean(self):
... access the request object via self.request ...
views.py
myform = MyForm(request.POST, request=request)
but what if I use class based views FormView?
If you need to access request in your MyForm you can override the FormView.get_form_kwargs method.
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'request': self.request})
return kwargs
FormView source
class FormMixin(ContextMixin):
...
def get_form(self, form_class=None):
"""
Returns an instance of the form to be used in this view.
"""
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs())
def get_form_kwargs(self):
"""
Returns the keyword arguments for instantiating the form.
"""
kwargs = {
'initial': self.get_initial(),
'prefix': self.get_prefix(),
}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs())
You can use the user_passes_test decorator, documented at:
https://docs.djangoproject.com/en/1.9/topics/auth/default/#django.contrib.auth.decorators.user_passes_test
def my_test(user):
return user.username == 'me'
#user_passes_test(my_test)
class MyView(FormView):
...
There is my view:
class SendTransfer(SingleObjectMixin, FormView):
model = BankAccount
form_class = SendTransferForm
template_name = 'dashboard/send_transfer.html'
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
return super(SendTransfer, self).dispatch(request, *args, **kwargs)
def get_object(self, queryset=None):
obj = super(SendTransfer, self).get_object(queryset)
if not obj.is_owner(self.request.user.citizen):
raise Http404
return obj
def form_valid(self, form):
data = form.cleaned_data
MoneyTransfer.objects.create(sender=self.object,
receiver=data['receiver'], # ModelChoiceField in the form
total=data['total'], # FloatField in the form, etc.
when=timezone.localtime(timezone.now()),
comment=data['comment'])
return redirect('AccountDetail', self.object.pk)
def form_invalid(self, form):
return render(self.request, self.template_name, self.get_context_data())
def get_form_kwargs(self):
return {'sender': BankAccount.objects.get(id=self.kwargs['pk']), 'user': self.request.user}
when form is submitting - I'm getting the same result as after get. Debugger says that clean() is not called but form_invalid is works. What is the problem?
You have overridden get_form_kwargs, and now you are no longer passing data to the form. Without data, the form is unbound, so will never be valid.
It would be better to call super() first, update the kwargs, then return them.
def get_form_kwargs(self):
kwargs = super(SendTransfer, self).get_form_kwargs()
kwargs['sender'] = BankAccount.objects.get(id=self.kwargs['pk']),
kwargs['user'] = self.request.user
return kwargs
hi im trying to use the above forms - but i get
__init__() takes at least 2 arguments (1 given)
i get to the form that it should show but it never save me the new password
i also needed to change the:
def __init__(self, user, *args, **kwargs):
self.user = user
super(AdminPasswordChangeForm, self).__init__(*args, **kwargs)
to:
def __init__(self, *args, **kwargs):
super(AdminPasswordChangeForm, self).__init__(*args, **kwargs)
since it doesnt get a user arg.
any ideas why?
thx
============================ edit =============================================
class set(FormView):
model = User
form_class = AdminPasswordChangeForm
template_name = 'set.html'
def dispatch(self, request, *args, **kwargs):
return super(set, self).dispatch(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super(set, self).get_form_kwargs()
kwargs['user_to_update'] = the user
return kwargs
the init:
def __init__(self, *args, **kwargs):
self.user = kwargs['user_to_update']
kwargs.pop('user_to_update')
super(AdminPasswordChangeForm, self).__init__(*args, **kwargs)
Use the existing form. Overide the view's get_form_kwargs method to pass the expected arguments to the form, instead of changing the __init__ method, which will break other things.
In order to save the password, you need to override the form_valid method and call form.save().
For create and update views, you don't always need to override form_valid, because the default behaviour is to save the form and redirect. For FormView, the default behaviour is simply to redirect, so you do have to override it to get it to do anything useful.
class SetPasswordView(FormView):
form_class = AdminPasswordChangeForm
template_name = 'set.html'
success_url = '/thanks/'
def get_form_kwargs(self):
kwargs = super(set, self).get_form_kwargs()
kwargs['user_to_update'] = the user
return kwargs
def form_valid(self, form):
form.save()
return super(SetPasswordView, self).form_valid(form)
In my forms.py I raise an validation error when the user is already a member of the project. If i try to add a user who is already a member the validation error gets perfectly raised, but then I get redirected to the template and I have no context any more.
Any Best Practices in raising a form validation error? What am I doing wrong?
class AddUserForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.project = kwargs.pop('project')
self.user = kwargs.pop('user')
super(AddUserForm, self).__init__(*args, **kwargs)
self._user_cache = None
def clean_user(self):
"""
Check if the user is already a member of the project.
"""
user = self.cleaned_data['user']
if ProjectMember.objects.filter(project=self.project, user=user).exists():
raise forms.ValidationError(_("User is already a member of this project."))
# store user instance we queried for here to prevent additional lookups.
self._user_cache = user
return user
views.py without the ProjectUpdate view because it does not matter in this case. The views are a little bit complicated, because I have 2 forms in one template. If you know any better way to accomplish this, let me know.
class ProjectDetailView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
view = ProjectDisplay.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if 'update_form' in request.POST:
view = ProjectUpdate.as_view()
elif 'add_user_form' in request.POST:
view = ProjectAddUser.as_view()
return view(request, *args, **kwargs)
class ProjectDisplay(DetailView):
model = Project
def get_context_data(self, **kwargs):
context = super(ProjectDisplay, self).get_context_data(**kwargs)
context['update_form'] = ProjectUpdateForm(initial={
'name': self.object.name,
'description': self.object.description
})
context['add_user_form'] = AddUserForm(project=self.object, user=self.request.user)
context['project'] = self.object
context['is_member'] = self.object.user_is_member(self.request.user)
return context
class ProjectAddUser(CreateView):
model = ProjectMember
form_class = AddUserForm
template_name = 'projects/project_detail.html'
def get_success_url(self):
return reverse('project_detail', kwargs={'slug': self.get_object().slug})
def get_object(self, queryset=None):
return Project.objects.get(slug=self.kwargs['slug'])
def get_form_kwargs(self):
kwargs = super(ProjectAddUser, self).get_form_kwargs()
kwargs.update({'project': self.get_object()})
kwargs.update({'user': self.request.user})
return kwargs
How would I pass a user object or a request to my form for validation?
For example, I want to be able to do something like this --
class Form(forms.Form):
...
def clean(self)
user = request.user # how to get request.user here?
user = User # how to pass the actual User object?
Thank you.
Just pass it into the constructor and store it as an instance variable:
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(MyForm, self).__init__(*args, **kwargs)
def clean(self):
print self.request.user
...
In your view:
form = MyForm(..., request=request)
And if using a class-based view (a CreateView in this example):
class MyCreateView(CreateView):
...
def get_form_kwargs(self):
kwargs = super(MyCreateView, self).get_form_kwargs()
kwargs.update({'request': self.request})
return kwargs