Python - Django - Pass Argument To Form Clean Method - python

I am trying to pass a variable to a ModelForm clean method using __init__ arguments but have had no success so far - I looked at various posts on StackOverflow but none seemed to help.
My code is the following:
forms.py
class property_booking_form(forms.ModelForm):
check_in_date = forms.DateField(widget=SelectDateWidget)
check_out_date = forms.DateField(widget=SelectDateWidget)
class Meta:
model = Properties_bookings
fields = ['check_in_date', 'check_out_date']
def __init__(self, property_id):
self.property_id = property_id
super(property_booking_form, self).__init__(self, property_id)
def clean(self):
check_in_date = self.cleaned_data.get('check_in_date')
check_out_date = self.cleaned_data.get('check_out_date')
property_min_nights = Properties.objects.get(id=self.property_id).property_minimum_nights
...
views.py
def view(request):
...
if request.method == 'POST':
booking_form = property_booking_form(request.POST, property_id=property_id)
if booking_form.is_valid():
...
else:
booking_form = property_booking_form(property_id=property_id)
return render(...)
This raises the following error:
'property_booking_form' object has no attribute 'get'
Which seems to be related to the widget as per the error description:
Exception Location:
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/forms/widgets.py in value_from_datadict, line 1058
The form works fine without the overriding __init__.
Does anyone know what would be the underlying cause of this issue?
Thanks.

Your __init__ method should accept *args and **kwargs, you should pass these when you call the superclass' __init__ method, rather than self and property_id.
def __init__(self, property_id, *args, **kwargs):
self.property_id = property_id
super(property_booking_form, self).__init__(*args, **kwargs)
You also need to change the way you instantiate the form in the view, since property_id is the first argument. For example:
if request.method == 'POST':
booking_form = property_booking_form(property_id=property_id, data=request.POST)
Alternatively, you can remove property_id from the signature, and pop it from kwargs. In this case, no changes to the views are required.
def __init__(self, *args, **kwargs):
self.property_id = kwargs.pop('property_id')
super(property_booking_form, self).__init__(*args, **kwargs)

This has been solved by amending __init__ as follows:
def __init__(self, *args, **kwargs):
self.property_id = kwargs.pop('property_id', None)
super(property_booking_form, self).__init__(*args, **kwargs)

Related

How do I pass arguments to django forms?

