Passing values into django forms - python

I have googled around trying to figure out and understand how this works, yet I still haven't grasped this quite right. What I want to do is pass a value into a form to then use for a query. I have a session variable called menu_term, which determines the choices in the form.
from views.py
def manage_groups(request):
form = CourseGroupForm(request,current_term=request.session.get('menu_term'))
return render_to_response("accounts/group_management.html", {'form':form}, context_instance=RequestContext(request))
from forms.py
class CourseGroupForm(ModelForm):
def __init__(self, current_term, *args, **kwargs):
super(CourseGroupForm, self).__init__(*args, **kwargs)
courseslist = Course.objects.filter(term=current_term, num_in=settings.LAB_COURSES).order_by('description').distinct();
print(courseslist)
self.fields['courses'].queryset = forms.ChoiceField(label='Select Course', choices=courseslist)
class Meta:
model = CourseGroup
fields = ['name','courses'];
The error I am getting is:
__init__() got multiple values for keyword argument 'current_term'
For the benefit of anyone else coming across this, what are the proper ways of defining a form that takes a value passed in from outside?
Thanks,
Good Day
MJ

Its important to pop the kwarg you instantiate your form with before calling the forms super __init__
class CourseGroupForm(ModelForm):
def __init__(self, current_term, *args, **kwargs):
current_term = kwargs.pop('current_term')
super(CourseGroupForm, self).__init__(*args, **kwargs)
The above assumes current_term is always present.
as #vishen pointed out, check your arguments, you are initializing your form with request as the value for current_term

The error is happening because in your model form init decleration
class CourseGroupForm(ModelForm):
def __init__(self, current_term, *args, **kwargs)
current_term is the first argument that the form is expecting to find, but because you are passing through the request object first and then the current_term after that, your effiectely passing the following
form = CourseGroupForm(current_term=request,current_term=request.session.get('menu_term'))
Hence the multiple values for keyword argument 'current_term' error message.

Related

Using value from URL in Form Wizard for Django

I'm trying to use this Form Wizard to design a multipage form in Django.
I need to catch a value from the URL, which is a client's ID, and pass it to one of the Forms instance, as the form will be built dynamically with specific values for that client.
I have tried redefining the method get_form_kwargs based on this thread, but this isn't working for me.
I have the following code in my views.py:
class NewScanWizard(CookieWizardView):
def done(self, form_list, **kwargs):
#some code
def get_form_kwargs(self, step):
kwargs = super(NewScanWizard, self).get_form_kwargs(step)
if step == '1': #I just need client_id for step 1
kwargs['client_id'] = self.kwargs['client_id']
return kwargs
Then, this is the code in forms.py:
from django import forms
from clients.models import KnownHosts
from bson import ObjectId
class SetNameForm(forms.Form): #Form for step 0
name = forms.CharField()
class SetScopeForm(forms.Form): #Form for step 1, this is where I need to get client_id
def __init__(self, *args, **kwargs):
super(SetScopeForm, self).__init__(*args, **kwargs)
client_id = kwargs['client_id']
clientHosts = KnownHosts.objects.filter(_id=ObjectId(client_id))
if clientHosts:
for h in clientHosts.hosts:
#some code to build the form
When running this code, step 0 works perfectly. However, when submitting part 0 and getting part 1, I get the following error:
_init_() got an unexpected keyword argument 'client_id'
I've done some debugging and I can see that the value for client_id is binding correctly to kwargs, but I have no clue on how to solve this problem. I think this might not be difficult to fix, but I'm quite new to Python and don't get which the problem is.
You should delete cliend_id from kwargs before calling super(SetScopeForm, self).__init__(*args, **kwargs).
To delete client_id you can use kwargs.pop('client_id', None):
class SetScopeForm(forms.Form): #Form for step 1, this is where I need to get client_id
def __init__(self, *args, **kwargs):
# POP CLIENT_ID BEFORE calling super SetScopeForm
client_id = kwargs.pop('client_id', None)
# call super
super(SetScopeForm, self).__init__(*args, **kwargs)
clientHosts = KnownHosts.objects.filter(_id=ObjectId(client_id))
if clientHosts:
for h in clientHosts.hosts:
#some code to build the form

missing 1 required positional argument: 'request'

here is my view.py
class categAdmin(admin.ModelAdmin):
change_form_template = 'category_forms.html'
list_display = ['title']
model = Category
fields = ['status','title','category_post','body', 'photo',
'url','slider','Gallery','lists','pk_tree','video','maps']
# def render_change_form(self, request, context, **kwargs):
# post = Post.objects.all()
# context['eve'] = post
# return super(categAdmin,self).render_change_form(request, context, **kwargs)
def item_add(request, self, post_id):
tree = post_id
return self.add_view(request, extra_context={'tree': tree})
i am getting error item_add() missing 1 required positional argument: 'request'
You need to swap self and request parameters.
def item_add(self, request, post_id):
tree = post_id
...
Always remember that methods are bound to objects and whenever you call methods, python implicitly passes the self argument(the object on which the method is being called) to the method call, In your example:
class CategAdmin:
def item_add(self, request, post_id):
pass
would be the signature format, note that the self object is the first parameter in the method signature. So when you do
categoryAdmin = CategAdmin()
categoryAdmin.item_add(request,123)
this is what will be called by the python interpreter CategAdmin.item_add(categoryAdmin,request,123)
One more feedback would be to improve your coding style, i.e follow some conventions like always start class names with Capital letter, give meaningful names to class and methods and variables.
This makes your code more readable and via this debugging will be way faster.
Cheers!

