I am customizing the RegistrationForm from django-registration-redux with django-crispy-forms. For that I have defined a FormHelper which is working fine:
class MyRegistrationForm(RegistrationForm):
def __init__(self, *args, **kwargs):
super(MyRegistrationForm, self).__init__(*args, **kwargs)
helper = self.helper = FormHelper()
# Moving field labels into placeholders
layout = helper.layout = Layout()
for field_name, field in self.fields.items():
layout.append(Field(field_name, placeholder=field.label))
helper.template_pack = 'bootstrap3'
helper.form_show_labels = False
My form is shown as I want: no labels, bootstrap3 and placeholders derived from label.
Now I would also like to suppress the help_text, which is coming from the Field definition. There is a somehow related flag here (help_text_inline), but that is not intended to disable the display of the help text. I can not find a flag to completely disable the display of the help text in the FormHelper documentation. Is this at all possible?
Removing the help text from the Field definition is not really an option, since I am inheriting the RegistrationForm and I do not want to modify it too much.
Related
Looking through the crispy forms I cannot find if help text is supported. im trying to add some help text to the select multiple field as per the below
Field('site_types', Title="Site Types", size="15", help_text="Hold down cmd on MacOS or ctrl on windows to select multiple"),
is this supported or would I use some other attribute to achieve this?
Thanks
Here is the working example i used to display help text
class myForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(myForm, self).__init__(*args, **kwargs)
self.fields['site_types'].help_text = "Please select bla bla bla"
Haven't used crispy forms in a little bit but I'm pretty certain you just define help_text like you would on a regular form. Looking at the docs, there are some additional configuration options for the help text if you happen to be using the Bootstrap template pack.
Rather than define help_text in crispy_forms.layout.Field, define it where you define choices (or use the solution by Pavan Kumar T S).
forms.py
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field
SITE_TYPES = [
('business', 'Business'),
('education', 'Education'),
('entertainment', 'Entertainment'),
('news', 'News'),
('other', 'Other')
]
class TestForm(forms.Form):
site_types = forms.MultipleChoiceField(
choices=SITE_TYPES,
help_text="Hold down cmd on MacOS or ctrl on windows to select multiple"
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Field('site_types', Title="Site Types", size="15")
)
Form:
Form with help text displayed below the field
I am trying to have a Django Form (using Crispy form rendering), where the displayed form fields depend on a parameter passed to the Form from the view. I believe the parameter gets passed on correctly, but nonetheless, still all form fields are shown.
The model "IndividualFeedback" contains fields that can be used for a variety of different assessment types and has 8 fields for feedback categories. How many of those are displayed should depend on the assessment type that I specify in the views. I would like the "ESSAY" assessment type to display 4 of those category fields, and the "PRESENTATION" type to display 3. Currently, I get all 8 no matter what I pass on.
Here is my forms.py:
class IndividualFeedbackForm(forms.ModelForm):
mark = forms.IntegerField() # One additional field that's not in the model
helper = FormHelper()
def __init__(self, *args, **kwargs):
marksheet_type = kwargs.pop('marksheet_type')
super(IndividualFeedbackForm, self).__init__(*args, **kwargs)
helper = FormHelper()
if marksheet_type == 'ESSAY':
print('Detects marksheet') # Just for testing - and it does...
helper.layout = Layout(
'marker',
'marking_date',
'submission_date',
Field('category_mark_1', label=CATEGORIES['ESSAY']['i-1']),
Field('category_mark_2', label=CATEGORIES['ESSAY']['i-2']),
Field('category_mark_3', label=CATEGORIES['ESSAY']['i-3']),
Field('category_mark_4', label=CATEGORIES['ESSAY']['i-4']),
'comments',
'mark'
)
elif marksheet_type == 'PRESENTATION':
...
class Meta:
model = IndividualFeedback
fields = [
'marker',
'marking_date',
'submission_date',
'comments',
'category_mark_1',
'category_mark_2',
'category_mark_3',
'category_mark_4',
'category_mark_5',
'category_mark_6',
'category_mark_7',
'category_mark_8',
]
The code in the views.py is quite straight forward and obviously passes the right parameter on - as shown by the "print" line.
How can I get this to work?
And as a second question: is it enough for testing purposes to simply test "assertContains" in the form_as_p() or do I need something else because of Crispy Forms?
You are creating a variable named 'helper' but not assigning it to the form instance. You need to use self.helper = FormHelper:
def __init__(self, *args, **kwargs):
marksheet_type = kwargs.pop('marksheet_type')
super(IndividualFeedbackForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
if marksheet_type == 'ESSAY':
print('Detects marksheet') # Just for testing - and it does...
self.helper.layout = Layout(
'marker',
'marking_date',
'submission_date',
Field('category_mark_1', label=CATEGORIES['ESSAY']['i-1']),
Field('category_mark_2', label=CATEGORIES['ESSAY']['i-2']),
Field('category_mark_3', label=CATEGORIES['ESSAY']['i-3']),
Field('category_mark_4', label=CATEGORIES['ESSAY']['i-4']),
'comments',
'mark'
)
elif marksheet_type == 'PRESENTATION':
...
See example http://django-crispy-forms.readthedocs.org/en/latest/form_helper.html#formhelper-with-a-form-attached-default-layout .
You can also remove the FormHelper() from the class definition, at the top.
Finally, keep in mind that crispy forms will automatically append any fields defined in the form Meta class not present in the layout. See source.
I am having trouble rendering individual fields in my template. I have a custom form that renders a multichoice widget and Charfield. I want to render the widget and Charfield individually, so I can place the Charfield on the right of the widget rather than on the bottom (which is what Django does by default). Is there a way to call individual fields in my form in the template?
forms.py
class UpdateStateOptionWithOutcomesForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
disease=kwargs.pop('disease', None)
super(UpdateStateOptionWithOutcomesForm, self).__init__(*args, **kwargs)
self.fields['relevantoutcome']=forms.ModelMultipleChoiceField(queryset=Outcome.objects.filter(relevantdisease_id=disease),required=True, widget=forms.CheckboxSelectMultiple)
self.fields['relevantoutcome'].label="Treatment Outcomes"
outcome_qs=Outcome.objects.filter(relevantdisease_id=disease)
for outcome in outcome_qs:
self.fields['outcomevalue_%s' % outcome.pk] = forms.CharField(required=False)
self.fields['outcomevalue_%s' % outcome.pk].label = "Outcome Value"
{{ form.fieldname }} will render only the field widget by its name.
Check out Customizing the form template.
The following code could be helpful to someone. Here is a way to get a rendering field with fieldname from a form:
form.fields[fieldname].get_bound_field(form, fieldname)
I'm using the following code to wrap all the form fields of a form in a formset together in a div with django crispy forms:
class OperatorForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(OperatorForm, self).__init__(*args, **kwargs):
self.helper = FormHelper(self)
self.helper.form_tag = False
self.helper.all().wrap_together(Div, css_class="operator-form")
self.helper.render_unmentioned_fields = True
class Meta:
model = Operator
fields = tuple(fields_list)
Instantiating FormHelper with 'self' autogenerates the layout without having to specify all fields. I need this because my fields are dynamically generated. The problem is that the DELETE and ORDER fields are not added to the layout. So I set render_unmentioned_fields to True. With this setting these two fields at least show up in the form, but are not wrapped with the rest of the fields in the div.
I know I can manually iterate over the forms in a formset and wrap them with a div, which is what I've been doing up to now, but this requires manual handling of the non form errors of the formset, which is usually done automatically by crispy.
EDIT: Updated code as requested. This entire class is actually wrapped in a factory method that populates fields_list, so that I can dynamically include only certain fields. ORDER and DELETE are left out on purpose. They are automatically detected (including them results in an error) and display in the form, they are simply not wrapped in the div.
As Brian Dant suggested, ORDER and DELETE should be added manually if they are to be included in the Layout. My problem was that I also passed them to the Meta class's `fields'.
class _OperatorForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(_OperatorForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_tag = False
self.helper.layout.extend(['ORDER', 'DELETE'])
self.helper.all().wrap_together(Div, css_class="operator-form")
class Meta:
model = ApplicationOperator
fields = tuple(fields_list)
I'm confused on whether I can set initial data to a field with crispy forms. I have one field in which I generate a number and want to set that to a specific field. If I do anything like:
def __init__(self, *args, **kwargs):
self.fields['medical_record_number'].initial = 'whatever you want'
I get a error stating that name 'self' is not defined.
I am assuming there is something different I have to do while using crispy forms to accomplish the same thing?
Thanks,
Tom
Nothing to do with crispy-forms, this is regular Django forms code. You have forgotten to call parent constructor, if you are using python 2.X do:
def __init__(self, *args, **kwargs):
super(YourFormClassName, self).__init__(*args, **kwargs)
self.fields['medical_record_number'].initial = 'whatever you want'
Beware that maybe you prefer to set initial in your Django form field:
whatever = forms.CharField(initial="whatever you want")