cant pass initial values to a formset using curry - django

as per this SO question Django Passing Custom Form Parameters to Formset im tryin to use curry to pass a dictionary of initial values to my formset.
its not working, it gives me a formset of empty values, which isn't what its supposed to do.
can anyone see if im implementing this wrong?
data = {'supplier': input_data['supplier'],}
InstanceFormSet = formset_factory(BulkAddInstanceForm, extra=int(input_data['copies']))
InstanceFormSet.form = staticmethod(curry(BulkAddInstanceForm, data))
EDIT: as requested
class BulkAddInstanceForm(forms.Form):
def __init__(self, *args, **kwargs):
super(BulkAddInstanceForm, self).__init__(*args, **kwargs)
self.fields['supplier'] = forms.ModelChoiceField(
queryset=Supplier.objects.all(),
label='Supplier',
empty_label='Select a Supplier...',
required=True,
)
self.fields['syllabus'] = forms.ModelChoiceField(
queryset=Syllabus.objects.all(),
label='Syllabus',
empty_label='Select a Syllabus...',
widget=FilterWidget(
queryset=Syllabus.objects.all(),
parent_queryset=Supplier.objects.all(),
control_name='supplier',
parent_attr='supplier',
),
required=False,
)
self.fields['venue'] = forms.ModelChoiceField(
queryset=Venue.objects.all(),
label='Venue',
empty_label='Select a Venue...',
widget=FilterWidget(
queryset=Venue.objects.all(),
parent_queryset=Supplier.objects.all(),
control_name='supplier',
parent_attr='supplier',
),
required=False,
)
start_date = NiceDateField(required=False, label='Start Date')
residential = forms.BooleanField(label='Res?', required=False)
special_offers = forms.BooleanField(label='S/O?', required=False)
manual_price = forms.IntegerField(required=False)
manual_cost = forms.IntegerField(required=False)
edit2: FAO Brandon
I have looked at the docco and it suggests to do a different thing.
formset = formset_factory(BulkAddInstanceForm, extra=int(input_data['copies']))
formset = InstanceFormSet(initial=[data, ])
which creates a formset with 'copies' amount of forms, plus another one with the data in.
if i do this
formset = InstanceFormSet(initial=[data, data])
then i get two extra forms with the data in.
so my idea is i need an iterator to add 'copies' number of dictionaries and zero the initial number of forms in the formset.
not that i know how to cod that yet!!

managed to do it with the following code:
InstanceFormSet = formset_factory(BulkAddInstanceForm, extra=0)
# build the list for populating the forms
n, datalist = 0, []
while n < int(input_data['copies']):
datalist.append(data)
print datalist
n +=1
formset = InstanceFormSet(initial=datalist)
so i create the formset, then i create a list of the data dictionaries, then populate the formset with the inital data as a list.
seems to be working so far, though i've got to work out how to collect and submit the info yet .

You would need to pick up the value you're passing in during the __init__
When I've used this in the past, I've set up the parameter as such:
class MyForm(forms.Form):
def __init__(self, data, *args, **kwargs):
self.data = data
#do whatever else I need to do with data
super(MyForm, self).__init__(*args, **kwargs)
Hope that helps you out.

Related

Django combine two fields data into a queryset

