Form doesn't accept additional parameters - python

I was trying to pass an additional parameter to my form, which is anObject to ForeignKey relation. But dunno why form returns __init__() got an unexpected keyword argument 'parent' when I'm pretty sure that it is possible to send additional parameters to form's __init__ (ie here : Simple form not validating). Am I wrong ?
def add_video(request):
parent = ParentObject.objects.all()[0]
if request.method == 'POST':
form = VideoForm(data=request.POST, parent=parent)
if form.is_valid():
form.save()
next = reverse('manage_playforward',)
return HttpResponseRedirect(next)
else:
form = VideoForm()
class VideoForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
try:
self.parent = kwargs.pop['parent']
logging.debug(self.parent)
except:
pass
super(VideoForm, self).__init__(*args, **kwargs)

kwargs.pop['parent'] is throwing TypeError: 'builtin_function_or_method' object is unsubscriptable, because you're trying to do a key lookup on a function method ({}.pop). This error is then being swallowed by your exception handler.
For this to work do kwargs.pop('parent', None). In your case:
class VideoForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.parent = kwargs.pop('parent', None)
super(VideoForm, self).__init__(*args, **kwargs)
As a side note, 99% of time its best to only catch specific Exceptions in your except blocks. Doing so will help dodge bugs/confusion like this. Also, I would highly suggest adding unit tests for this custom construction (or just TDDing your other code, but that's a separate issue)

Related

Accessing Cleaned Data of one form inside a second form

So I have two forms, Sale and SaleItems
SaleForm = SaleForm(request.POST or None, auto_id=False, prefix = 'SaleForm')
SaleItemsForm = modelformset_factory(
Sale, form = SaleItemsForm, formset = ItemsFormSet, extra=1, can_delete=True
)
once they're both given POST data and valid, they're in the usual statement:
if SaleForm.is_valid() and SaleItemsForm.is_valid():
pass
When it comes time to do validation I've superseded the basemodelformset and want to write my own custom clean method for the modelformset. I want to use cleaned data from SaleForm inside the clean method for the ItemsFormSet:
from django import forms
class ItemsFormSet(forms.models.BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(ItemsFormSet, self).__init__(*args, **kwargs)
def clean(self):
super().clean()
print(TheSaleForm.cleaned_data['Value'])
This doesn't work, and I've tried a few dumb things:
try to make SaleForm a global and access it between the views.py and forms.py modules. That was a bad idea and now I understand more about the module scope
try to import the actual object, again not smart
I'm assuming there has to be a way to do this without saving the cleaned data off somewhere to the database and retrieving it again in the clean method of the formset. I'm not sure if overwriting ItemsFormSet.is_valid() and trying to allow a kwarg dictionary item to be passed through would be the right way to go.... but I'm hoping someone has an idea of what the "correct" way to approach this is.
You should allow the data to be passed into the init of ItemsFormSet and keep it as an instance attribute which you can reference later.
class ItemsFormSet(forms.models.BaseModelFormSet):
def __init__(self, *args, **kwargs):
self.sale_form = kwargs.pop('sale_form', None)
super(ItemsFormSet, self).__init__(*args, **kwargs)
def clean(self):
super().clean()
print(self.sale_form.cleaned_data['Value'])
and now in your view:
if request.method == 'POST':
form = SaleForm(request.POST)
formset = SaleItemsForm(request.POST, sale_form=form)

Failure when using setattr to add field to Django form class

I'm trying to write use Django FormView and a bit of ingenuity to create a view which will allow me to get inputs from a user that will be fed to a function. I'd like the code to be reusable, so I'd like to make a view that will be able to take a target function as a parameter and automagically create a form appropriate to that function. There is more plumbing to be done, but the general idea would be:
class FormViewForFunction(FormView):
template_name = '...'
func = None
def get_form_class(self):
class _FunctionForm(forms.Form):
pass
a = inspect.getargspec(self.func)
for argname in a['args']:
setattr(_FunctionForm, argname, forms.CharField())
return _FunctionForm
The idea would be that then you could set up something in your URLConf that used FormViewForFunction.as_view(func=***insert any function you want***) and you would wind up being presented with a form that was appropriate for specifying parameters for that function. Let's not worry about what would happen on form submission. For now I'm just stuck getting the form to generate properly.
With the code above, the form doesn't wind up having any fields! What am I doing wrong?
form's fields are initialized during initialization, you should override the __init__ method and then append the fields to the self.fields dictionary
This should work:
class FormViewForFunction(FormView):
template_name = '...'
func = None
def get_form_class(self):
a = inspect.getargspec(self.func)
class _FunctionForm(forms.Form):
def __init__(self, *args, **kwargs):
super(_FunctionForm, self).__init__(*args, **kwargs)
for argname in a['args']:
self.fields[argname] = forms.CharField()
return _FunctionForm

Class Based View and decorators

I have a FormView for displaying form, and code goes on like this:
class AddProject(FormView):
template_name = "project/add_project.html"
#method_decorator(check_user_type)
def dispatch(self,request, *args, **kwargs):
return super(AddProject,self).dispatch(request, *args, **kwargs)
def get_form_class(self):
return AddProjectForm
def form_valid(self,form):
#do validation, return response
the decorator check_user_type is like this:
def check_user_type(func):
def wrapped_func(request, *args, **kwargs):
kwargs['invalid_user'] = True
return func(request,*args, **kwargs)
return wrapped_func
In my decorator I want To make sure that only certain type of user get to see the form, i.e if request.user.Iam == 'Architect' or request.user.Iam == 'Interior Designer' only see the form and others see a message "Only architects/Interior Designer get to upload photos".For this i want to insert a variable 'invalid_user' to be passed along, depending on which i display the form or the message.
Problem is I am unable to pass the variable :( alongwith it .. and a doubt.. if i have correctly devised the idea to do so.. ?
If I understand you correctly, you want to pass this argument to check_user_type decorator, right? Then you need to nest another function in your decorator, using closure to set variables inside it. Something like
def check_user_type(parameter):
def check_user_type_inner(func):
def wrapped_func(request, *args, **kwargs):
... # parameter here depends on argument passed to most outer function
return func(request,*args, **kwargs)
return wrapped_func
return check_user_type_inner
then parameter is available inside scopes of both inner functions.

Django: Access and Update fields of a Form Class inside __init__ or save functions

I have a MyModelForm form class for MyModel model class, and I want to generate a random value for a certain field.
The way I see it is either inside init or save functions, I tried using self.fields['randfield'] but it throws an error 'MyModelForm' object has no attribute 'fields'.
How can I access and update a field inside form class so that I can instantiate it with a random value?
Thanks.
EDIT: After using self.fields['randint'].initial I am getting a KeyError. The code is
Okay, here goes:
def __init__(self, instance=None, *args, **kwargs):
_fields = ('username', 'email')
_initial = model_to_dict(instance.user, _fields) if instance is not None else {}
super(UserDetailsForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
self.fields.update(fields_for_model(User, _fields))
self.fields['randint'].initial = '987654321'
Use something like this:
class RandomValueForm(ModelForm):
myfield = models.IntegerField(default=0)
def __init__(self, *args, **kwargs):
super(RandomValueForm, self).__init__(*args, **kwargs)
self.fields['myfield'].initial = my_random_generator()
You got this error because you would have tried accessing fields on self without calling the __init__ of superclass. So, first you need to call superclass __init__ i.e __init__ of ModelForm and then you can access fields.
class MyModelForm(ModelForm):
def __init__(self, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
self.fields['myfield'].initial = my_random_number()

Django form, overriding init, variable scope

In django, I have a form being called from the view, which is passed an extra object that popped in the form init. I want to use this object data (person) in the clean def's outside of init. How can I fix the scope of this passed information? Thanks!
class RegForm(forms.Form):
first = forms.CharField(min_length=5)
def __init__(self, *args, **kwargs):
person = kwargs.pop("person")
super(CompleteRegistrationForm, self).__init__(*args, **kwargs)
def clean_first(self):
if not self.cleaned_data['first'] == person.first:
raise forms.ValidationError(_("This information does not match records."))
else:
return self.cleaned_data['first']
person should be an instance variable:
def __init__(self, *args, **kwargs):
self.person = kwargs.pop("person")
super(CompleteRegistrationForm, self).__init__(*args, **kwargs)
Then, in other methods, refer to it as self.person (not just person).
You should assign it to self:
self.person = kwargs.pop("person")
This is fairly basic Python - you would probably benefit from doing a tutorial.

Categories

Resources