Subclassed form doesn't render customized input field in Django - django

I have a subclassed form which I want to render a custom max attribute on the input field. However, it doesn't return any form instance in the view and thus doesn't render any input field either:
# views.py
..
custom_max_attribute = qs_aggregates['total_equity']
print(custom_max_attribute) # prints 4000
withdraw_form = WithdrawForm(custom_max_attribute)
print(withdraw_form) # prints nothing
..
# template
..
<div class="field-wrapper">
<div class="field-title">Set your Amount</div>
<div class="withdraw-input-field">{{ withdraw_form.withdraw_amount }}</div> <!-- isnt rendered -->
</div>
..
# forms.py
class WithdrawForm(forms.Form):
"""
A form to withdraw available funds from Farena to the users bank account
"""
def __init__(self, custom_max_attribute, *args, **kwargs):
super().__init__(self, custom_max_attribute, *args, **kwargs)
withdraw_amount = forms.FloatField(widget=forms.NumberInput(attrs={'min': 1, 'max': custom_max_attribute, 'step': 0.01}))

This is a workaround, works for me but I see that you are working with withdrawals so you may need to implement some additional validation on form submission.
Basically. You can set custom attributes in JS, add class to the input in forms.py like this
widgets ={
'name' :forms.TextInput(attrs={'class':'shippForm'}),
'withdraw':forms.NumberInput(attrs={'class':'shippForm'}),
}
and then you can just
var form_fields = document.getElementsByClassName('shippForm')
form_fields[0].placeholder='{% trans "Name" %}';
form_fields[1].max='2115';
the max (2115) can be taken from context/database etc so you can check if user did some frontend shenanigans

Related

how can i add Django Filter in one line by removing the label and making it placeholder?

this is my filters.py
class StudentFilterSel(django_filters.FilterSet):
class Meta:
model=Student
fields=['registrationSession','registrationNumber','studentName','applyingForClass','studentDob','studentAadharNum','fathersName','fathersContactNumber']
this is my views.py
def selectedList(request):
Studentss=Student.objects.filter(AdmissionStatus='Selected')
myFilter=StudentFilterSel(request.GET,queryset=Studentss)
Studentss=myFilter.qs
return render(request,"register/selectedList.html",{'myfilter':myFilter,"Studentss":Studentss})
this is my HTML file
<form method='get'>
{{myfilter.form}}
<button type="submit">search</button>
</form>
I need a lot of filters in my table but they are getting distorted and getting in multiple lines how can I remove the label and change it to the placeholder to reduce space?
You can customise the widget, like you would in a form. The following would work on a text input field:
studentName = django_filters.CharFilter(label='',
lookup_expr='icontains',
widget=forms.widgets.TextInput(attrs={'placeholder':'Student Name'}))
Or you could do it in the the filter's init. The following would work for a choice field:
def __init__(self, *args, **kwargs):
super(StudentFilterSel, self).__init__(*args, **kwargs)
self.filters['registrationSession'].label = ''
self.filters['registrationSession'].extra['empty_label'] = "Registration"

Initialize django CheckboxSelectMultiple with everything unchecked?

I have a model form that is rendering a ModelMultipleChoiceField as a CheckboxSelectMultiple
class VisitForm(ModelForm):
def __init__(self, queryset=None, *args, **kwargs):
super(VisitForm, self).__init__(*args, **kwargs)
if queryset:
self.fields['students'] = forms.ModelMultipleChoiceField(
queryset=queryset,
widget=forms.CheckboxSelectMultiple()
)
It's rendered very simply in the template right now:
<div class="form-group">
{{field.label}}
{{field}}
</div>
It initializes okay with the selections I expect to be there - but by default both options are checked
I am trying to figure out how to intialize the boxes so they are unchecked by default.
What is the best way to accomplish this?
It's initialized in the view like this:
visitor = Visitor.objects.get(unique_id=unique_id)
students = Student.objects.filter(parents__unique_id=unique_id)
form = VisitForm(initial={'visitor':visitor, 'students':students}, queryset=students)
You are passing the students queryset as the initial data, so Django selects every student in the queryset.
You could use an empty queryset Student.objects.none(), but it is even easier to remove students from the initial dictionary:
form = VisitForm(initial={'visitor':visitor}, queryset=students)

add field to a form if necessary django

