Stuck in passing and catching kwargs - python

Python 3.5.2
Could you help me understand how parameters are transmitted to the method.
Just in case: this is Django but the question seems to be just about Python.
As far as I can catch from the Django's documentation: if I pass form_kwargs to a formset like this, several forms will be created. And in the form I expect to catch the parameter in **kwargs.
class MyArticleForm(ArticleForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(MyArticleForm, self).__init__(*args, **kwargs)
formset = ArticleFormSet(form_kwargs={'user': user})
Well, this is not working:
File "/home/michael/workspace/formsets/general/forms.py", line 15, in __init__
self.user = kwargs.pop('user')
KeyError: 'user'
If I step in the debugger: kwargs definitely doesn't contain any 'user'. And args is an empty tuple.
But this works:
class MyArticleForm(ArticleForm):
def __init__(self, user=None, *args, **kwargs):
pass
And this works:
class MyArticleForm(ArticleForm):
def __init__(self, user, *args, **kwargs):
pass
Well, I can't understand how can I catch this parameter in using kwargs?
///////////
ADDED LATER
ArticleFormSet = formset_factory(MyArticleForm)
def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
can_delete=False, max_num=None, validate_max=False,
min_num=None, validate_min=False):
pass
class BaseFormSet(object):
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, form_kwargs=None):
...
self.form_kwargs = form_kwargs or {}
....

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.

Python - Django - Pass Argument To Form Clean Method

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)

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)

django: subclass FormView twice and override form_class

Im trying to build a FormView for an app that needs to be subclassed afterwards. Sadly I was not able to set the formclass by the subclass.
My Code:
class EventCreateView(FormView):
template_name='Events/add_event_form.html'
success_url = reverse_lazy('events_list')
form_class = None # replaced by __init__ function
def __init__(self, *args, **kwargs):
self.form_class=EventForm
return super(EventCreateView, self).__init__(*args, **kwargs)
#other functions, not shown here ..
class TrainingCreateView(EventCreateView):
def __init__(self, *args, **kwargs):
self.form_class=TrainingForm
return super(TrainingCreateView, self).__init__(*args, **kwargs)
urls.py:
urlpatterns = patterns('',
url(r'event/event/add/$', EventCreateView.as_view(), name='event_add'),
url(r'event/training/add/$', TrainingCreateView.as_view(), name='training_add'),
)
What am I doing wrong?
Try this instead:
class EventCreateView(FormView):
template_name='Events/add_event_form.html'
success_url = reverse_lazy('events_list')
form_class = EventForm
...
class TrainingCreateView(EventCreateView):
form_class = TrainingForm
This doesn't work for the TrainingCreateView because the __init__ view does the following
It sets self.form_class = TrainingForm
super(TrainingCreateView, self).__init__(*args, **kwargs) calls the __init__ of EventCreateView ...
Which sets self.formclass = EventForm
You can get around this by changing the order of your __init_ method. Note that the method doesn't have to return anything.
class TrainingCreateView(EventCreateView):
def __init__(self, *args, **kwargs):
super(TrainingCreateView, self).__init__(*args, **kwargs)
self.form_class = TrainingForm
However, from the code you've written, it is not clear why you need to set self.form_class in the __init__ method, rather than just setting it as a class attribute. If you need to set it dynamically, a better option might be to override get_form_class instead.

Context processor in forms.py

context_processor.py:
def max_min(request):
"""
some code. request required, because I need
calculate some information about each user
"""
return {'max': max, 'min':max}
forms.py:
class MaxMin(forms.Form):
my_field = forms.IntegerField(
min_value=min, max_value=max, required=True,
widget=forms.NumberInput(attrs={'class':'form-control',
'required': 'true'}))
So, I have code in context_processor.py (it uses on all pages of site), that I want use in my forms.py. But I dont know, how to import and use it. max_min needs request, but in forms.py I dont have it.
Yes, I know, I can use {{min}} and {{max}} in forms html, without forms.py, but then I need to check in code, whats values I got through POST.
Thanks.
UPDATE:
class MAxMin(forms.Form):
def __init__(self, req, *args, **kwargs):
super(MAxMin, self).__init__(*args, **kwargs)
self.request = req
def mymethod(self):
return max_min(self.request)
my_field = forms.IntegerField(
min_value=mymethod(), required=True,
widget=forms.NumberInput(attrs={'class':'form-control',
'required': 'true'}))
What about injecting request at the time of instantiation?
#views.py
form = MyForm(request)
#forms.py
def __init__(self, req, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.request = req
#somewhere in your form's body
max_min(self.request)
UPDATE
Well, your situation appears to be different from what I've expected.
Consider this approach:
class MAxMin(forms.Form):
my_field = forms.IntegerField(required=True,
widget=forms.NumberInput(attrs={'class':'form-control',
'required': 'true'}))
def __init__(self, req, *args, **kwargs):
super(MAxMin, self).__init__(*args, **kwargs)
self.fields['my_field'].min_value = max_min(req)

Categories

Resources