I want to filter form fields querysets based on the user selected. Therefore, I want to pass user as argument to the form in order to filter fields querysets in the form's __init__ method. When I pass any arguments to the form I get the following error.
class UserDetailView(LoginRequiredMixin, FormMixin, DetailView):
model = TbUser
form_class = TbPeopleEntranceRightForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = TbPeopleEntranceRightForm(user=self.object)
context['form'] = form
return context
__init__() got an unexpected keyword argument 'user'
how do I pass the argument correctly, and how I get it in the __init__ form method?
Update:
class TbPeopleEntranceRightForm(forms.ModelForm):
def __init__(self, user, **kwargs):
super().__init__(**kwargs)
print(user)
# Use `self.user` here or in some other methods.
__init__() missing 1 required positional argument: 'user'
don't do it in get_context_data, it's made for something else.
Use get_form_kwargs instead:
class UserDetailView(LoginRequiredMixin, FormMixin, DetailView):
model = TbUser
form_class = TbPeopleEntranceRightForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["user"] = self.object
return kwargs
And in your forms.py:
def __init__(self, user=None, *args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
You need to add the parameter to the form's __init__ method:
class TbPeopleEntranceRightForm(forms.ModelForm):
...
def __init__(self, user=None, **kwargs):
super().__init__(**kwargs)
self.user = user
# Use `self.user` here or in some other methods.
Also, the correct way to then pass the user argument to the form is to override get_form_kwargs in the view, like #MojixCoder showed.

Django 'TestForm' object has no attribute 'fields'

I'm using django:
I'm trying to pass a list of tuples from views.py to a dropdown box form but I get this attribute error
forms.py
import logging
from django import forms
log = logging.getLogger(__name__)
class TestForm(forms.Form):
def __init__(self, *args, **kwargs):
testlist = kwargs.pop('testlist',None)
log.info(regionlist)
self.fields['testlist'] = forms.ChoiceField(choices=testlist)
super(TestForm, self).__init__(*args, **kwargs)
views.py
form = forms.RegionForm(regionlist=data)
Am I using the right method to pass variables between views.py and forms.py?
You need to call super first, so that the superclass sets up the fields attribute.
def __init__(self, *args, **kwargs):
testlist = kwargs.pop('testlist', None)
log.info(regionlist)
super(TestForm, self).__init__(*args, **kwargs)
self.fields['testlist'] = forms.ChoiceField(choices=testlist)

Django FormSetView with custom validation

I'm using the ModelFormSetView class in django-extra-views to create a formset view of all WorkerStatus entries connected to a Worker. I'd also like to use custom validation on the formset, so I've defined my own formset_class and form_class in the view. Here's the view definition:
class WorkerStatusUpdateView(ModelFormSetView):
model = WorkerStatusEntry
formset_class = WorkerStatusFormSet
form_class = WorkerStatusForm
template_name = 'staff/workers/worker_status_update.tmpl'
can_delete = True
can_order = False
fields = ['status', 'start_date']
def dispatch(self, request, *args, **kwargs):
self.worker = Worker.objects.get(pk=self.kwargs['worker_pk'])
return super(WorkerStatusUpdateView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, *args, **kwargs):
ctx = super(WorkerStatusUpdateView, self).get_context_data(*args, **kwargs)
ctx['worker'] = self.worker
return ctx
def get_queryset(self, *args, **kwargs):
return self.worker.statuses.all()
...and here are the definitions of the form and formset respectively:
class WorkerStatusForm(forms.ModelForm):
class Meta:
model = WorkerStatusEntry
fields = ['status', 'start_date']
class WorkerStatusFormSet(BaseModelFormSet):
class Meta:
model = WorkerStatusEntry
def __init__(self, queryset, *args, **kwargs):
super(WorkerStatusFormSet, self).__init__(*args, **kwargs)
def clean(self):
print "Cleaning"
This results in a page where EVERY WorkerStatusEntry in the database is shown in the formset, regardless of get_queryset(). One thing you'll notice is that WorkerStatusFormSet.__init__ takes a queryset argument: I put that there because there was a queryset argument passed to it from the ModelFormSetView, but I don't know what to do with it.
Another thing to note: if I take formset_class = WorkerStatusFormSet out of the view definition, the correct queryset shows up in the formset. However I need to use my own formset class to validate across the whole formset. Unless there's another way?
The problem is your WorkerStatusFormSet.__init__ method. Looking at the code for BaseModelFormSet, the __init__ method already takes a queryset parameter. Since you aren't doing anything in your __init__ method except calling super(), the easiest fix is to remove it.
It's not a good idea to change the signature of the __init__ method as you have done for two reasons
def __init__(self, queryset, *args, **kwargs):
super(WorkerStatusFormSet, self).__init__(*args, **kwargs)
You have changed the order of the arguments. If you look at the code for BaseModelFormset, the first argument is data. That means that data might be incorrectly assigned to queryset if somebody calls WorkerStatusFormSet(data, ...)
You do not do anything with queryset or pass it to super(), so it is lost.

Django pass User instance to Forms when form is created

I have a Django form and l would like to pass a user instance when the form is created
First Approach
This is where l create the form and pass the instance of the user:
form = QuestionForm(request.user, request.POST)
And inside the QuestionForm
def __init__(self, user, *args, **kwargs):
super(QuestionForm, self).__init__(*args, **kwargs)
self.data = user
log.info(self)
Study.objects.filter(owner = self.data.id))
Second Approach
This is where l create the form and pass the request:
form = QuestionForm ( ..., request=request)
And inside the QuestionForm
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(MyForm, self).__init__(*args, **kwargs)
ref = forms.ModelChoiceField(queryset=Study.objects.filter(owner = self.request.user.id))
Now l am getting an error that self is not define and as such l cannot get the user id to query the Study class
Any help would be much appreciated
If you do this code in field declaration section like
class QuestionForm(forms.Form):
ref = forms.ModelChoiceField(queryset=Study.objects.filter(owner=...)
then it will not work because it still doesn't have self variable.
You can do this in init method like this
class QuestionForm(forms.Form):
ref = forms.ModelChoiceField()
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['ref'].queryset = Study.objects.filter(owner=request.user)

Passing arguments to a form obtained by 'getattr()'

I'm creating a view which loads differents forms according to an argument given using the getattr() function:
form = getattr(forms, service.form)
but in the form I need my username to filter my files, so I have this:
class MyForm(forms.Form):
filename = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple)
K = forms.CharField(label='K', max_length=1)
fullOut = forms.CharField(label='fullOut', max_length=1)
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.user = kwargs.pop('user', None)
self.fields['filename'].queryset = userFile.objects.filter(self.user)
The problem is that I don't know how to pass the 'request.user' in my getattr() funtion. I know that if it was static it should be something like:
form = MyForm(user=request.user)
But I have tried somethings like:
form = getattr(forms, service.form, user=request.user)
And it doesn't work.I'm trying this but any idea of how list user files in a form will be welcomed.
Thanks in advance.
This doesn't have anything to do with you using getattr, the problem is in your __init__ method. You need to pop user before calling super().
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(MyForm, self).__init__(*args, **kwargs)
You should instantiate the form as you usually do:
form = MyForm(user=request.user)
It doesn't matter whether MyForm is declared in the same module:
class MyForm(forms.Form):
my_field = forms.CharField()
form = MyForm(user=request.user)
or if you get the form class dynamically using getattr
MyForm = getattr(forms, service.form)
form = MyForm(user=request.user)

Categories

Resources