django forms error_class - django

Is there a way to give a form a special error rendering function in the form definition? In the docs under customizing-the-error-list-format it shows how you can give a form a special error rendering function, but it seems like you have to declare it when you instantiate the form, not when you define it.
So you can define some ErrorList class like:
from django.forms.util import ErrorList
class DivErrorList(ErrorList):
def __unicode__(self):
return self.as_divs()
def as_divs(self):
if not self: return u''
return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % e for e in self])
And then when you instantiate your form you can instantiate it with that error_class:
f = ContactForm(data, auto_id=False, error_class=DivErrorList)
f.as_p()
<div class="errorlist"><div class="error">This field is required.</div></div>
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div>
<p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
But I don't want to name the error class every time I instantiate a form, is there a way to just define the custom error renderer inside the form definition?

If you want this behaviour to be common to all your forms, you could have your own form base class defined like that :
class MyBaseForm(forms.Form):
def __init__(self, *args, **kwargs):
kwargs_new = {'error_class': DivErrorList}
kwargs_new.update(kwargs)
super(MyBaseForm, self).__init__(self, *args, **kwargs_new)
And then have all your form subclass that one. Then all your form will have DivErrorList as a default error renderer, and you will still be able to change it using the error_class argument.
For ModelForm's:
class MyBaseModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
kwargs_new = {'error_class': DivErrorList}
kwargs_new.update(kwargs)
super(MyBaseModelForm, self).__init__(*args, **kwargs_new)

Try the following:
class MyForm(forms.Form):
...
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.error_class = DivErrorList
Should work. But I did not test it.

Related

Django boundfields auto naming: changing the form part