I am trying to make a form which has an option of adding multiple objects at a time. I want it to have a button "add another" - when clicked a new form field would appear for adding additional object. If there was an previous not submitted input I want the form to keep it. Is it possible to use templates tags for this(i.e. django template tags and not javascript)?
Your form would have to be constructed based on some variables passed to it from your POST (or blindly check for attributes). The form itself is constructed every time the view is reloaded, errors or not, so the HTML needs to contain information about how many fields there are to construct the correct amount of fields for validation.
I'd look at this problem the way FormSets work: there is a hidden field that contains the number of forms active, and each form name is prepended with the form index. In fact, you could make a one field FormSet.
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#formsets
Here's one made from scratch - it should give you some ideas. It also answers your questions about passing arguments to __init__ - you just pass arguments to an objects constructor: MyForm('arg1', 'arg2', kwarg1='keyword arg')
### Views
class MyForm(forms.Form):
original_field = forms.CharField()
extra_field_count = forms.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
extra_fields = kwargs.pop('extra', 0)
super(MyForm, self).__init__(*args, **kwargs)
self.fields['extra_field_count'].initial = extra_fields
for index in range(int(extra_fields)):
# generate extra fields in the number specified via extra_fields
self.fields['extra_field_{index}'.format(index=index)] = \
forms.CharField()
def myview(request):
if request.method == 'POST':
form = MyForm(request.POST, extra=request.POST.get('extra_field_count')
if form.is_valid():
print "valid!"
else:
form = MyForm()
return render(request, "template", { 'form': form })
### HTML
form_count = Number($("[name=extra_field_count]").val());
// get extra form count so we know what index to use for the next item.
$("#add-another").click(function() {
form_count ++;
element = $('<input type="text"/>');
element.attr('name', 'extra_field_' + form_count);
$("#forms").append(element);
// build element and append it to our forms container
$("[name=extra_field_count]").val(form_count);
// increment form count so our view knows to populate
// that many fields for validation
})
<form>
<div id="forms">
{{ form.as_p }}
</div>
<button id="add-another">add another</button>
<input type="submit" />
</form>

django dynamic data driven form fields [duplicate]

I'm working on something like an online store. I'm making a form in which the customer buys an item, and she can choose how many of these item she would like to buy. But, on every item that she buys she needs to choose what its color would be. So there's a non-constant number of fields: If the customer buys 3 items, she should get 3 <select> boxes for choosing a color, if she buys 7 items, she should get 7 such <select> boxes.
I'll make the HTML form fields appear and disappear using JavaScript. But how do I deal with this on my Django form class? I see that form fields are class attributes, so I don't know how to deal with the fact that some form instance should have 3 color fields and some 7.
Any clue?
Jacob Kaplan-Moss has an extensive writeup on dynamic form fields:
http://jacobian.org/writing/dynamic-form-generation/
Essentially, you add more items to the form's self.fields dictionary during instantiation.
Here's another option: how about a formset?
Since your fields are all the same, that's precisely what formsets are used for.
The django admin uses FormSets + a bit of javascript to add arbitrary length inlines.
class ColorForm(forms.Form):
color = forms.ChoiceField(choices=(('blue', 'Blue'), ('red', 'Red')))
ColorFormSet = formset_factory(ColorForm, extra=0)
# we'll dynamically create the elements, no need for any forms
def myview(request):
if request.method == "POST":
formset = ColorFormSet(request.POST)
for form in formset.forms:
print "You've picked {0}".format(form.cleaned_data['color'])
else:
formset = ColorFormSet()
return render(request, 'template', {'formset': formset}))
JavaScript
<script>
$(function() {
// this is on click event just to demo.
// You would probably run this at page load or quantity change.
$("#generate_forms").click(function() {
// update total form count
quantity = $("[name=quantity]").val();
$("[name=form-TOTAL_FORMS]").val(quantity);
// copy the template and replace prefixes with the correct index
for (i=0;i<quantity;i++) {
// Note: Must use global replace here
html = $("#form_template").clone().html().replace(/__prefix_/g', i);
$("#forms").append(html);
};
})
})
</script>
Template
<form method="post">
{{ formset.management_form }}
<div style="display:none;" id="form_template">
{{ formset.empty_form.as_p }}
</div><!-- stores empty form for javascript -->
<div id="forms"></div><!-- where the generated forms go -->
</form>
<input type="text" name="quantity" value="6" />
<input type="submit" id="generate_forms" value="Generate Forms" />
you can do it like
def __init__(self, n, *args, **kwargs):
super(your_form, self).__init__(*args, **kwargs)
for i in range(0, n):
self.fields["field_name %d" % i] = forms.CharField()
and when you create form instance, you just do
forms = your_form(n)
it's just the basic idea, you can change the code to whatever your want. :D
The way I would do it is the following:
Create an "empty" class that inherits from froms.Form, like this:
class ItemsForm(forms.Form):
pass
Construct a dictionary of forms objects being the actual forms, whose composition would be dependent on the context (e.g. you can import them from an external module). For example:
new_fields = {
'milk' : forms.IntegerField(),
'butter': forms.IntegerField(),
'honey' : forms.IntegerField(),
'eggs' : forms.IntegerField()}
In views, you can use python native "type" function to dynamically generate a Form class with variable number of fields.
DynamicItemsForm = type('DynamicItemsForm', (ItemsForm,), new_fields)
Pass the content to the form and render it in the template:
Form = DynamicItemsForm(content)
context['my_form'] = Form
return render(request, "demo/dynamic.html", context)
The "content" is a dictionary of field values (e.g. even request.POST would do).
You can see my whole example explained here.
Another approach: Rather than breaking the normal field initialization flow, we can override fields with a mixin, return an OrderedDict of dynamic fields in generate_dynamic_fields which will be added whenever its set.
from collections import OrderedDict
class DynamicFormMixin:
_fields: OrderedDict = None
#property
def fields(self):
return self._fields
#fields.setter
def fields(self, value):
self._fields = value
self._fields.update(self.generate_dynamic_fields())
def generate_dynamic_fields(self):
return OrderedDict()
A simple example:
class ExampleForm(DynamicFormMixin, forms.Form):
instance = None
def __init__(self, instance = None, data=None, files=None, auto_id='id_%s', prefix=None, initial=None,
error_class=ErrorList, label_suffix=None, empty_permitted=False, field_order=None,
use_required_attribute=None, renderer=None):
self.instance = instance
super().__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, field_order,
use_required_attribute, renderer)
def generate_dynamic_fields(self):
dynamic_fields = OrderedDict()
instance = self.instance
dynamic_fields["dynamic_choices"] = forms.ChoiceField(label=_("Number of choices"),
choices=[(str(x), str(x)) for x in range(1, instance.number_of_choices + 1)],
initial=instance.initial_choice)
return dynamic_fields