How to filter model choice field options based on user in django?

Here is my form:
class RecipeForm(forms.Form):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(RecipeForm, self).__init__(*args, **kwargs)
Recipebase_id = forms.ModelChoiceField(queryset=Recipebase.objects.filter(user = self.user))
title = forms.CharField(max_length=500)
instructions = forms.CharField(max_length=500)
I want to filter model choice field based on user as you can see from the filter. But it gives the following error:
name 'self' is not defined
Any suggestions would be highly appreciated.
The self. would work only for objects created from a class. In this case you are not creating one, so it would not work as you would expect.
Instead, you need to override the queryset in the __init__ like this:
class RecipeForm(forms.Form):
Recipebase_id = forms.ModelChoiceField(queryset=Recipebase.objects.none())
def __init__(self, *args, **kwargs):
user = kwargs.pop('user') #Throws an error if user is not present
super(RecipeForm, self).__init__(*args, **kwargs)
qs = Recipebase.objects.filter(user=user)
self.fields['Recipebase_id'].queryset = qs
Another way to achieve the same is to make user a required argument in the form
class RecipeForm(forms.Form):
Recipebase_id = forms.ModelChoiceField(queryset=Recipebase.objects.none())
def __init__(self, user, *args, **kwargs):
super(RecipeForm, self).__init__(*args, **kwargs)
qs = Recipebase.objects.filter(user=user)
self.fields['Recipebase_id'].queryset = qs
And the view code would look like this:
form = RecipeForm(request.POST, user=request.user) #user would be passed in as a kwarg to the form class.
Putting your code starting at "Recipebase_id" at the indentation level you have it causes python to execute it at the time the file is parsed/imported. Self is passed into a method when the class is instantiated and the instance method is called, so at parse time self does not exist.
It's unclear to me if you want the Recipebase_id, title and instructions set in the init method. If you do, indent them to the same level as the lines above it. If not, then you'll need to get the value of user from somewhere other than self.

Primary keys as choices in MultipleChoiceField

The thing is quite obvious to my mind, still I can get it working.
Previously I tried to get the filtered model instances from MultipleModelChoiceField by overriding the __init__ method and it worked as expected. Now I need to get only pk from those instances and I decided to do it in MultipleChoiceField. I try to do it the following way but do not succeed:
class AnswerForm(forms.Form):
answers = forms.MultipleChoiceField(
choices = [answer.pk for answer in Answer.objects.all()],
widget = forms.CheckboxSelectMultiple,
)
def __init__(self, *args, **kwargs):
q_pk = kwargs.pop('q_pk')
super(AnswerForm, self).__init__(*args, **kwargs)
self.fields['answers'].choices = [answer.pk for answer in Answer.objects.filter(question__pk=q_pk)]
In a nutshell: don't do this, stick with ModelMultipleChoiceField.
It obviously won't work because choices expects a list of tuples. Taking that in account, [answer.pk for answer in Answer.objects.filter(question__pk=q_pk)] can be rewritten like Answer.objects.filter(question__pk=q_pk).values_list('pk', 'someotherfield'), which brings you back to what ModelMultipleChoiceField does.
Many thanks to Ivan for his pointing me at using ModelChoiceField.
It is my inattention, since I only now figured out that I need some other model fields (except pk) to be passed to the form as well.
In that case the best way, that I found to get the model primary key as a value of a chosen input(s) is to get the entire models from form first and then iterate them to get the desired field value as follows:
forms.py
class AnswerForm(forms.Form):
answer = forms.ModelMultipleChoiceField(
queryset = Answer.objects.all(),
widget = forms.CheckboxSelectMultiple,
)
def __init__(self, *args, **kwargs):
q_pk = kwargs.pop('q_pk', None)
super(AnswerForm, self).__init__(*args, **kwargs)
self.fields['answer'].queryset = Answer.objects.filter(question__pk=q_pk)
views.py
checked = [answer.pk for answer in form.cleaned_data['answer']]

Updateview with dynamic form_class

I would like to dynamically change the form_class of an UpdateView CBV in Django 1.6.
I've tried to do this using the get_context_data(), but that didn't help since the form is already initialized. So it will need to happen during __init__, I guess.
Here's what I've tried on __init__:
class UpdatePersonView(generic.UpdateView):
model = Person
form_class = ""
def __init__(self, *args, **kwargs):
super(UpdatePersonView, self).__init__(*args, **kwargs)
person = Person.objects.get(id=self.get_object().id)
if not person.somefield:
self.form_class = OneFormClass
elif person.somefield:
self.form_class = SomeOtherFormClass
I'm stuck with a 'UpdatePersonView' object has no attribute 'kwargs' error message when executing person = Person.objects.get(id=self.get_object().id).
When manually specifying the id (e.g. id=9), then the setup works.
How can I get the args/kwargs inside the init method that I'm overriding? Particularly I would need access to the pk.
You should simply override get_form_class.
(Also I'm not sure why you're querying for person: that object is the same is self.get_object() already, so there's no point getting the ID of that then querying again.)
def get_form_class(self):
if self.object.somefield:
return OneFormClass
else:
return SomeOtherFormClass

Categories