I can't seem to find if/how this is possible. But say I have a form:
class Detform(ModelForm):
class Meta:
model = Ap_detcmd
fields = ["foo"]
Formset = inlineformset_factory(ParentModel, ChildModel,
form=Detform,
can_delete=False,
extra=0)
Then in the template this gets renders, for instance in the management form (or any field):
<input type="hidden" name="ap_detcmd-TOTAL_FORMS" value="0" id="id_ap_detcmd-TOTAL_FORMS">
Since the model of the form is "Ap_detcmd", then I get #id_ap_detcmd-.... as a prefix for all fields.
Is there a way to specify that prefix?
Okay, so in short:
Subclass BaseInlineFormset
add {"prefix":"foo"} to the kwargs in init & pass that on
Magic
For instance:
class MyBaseInlineFormset(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
kwargs["prefix"] = "foo"
super().__init__(*args, **kwargs)
Then your inlineformset declaration be like:
DetPoFormset = inlineformset_factory(Ap_entcmd, Ap_detcmd, form=Detform, formset=MyBaseInlineFormset, can_delete=True, extra=0)
Then your management form input (id_XXX-TOTAL_FORMS etc.) will be like:
<input type="hidden" name="foo-TOTAL_FORMS" value="0" id="id_foo-TOTAL_FORMS">
as well as all the tags in the formset.

Populate select with initial data

I'm trying to populate in my template a <select> element with data from a form field like this:
forms.py
class CreatePlayerForm(forms.Form):
size = forms.CharField()
views.py
class CreatePlayer(FormView):
...
def get(self, request, *args, **kwargs):
...
boots = Boots.objects.filter(...).values_list('size', flat=True) # return a list
form.initial['boots'] = boots
template
<select id="leftValues4" size="5" multiple>
{% for boot in form.boots %}
<option>{{ boot }}</option>
{% endfor %}
</select>
With the above code I don't get any results.
Any suggestion?
You are not approaching this in the right way at all.
initial is specifically for pre-setting the chosen value. You are trying to populate the list of values. For that, you will need to use an actual field that supports such a list; and that is a ChoiceField, not a CharField.
Secondly, choices need to have an ID value as well as a display value; so you need a list/tuple of 2-tuples.
form:
class CreatePlayerForm(forms.Form):
size = forms.ChoiceField(choices=[])
def __init__(self, *args, **kwargs):
sizes = kwargs.pop('sizes')
super(CreatePlayerForm, self).__init__(*args, **kwargs)
self.fields['sizes'].choices = sizes
view:
class CreatePlayer(FormView):
...
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(CreatePlayer, self).get_form_kwargs(*args, **kwargs)
boots = Boots.objects.filter(...).values_list('id', 'size')
form_kwargs['sizes'] = boots
return form_kwargs
template:
{{ form.boots }}

Create controls-row in django-crispy-forms

Using django-crispy-forms I want to combine two (or more) widgets on a single row. See also the attached example. I think it should be possible using that library, although the documentation does include examples on this issue and the source code didn't help either. So has anyone managed to get similar results using django-crispy-forms?
The HTML required for such a form looks like this:
<div class="control-group">
<label for="desc" class="control-label">
Description
</label>
<div class="controls controls-row">
<input class="span2" maxlength="255" type="text" id="desc">
<input class="span3" maxlength="255" type="text">
</div>
</div>
Yes. Use Layouts
Here's an example snippet to get you going.
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = 'get'
self.helper.form_id = 'id_search'
self.helper.layout = Layout(
Div(
Div(Field('arrival_date', css_class='span12 input-large calendar',
placeholder='dd/mm/yyyy'), css_class='span5'),
Div(Field('departure_date', css_class='span12 input-large calendar',
placeholder='dd/mm/yyyy'), css_class='span5'),
Div(Field('rooms', css_class='span12'), css_class='span2 styled-rooms'),
css_class='row-fluid',
),
)
You could do it with defining HTML object in `def init(self, *args, **kwargs): in layout object. This HTML will give you all the freedom you want for your needs.
There is another way too. To actually make your own template in in your templates folder and then in your layout object define the path to that template
Example:
Field('name', template='path/to/template/single_line_input.html'),
Where 'name' is actually a widget.
Of course you could always try to do it with playing around in the CSS files for your app, something like float: left or display: inline might help, but you would have to define the classes on the widgets which should be displayed inline. This could be a bit tricky for someone who is not skilled in frontend CSS (for result could be various in different browsers and on different resolutions, so needless to say some testing is needed).
The problem you have is mainly the fact that browsers are rendering input fields as block objects by default, so they take up all the space in one line even if their width is less.
You can use MultiValueField and MultiWidget to get this behavior. You'll need to think about and modify the compress and decompress methods, but something like the following should get you part way there.
from django import forms
from crispy_forms.helper import FormHelper
class MyWidget(forms.MultiWidget):
widgets = (forms.TextInput(), forms.TextInput())
super(ExpirationDateWidget, self).__init__(widgets)
def decompress(self, value):
if value:
return value.split('|') # or however you combined the two values
return [None, None]
class MyMultiField(forms.MultiValueField):
def __init__(self, *args, **kwargs):
self.widget = MyWidget(*args, **kwargs)
def compress(self, data_list):
return data_list[0] + '|' + data_list[1] # or other logical compression
class MyForm(forms.Form):
multi_field = MyMultiField()
# other fields like category and category_new
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.layout = Layout('multi_field', 'category', 'category_new')
This will result in something like:
<div id="div_id_multi_field" class="control-group">
<label for="id_multi_field_0" class="control-label">Multi Field</label>
<div class="controls">
<input id="id_multi_field_0">
<input id="id_multi_field_1">
</div>
</div>

Django: how to submit form whes user click radiobutton

How to add:
onclick="this.form.submit();"
in my radiobutton form? I would like to post my form when user click to the radiobutton.
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.news = kwargs.pop('news')
super(MyForm, self).__init__(*args, **kwargs)
choices = ([ ("%s" % a.id, "%s" % a.text) for a in self.news])
self.fields['new'] = forms.ChoiceField(choices = choices, widget=forms.RadioSelect())
I would like to have this result in template:
<input type="radio" id="new" name="new" value="new" onclick="this.form.submit();">
self.fields['new'] = forms.ChoiceField(choices = choices, widget=forms.RadioSelect(attrs={'onclick': 'this.form.submit();'}))
while it's not the best idea to place template logic in your .py files.
How do you generate your form in a template?
If you use {{ form.as_p }} than consider rendering your custom form like described in: Django's Custom Forms

Django form multiple select box size in template

I have a template:
...
<form action="/reportform/" method="post">
<p><label>Aircraft system:</label>
<br>{{ Querry.system }}
...
it looks like this
How can I set a Size option for this box? for example, 10.
Use the attrs attribute to define the size.
class MyForm(forms.Form):
system = forms.ChoiceField(choices=SYSTEM_CHOICES,
widget=forms.SelectMultiple(attrs={'size':'40'}))
Sometimes, it is useful to override the widget in the forms init method.
class MyForm(forms.Form):
<snip>
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['system'].widget = forms.SelectMultiple(attrs={'size':'40'}))