I have a model say Club where we have fields like:
manager = models.ForeignKey(Users, related_name="return_manager", on_delete=models.CASCADE)
members = models.ManyToManyField(Users, related_name="return_members", blank=True)
Now I want to create a drop down in a form where I can add both the manager and members to it. I tried making two requests for Club.objects.filter(pk=mypk).members.all() and Club.objects.filter(pk=mypk).manager. I tried chain function and using '| ' operator but none worked. I think the manager is a single User and not a queryset, that is what the main problem is. Any workarounds?
One possible way getting all of the information together involves modifying your form choices.
In your view you would need to pass the choices along as context to your form.
def my_view(request, club_pk):
context = {}
context.update({
"manager": Club.objects.get(pk=club_pk).manager,
"members": Club.objects.get(pk=club_pk).members.all()
}
form = MyForm(request.POST or None, request=request, context=context)
In your form, you would need to modify the __init__ method to update your choices like so:
class MyForm(forms.Form):
all_club_members = forms.ChoiceField('Manager + Members', required=True)
def __init__(self, *args, **kwargs):
self.context = kwargs.pop('context', None)
super(MyForm, self).__init__(*args, **kwargs)
manager_tuple = [(self.context['manager'].id, self.context['manager'].display_name)]
members_tuples = [(member.id, member.display_name) for member in self.context['members']
self.fields['all_club_members'].choices = manager_tuple + members_tuples
Try this:
manager = [Club.objects.filter(pk=mypk).manager]
members = Club.objects.filter(pk=mypk).members.all()
userlist = list(manager) + list(members)
return Users.objects.filter(pk__in=userlist)
Should create a queryset of all users

Django ChoiceField initial setup not working

I have ModelForm where i use Django Forms.ChoiceField. Writing the value to the database works. But when i open the url, the dropdown list is not showing the previously selected value as selected value.
I tried setting initial=value, but it's not working as well.
class GameForm(forms.ModelForm):
gameCode = forms.ChoiceField(required=False)
def __init__(self, *args, **kwargs):
obj = AllGameCodes.objects.filter(game=game)
choices = []
choices.append(('', '-----------'))
for i in obj:
choices.append((i.code,i.description))
self.fields['gameCode'].choices = choices
in views.py,
game = games.objects.get(id=1)
form = GameForm(request.POST, initial={'code':game.code}
You must take game variable from kwargs. Also using ModelChoicefield may ease your solution
def __init__(self, *args, **kwargs):
super(GameForm, self).__init__(*args, **kwargs)
_game = kwargs.get("game"):
if _game:
self.fields['gameCode'] = ModelChoiceField(queryset=AllGameCodes.objects.filter(game=_game), required=False)
For future reference, you may use form = GameForm(instance=game) to load the form with the model data and write new data to that model.
Also instead of overwriting the form class, you can alter fields in your view
#views.py
game = games.objects.get(id=1)
form = GameForm(request.POST, instance=game)
form.fields['gameCode'].queryset = AllGameCodes.objects.filter(game=game)

pass multiple parameters to form from html table in django

I am newbie with Django and I get stucked trying to pass the value from a html table rendered with django-tables2 to a form.
view.py
def configView(request):
form = ConfigForm(request.POST or none)
if form.is_valid():
save_it = form.save(commit=False)
save_it.save()
Messages.success(request, 'Configuracion Actualizada')
return HttpResponseRedirect('/monitor/')
return render_to_response("config.html",
locals(),
context_instance=RequestContext(request))
This is my forms.py
class ConfigForm(forms.ModelForm):
class Meta:
model = Config
def __init__(self, *args, **kwargs):
super(ConfigForm, self).__init__(*args,**kwargs)
self.fields['id_proveedor'].initial = kwargs.pop('id_proveedor',None)
But I don't know how to retrieve and pass the value to theform.
I need pass the values from the cells 0, 2 and 6.
Any advice or snippet will be appreciated.
Thanks in advance
I would try this:
class ConfigForm(forms.Form):
def __init__(self, *args, **kwargs):
your_variable_to_pass = kwargs.pop("your_variable_to_pass")
super(ConfigForm, self).__init__(*args,**kwargs)
self.fields['id_proveedor']= forms.FieldClass(attribute=your_variable_to_pass)
id_proveedor = FieldClass()
where, 'FieldClass' is whatever field you choose (i.e. ChoiceField, CharField) and
attribute is the attribute to pass (your variable), i.e. 'choices', 'initial' etc.
thus, it may look like this:
self.fields['id_proveedor']= forms.ChoiceField(choices=your_variable_to_pass)
id_proveedor = ChoiceField()
Notice indentation - you assign value of the attribute to pass in the constructor!; in case of ChoiceField choices is a list of tuples, i.e. (('1', 'First',), ('2', 'Second',)); I use Forms instead of ModelForm as super or base class in this example
Then, in the views: f = ConfigFrom(request.POST, your_variable_to_pass=your_variable_to_pass)
notice your_variable_to_pass=your_variable_to_pass otherwise it'll generate a key error
I hope, it helps!

Django mutable POST doesn't appear in cleaned_data

I have been searching around for this for two days now, but I couldn't find any realiable solution.
form:
class SMSSettingsForm(forms.ModelForm):
smsQuota = forms.IntegerField(label=_("Account Quota"), max_value=432000, min_value=1, required=True, help_text=_('(mins)'), error_messages={'required': _('This field cannot be empty')})
smsTimeout = forms.IntegerField(label=_("Timeout"), max_value=9999999, min_value=1,required=False, help_text=_("(mins)"))
class Meta:
model = Settings
fields = ("smsQuota", "smsTimeout")
def __init__(self, *args, **kwargs):
super(SMSSettingsForm, self).__init__(*args, **kwargs)
def save(self):
settings = SettingsManager.get()
settings.smsQuota = self.cleaned_data['smsQuota']
settings.smsTimeout = self.cleaned_data['smsTimeout']
# Following lines are extra fields, rendered by JS in HTML
settings.ck = self.cleaned_data['ck']
settings.ck_per = self.cleand_data['ck_per']
settings.save()
view:
form_with_extra_elem = request.POST.copy()
form_with_extra_elem['ck'] = request.POST.get("ck")
form_with_extra_elem['ck_per'] = request.POST.get("ck_per")
# The two lines above didn't work, so I tried the following, but didn't work again
#form_with_extra_elem.update({'ck': request.POST.get("ck")})
#form_with_extra_elem.update({'ckper': request.POST.get("ck_per")})
form = SMSSettingsForm(form_with_extra_elem)
Do you have any idea how to solve this? What I think is the new element doesn't pass by the validation, so I cannot use them. But how to make them to do so? Actually, I don't need any validation - is there any other way than cleaned_data, to access form parameters?
I don't understand why you want to add extra fields via JS only. If you want them to appear in cleaned_data, they have to be part of the form. You can declare extra fields on a ModelForm simply by specifying them like you have with the other fields:
class SMSSettingsForm(forms.ModelForm):
smsQuota = forms.IntegerField(...)
smsTimeout = forms.IntegerField(...)
ck_per = forms.IntegerField()
ck = forms.IntegerField()

Validate a dynamic select field in Django

I'm using Django 1.4 with Python 2.7 on Ubuntu 12.10.
I have a form where I need to populate a few drop-downs dynamically (using jQuery) but need 2 of them to be required and the 3rd to be optional.
I'm using Tastypie to help with the API to get the options. Basically the first drop-down is populated with industry level codes for schools. Once a code is selected a category drop-down is populated for all categories for that code. Once the category is chosen a subcategory drop-down is populated for all subcategories for that combination of code and category.
I'm able to require the code drop-down (it's not dynamically populated). However, I'm having a tough time getting the category drop-down to be required. There are basically 2 routes I can take - front-end validation or back-end validation. I'm trying to go with back-end validation so I can easily create further validation if needed.
Here is the form:
class SchoolProductForm(forms.ModelForm):
cip_category = forms.ChoiceField(required=True,
choices=(('', '----------'),))
def __init__(self, *args, **kwargs):
super(SchoolProductForm, self).__init__(*args, **kwargs)
self.fields['short_description'].widget = TA_WIDGET
self.fields['salary_info'].widget = TA_WIDGET
self.fields['job_opportunities'].widget = TA_WIDGET
self.fields['related_careers'].widget = TA_WIDGET
self.fields['meta_keywords'].widget = TI_WIDGET
self.fields['meta_description'].widget = TI_WIDGET
self.fields['cip'].queryset = models.CIP.objects.filter(
parent_id__isnull=True)
class Meta:
model = models.SchoolProduct
exclude = ('campus',)
I've tried to override the clean method. I've tried to create a field specific clean method. Neither seem to work.
Variations of the following:
def clean(self):
super(SchoolProductForm, self).clean()
if cip_category in self._errors:
del self._errors['cip_category']
if self.cleaned_data['cip_category'] == '----------':
self._errors['cip_category'] = 'This field is required.'
return self.cleaned_data
This gives an error that there is no cip_category in cleaned_data, which makes sense because it didn't validate.
I've tried variations with the field specific clean:
def clean_cip_category(self):
data = self.cleaned_data['cip_category']
self.fields['cip_category'].choices = data
return data
But get a validation error on the page stating my choice is not one of the available choices.
I've tried to create a dynamic field type (several variations):
class DynamicChoiceField(forms.ChoiceField):
def valid_value(self, value):
return True
class SchoolProductForm(forms.ModelForm):
cip_category = DynamicChoiceField(required=True,
choices=(('', '----------'),))
But it accepts ---------- as a valid option (which I don't want) and causes an error since the ORM tries to match a value of ---------- in the database (which it won't find).
Any ideas?
I was able to solve this with a little overriding of a method in ChoiceField.
I added the field to the form and handled the pre-population with the self.initial:
class SchoolProductForm(forms.ModelForm):
cip_category = common_forms.DynamicChoiceField(
required=True, choices=(('', '----------'),))
def __init__(self, *args, **kwargs):
super(SchoolProductForm, self).__init__(*args, **kwargs)
self.fields['short_description'].widget = TA_WIDGET
self.fields['salary_info'].widget = TA_WIDGET
self.fields['job_opportunities'].widget = TA_WIDGET
self.fields['related_careers'].widget = TA_WIDGET
self.fields['meta_keywords'].widget = TI_WIDGET
self.fields['meta_description'].widget = TI_WIDGET
self.fields['cip'].queryset = models.CIP.objects.filter(
parent_id__isnull=True)
# Get the top parent and pre-populate
if 'cip' in self.initial:
self.initial['cip'] = models.CIP.objects.get(
pk=self.initial['cip']).top_parent()
class Meta:
model = models.SchoolProduct
exclude = ('campus',)
Where DynamicChoiceField is:
class DynamicChoiceField(forms.ChoiceField):
def valid_value(self, value):
return True
Then, in the view I added handling in the form_valid override:
def form_valid(self, form):
self.object = form.save(commit=False)
# Handle the CIP code
self.object.cip_id = self.request.POST.get('cip_subcategory')
if self.object.cip_id == '':
self.object.cip_id = self.request.POST.get('cip_category')
self.object.save()