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>
Related
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"
I have a django Formset that I'd like to layout in the middle of another form. I'm using django-crispy-forms to set the layout in the parent form's __init__:
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout, Field, Div
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.layout = Layout(
Div(
Div(Field('foo'), css_class='span3'),
Div(Field('bar'), css_class='span4'),
css_class='row'
),
Field('baz', css_class='span1'),
...
)
self.helper.add_input(Submit('submit', 'Submit', css_class='btn btn-primary offset4'))
My template simply renders the form using the {% crispy %} tag.
I'd like to know how I should incorporate the formset. Should I instantiate it in the above init function? How do I refer to it there?
There are other examples of form and formset combos online that have one render after the other serially, but I'm wondering whether I can have more control over how they fit together with crispy's layout.
I solved this without modifying Crispy Forms, by creating a new field type that renders a formset:
from crispy_forms.layout import LayoutObject, TEMPLATE_PACK
class Formset(LayoutObject):
"""
Layout object. It renders an entire formset, as though it were a Field.
Example::
Formset("attached_files_formset")
"""
template = "%s/formset.html" % TEMPLATE_PACK
def __init__(self, formset_name_in_context, template=None):
self.formset_name_in_context = formset_name_in_context
# crispy_forms/layout.py:302 requires us to have a fields property
self.fields = []
# Overrides class variable with an instance level variable
if template:
self.template = template
def render(self, form, form_style, context, template_pack=TEMPLATE_PACK):
formset = context[self.formset_name_in_context]
return render_to_string(self.template, Context({'wrapper': self,
'formset': formset}))
It needs a template to render the formset, which gives you control over exactly how it's rendered:
{% load crispy_forms_tags %}
<div class="formset">
{% crispy formset %}
<input type="button" name="add" value="Add another" />
</div>
You can use it to embed a formset in your layouts just like any other Crispy layout element:
self.helper.layout = Layout(
MultiField(
"Education",
Formset('education'),
),
A slight modification to the earlier answer by qris.
This update (as suggested by Alejandro) will allow for our custom Formset Layout Object to use a FormHelper object to control how the formset's fields are rendered.
from crispy_forms.layout import LayoutObject
from django.template.loader import render_to_string
class Formset(LayoutObject):
"""
Renders an entire formset, as though it were a Field.
Accepts the names (as a string) of formset and helper as they
are defined in the context
Examples:
Formset('contact_formset')
Formset('contact_formset', 'contact_formset_helper')
"""
template = "forms/formset.html"
def __init__(self, formset_context_name, helper_context_name=None,
template=None, label=None):
self.formset_context_name = formset_context_name
self.helper_context_name = helper_context_name
# crispy_forms/layout.py:302 requires us to have a fields property
self.fields = []
# Overrides class variable with an instance level variable
if template:
self.template = template
def render(self, form, form_style, context, **kwargs):
formset = context.get(self.formset_context_name)
helper = context.get(self.helper_context_name)
# closes form prematurely if this isn't explicitly stated
if helper:
helper.form_tag = False
context.update({'formset': formset, 'helper': helper})
return render_to_string(self.template, context.flatten())
Template (used to render formset):
{% load crispy_forms_tags %}
<div class="formset">
{% if helper %}
{% crispy formset helper %}
{% else %}
{{ formset|crispy }}
{% endif %}
</div>
Now it can be used in any layout just like any other crispy forms layout object.
self.helper.layout = Layout(
Div(
Field('my_field'),
Formset('my_formset'),
Button('Add New', 'add-extra-formset-fields'),
),
)
# or with a helper
self.helper.layout = Layout(
Div(
Field('my_field'),
Formset('my_formset', 'my_formset_helper'),
Button('Add New', 'add-extra-formset-fields'),
),
)
This is currently not supported in crispy-forms. Your only option would be to use |as_crispy_field filter (not documented yet, sorry).
I have started development of this feature for {% crispy %} tag and in a feature branch, it's all explained here: https://github.com/maraujop/django-crispy-forms/issues/144
I'm looking for feedback, so if you are still interested, feel free to post.
Basing on above solution Formset(LayoutObject), you would combine django-dynamic-formset & crispy.
On my order page I have:
order's section part 1
order's inline formset with dynamic-add forms
order's section part N
Now it is simple and clear, ModelForms are:
class OrderTestForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(OrderTestForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_tag = True
self.helper.html5_required = True
self.helper.form_action = 'test_main'
self.helper.layout = Layout(
'product_norms', #section 1
'reference_other', #section 1
# rest of the section 1 fields
Formset('samples', 'helper'), # inline dynamic forms
'checkbox_is_required' # start of section N
# other order sections fields
)
self.helper.add_input(Submit("submit", "Save order"))
Formset helper layout:
class SamplesFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(SamplesFormSetHelper, self).__init__(*args, **kwargs)
self.form_method = 'post'
self.html5_required = True
self.layout = Layout(
Fieldset('',
'description',
'product', # foreign key
'DELETE', # delete django-dynamic-formset
css_class="formset_row"), # add-rows
)
self.form_tag = False
self.render_required_fields = False
Add/delete inlines, saving order with formset operations work as expected.
Does anybody know if there is a correct way to remove labels in a crispy form?
I got as far as this:
self.fields['field'].label = ""
But it's not a very nice solution.
Just do:
self.helper.form_show_labels = False
To remove all labels.
Works with Boostrap ( see documentation )
In your form :
from crispy_forms.helper import FormHelper
from django import forms
class MyForm(forms.Form):
[...]
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
In your template:
<form method='POST' action=''>{% csrf_token %}
{% crispy form %}
<input type='submit' value='Submit' class='btn btn-default'>
</form>
You could edit the field.html template:
https://github.com/maraujop/django-crispy-forms/blob/dev/crispy_forms/templates/bootstrap/field.html#L7
Add a FormHelper attribute to your form that controls the label rendering and use it in that template if. Custom FormHelper attributes are not yet officially documented, because I haven't had time, but I talked about them in a keynote I gave, here are the slides:
https://speakerdeck.com/u/maraujop/p/django-crispy-forms
The solution below lets you remove a label from both a regular or crispy control. Not only does the label text disappear, but the space used by the label is also removed so you don't end up with a blank label taking up space and messing up your layout.
The code below works in django 2.1.1.
# this class would go in forms.py
class SectionForm(forms.ModelForm):
# add a custom field for calculation if desired
txt01 = forms.CharField(required=False)
def __init__(self, *args, **kwargs):
''' remove any labels here if desired
'''
super(SectionForm, self).__init__(*args, **kwargs)
# remove the label of a non-linked/calculated field (txt01 added at top of form)
self.fields['txt01'].label = ''
# you can also remove labels of built-in model properties
self.fields['name'].label = ''
class Meta:
model = Section
fields = "__all__"
I'm not clear what the problem the OP had with the code snippet he showed, except that he wasn't putting the line of code in the right place. This seems like the best and simplest solution.
if you are only to remove some labels from input, then explicitly don't give a label name in model definition, i.e:
field = models.IntegerField("",null=True)
To Remove All Labels:
self.helper.form_show_labels = False
To Show Specific Lable when all False :
HTML('<span>Your Label</span>')
To Disable Label for Specific field when all is True
self.fields['fieldName'].label = True
Example:
Row(
HTML('<span> Upolad Government ID (Adhar/PAN/Driving Licence)</span>'),
Column('IdProof',css_class='form-group col-md-12 mb-0'),
css_class='form-row'
),
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
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.