django - dynamic form fieldsets

A form will be spitting out an unknown number of questions to be answered. each question contains a prompt, a value field, and a unit field. The form is built at runtime in the formclass's init method.
edit: each questions receives a unique prompt to be used as a label, as well as a unique list of units for the select element.
this seems a case perfect for iterable form fieldsets, which could be easily styled. but since fieldsets - such as those in django-form-utils are defined as tuples, they are immutable... and I can't find a way to define them at runtime. is this possible, or perhaps another solution?
Edit:
formsets with initial_data is not the answer - initial_data merely enables the setting of default values for the form fields in a formset. a list of items can't be sent to the choicefield constructor by way of initial_data.
...unless I'm wrong.
Check out formsets. You should be able to pass in the data for each of the N questions as initial data. Something along these lines:
question_data = []
for question in your_question_list:
question_data.append({'prompt': question.prompt,
'value': question.value,
'units': question.units})
QuestionFormSet = formset_factory(QuestionForm, extra=2)
formset = QuestionFormSet(initial=question_data)
Old question but I am running into a similar problem. The closest thing that I have found so far is this snippet based of a post that Malcom did a couple years ago now.
http://djangosnippets.org/snippets/1955/
The original snippet did not address the template side and splitting them up into fieldsets, but adding each form to its own fieldset should accomplish that.
forms.py
from django.forms.formsets import Form, BaseFormSet, formset_factory, \
ValidationError
class QuestionForm(Form):
"""Form for a single question on a quiz"""
def __init__(self, *args, **kwargs):
# CODE TRICK #1
# pass in a question from the formset
# use the question to build the form
# pop removes from dict, so we don't pass to the parent
self.question = kwargs.pop('question')
super(QuestionForm, self).__init__(*args, **kwargs)
# CODE TRICK #2
# add a non-declared field to fields
# use an order_by clause if you care about order
self.answers = self.question.answer_set.all(
).order_by('id')
self.fields['answers'] = forms.ModelChoiceField(
queryset=self.answers())
class BaseQuizFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
# CODE TRICK #3 - same as #1:
# pass in a valid quiz object from the view
# pop removes arg, so we don't pass to the parent
self.quiz = kwargs.pop('quiz')
# CODE TRICK #4
# set length of extras based on query
# each question will fill one 'extra' slot
# use an order_by clause if you care about order
self.questions = self.quiz.question_set.all().order_by('id')
self.extra = len(self.questions)
if not self.extra:
raise Http404('Badly configured quiz has no questions.')
# call the parent constructor to finish __init__
super(BaseQuizFormSet, self).__init__(*args, **kwargs)
def _construct_form(self, index, **kwargs):
# CODE TRICK #5
# know that _construct_form is where forms get added
# we can take advantage of this fact to add our forms
# add custom kwargs, using the index to retrieve a question
# kwargs will be passed to our form class
kwargs['question'] = self.questions[index]
return super(BaseQuizFormSet, self)._construct_form(index, **kwargs)
QuizFormSet = formset_factory(
QuestionForm, formset=BaseQuizDynamicFormSet)
views.py
from django.http import Http404
def quiz_form(request, quiz_id):
try:
quiz = Quiz.objects.get(pk=quiz_id)
except Quiz.DoesNotExist:
return Http404('Invalid quiz id.')
if request.method == 'POST':
formset = QuizFormSet(quiz=quiz, data=request.POST)
answers = []
if formset.is_valid():
for form in formset.forms:
answers.append(str(int(form.is_correct())))
return HttpResponseRedirect('%s?a=%s'
% (reverse('result-display',args=[quiz_id]), ''.join(answers)))
else:
formset = QuizFormSet(quiz=quiz)
return render_to_response('quiz.html', locals())
template
{% for form in formset.forms %}
<fieldset>{{ form }}</fieldset>
{% endfor %}
I used the trick below to create a dynamic formset. Call the create_dynamic_formset() function from your view.
def create_dynamic_formset(name_filter):
"""
-Need to create the classess dynamically since there is no other way to filter
"""
class FormWithFilteredField(forms.ModelForm):
type = forms.ModelChoiceField(queryset=SomeType.objects.filter(name__icontains=name_filter))
class Meta:
model=SomeModelClass
return modelformset_factory(SomeModelClass, form=FormWithFilteredField)
Here is what I used for a similar case (a variable set of fieldsets, each one containing a variable set of fields).
I used the type() function to build my Form Class, and BetterBaseForm class from django-form-utils.
def makeFurnitureForm():
"""makeFurnitureForm() function will generate a form with
QuantityFurnitureFields."""
furnitures = Furniture.objects.all()
fieldsets = {}
fields = {}
for obj in furnitures:
# I used a custom Form Field, but you can use whatever you want.
field = QuantityFurnitureField(name = obj.name)
fields[obj.name] = field
if not obj.room in fieldsets.keys():
fieldsets[obj.room] = [field,]
else:
fieldsets[obj.room].append(field)
# Here I use a double list comprehension to define my fieldsets
# and the fields within.
# First item of each tuple is the fieldset name.
# Second item of each tuple is a dictionnary containing :
# -The names of the fields. (I used a list comprehension for this)
# -The legend of the fieldset.
# You also can add other meta attributes, like "description" or "classes",
# see the documentation for further informations.
# I added an example of output to show what the dic variable
# I create may look like.
dic = [(name, {"fields": [field.name for field in fieldsets[name]], "legend" : name})
for name in fieldsets.keys()]
print(dic)
# Here I return a class object that is my form class.
# It inherits from both forms.BaseForm and forms_utils.forms.BetterBaseForm.
return (type("FurnitureForm",
(forms.BaseForm, form_utils.forms.BetterBaseForm,),
{"_fieldsets" : dic, "base_fields" : fields,
"_fieldset_collection" : None, '_row_attrs' : {}}))
Here is an example of how dic may look like :
[('fieldset name 1',
{'legend': 'fieldset legend 2',
'fields' ['field name 1-1']}),
('fieldset name 2',
{'legend': 'fieldset legend 2',
'fields' : ['field 1-1', 'field 1-2']})]
I used BetterBaseForm rather than BetterForm for the same reason this article suggests to use BaseForm rather than Form.
This article is interesting even if it's old, and explains how to do dynamic forms (with variable set of fields). It also gives other ways to achieve dynamic forms.
It doesn't explain how to do it with fieldsets though, but it inspired me to find how to do it, and the principle remains the same.
Using it in a view is pretty simple :
return (render(request,'main/form-template.html', {"form" : (makeFurnitureForm())()}))
and in a template :
<form method="POST" name="myform" action=".">
{% csrf_token %}
<div>
{% for fieldset in form.fieldsets %}
<fieldset>
<legend>{{ fieldset.legend }}</legend>
{% for field in fieldset %}
<div>
{% include "main/furniturefieldtemplate.html" with field=field %}
</div>
{% endfor %}
</fieldset>
{% endfor %}
</div>
<input type="submit" value="Submit"/>
</form>