Custom button classes in django crispy forms - django

The technical issue of styling buttons in django crispy forms. I would like to apply my own class, without using the primary button class.
class MyForm(Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fields("field_1"),
Fields("field_2"),
Submit('submit', u'On', css_class='own-css-class'),
)
Basically, I solved this by adding self.helper.form_tag = False and inserting the button code directly into the html template. In addition, I deleted the submit button from the layout.
class MyForm(Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fields("field_1"),
Fields("field_2"),
)
self.helper.form_tag = False
Is this solution correct and will it be compatible in the long term?

I'd recommend that you create your own custom button and use that when creating your layout.
class CustomButton(BaseInput)
input_type = 'submit'
field_classes = 'my custom css'

Related

Rendering several forms with helpers Crispy Django

I'm having an issue using several forms with django-crispy-forms for Django.
From the documentation, we must set self.helper.form_tag = False in out Form. Documentation here
Then wrapp the forms with a Form tag in the HTML.
class SearchForm(forms.Form):
X = forms.IntegerField(label='X', min_value=0, max_value=10)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper
self.helper.form_method = 'post'
self.helper.form_tag = False
self.helper.layout = Layout(
'X',
Submit('submit', 'Submit', css_class='btn-success')
)
class PredictForm(forms.Form):
Y = forms.IntegerField(label='X', min_value=0, max_value=10)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper
self.helper.form_method = 'post'
self.helper.form_tag = False
self.helper.layout = Layout(
'Y',
Submit('submit', 'Submit', css_class='btn-success')
)
Then in the HTML file I have:
<form action="my_action" class="uniForm" method="post">
<div id="box" class="box">{% crispy formSearch formSearch.helper %}</div>
<div id="box2" class="box2">{% crispy formPrediction formPrediction.helper %}</div>
</form>
In my View.py:
def index(request):
if request.method == 'POST':
formSearch = SearchForm(request.POST)
formPrediction = PredictionForm(request.POST)
# Do stuff
else
formSearch = SearchForm()
formSearch.fields['X'].initial = 5
formPrediction = PredictionForm()
formSearch.fields['Y'].initial = 5
return render(request, 'index.html', {'formSearch': formSearch}, 'formPrediction': formPrediction)
What I get is 2 crispy forms that are displayed on my web page, but the first forms tries to load the fields of the seconds forms. It seems like my second form is duplicated.
I get this error:
KeyError: "Key 'Y' not found in 'SearchForm'. Choices are: X."
It is trying to get data from PredictForm but is aware that only X is in SearchForm. I thought it was I typo somewhere, but I can't find my mistake.
Here is the answer:
A collision war made between the 2 forms, why? Because I did not initiate properly each form functions.
I have changed
self.helper = FormHelper
to
self.helper = FormHelper(self)

Django overrides a Button in Crispy Form

I've got a first form as following:
class SupplierRegistrationSupplementForm(forms.ModelForm):
siret = FRSIRETField()
def __init__(self, *args, **kwargs):
super(SupplierRegistrationSupplementForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.form_id = 'company_supplement_form'
self.helper.form_action = "."
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-8'
self.helper.add_input(
Submit('submit', _('Save'),
css_class="btn-primary btn-lg",
css_id="save_company"
)
)
I need to inherit from it but with a different button. In order to change the button id, I would have done this:
class SupplierUpdateForm(SupplierRegistrationSupplementForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper.form_tag = False
self.helper.add_input(
Submit('submit', _('Save'),
css_class="btn-primary btn-lg",
css_id="save_user"
)
)
But it adds a new button to the previous one. Is it possible to remove the first one?
When you call add_inputs, the code appends the input to self.inputs. So a quick hack would be to pop the existing input from the list before adding the new one.
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
...
self.inputs.pop()
self.helper.add_input(...)
However this is fragile, because it assumes that there is exactly one input. It might be better to have a BaseSupplierForm which does not have any inputs, then have two subclasses SupplierRegistrationSupplementForm and SupplierUpdateForm, which both define their own inputs.

performing different functionality on modelform buttons django

I have a modelform with 2 buttons and i want to perform different functionality on them.
My modelform:
class jobpostForm(ModelForm):
class Meta:
model = jobpost
fields = ('job_title','job_type','job_location','job_description','start_date','end_date','country','how_to_apply')
widgets = {
'job_type':RadioSelect(),
'job_location':TextInput(attrs={'size':'70'}),
'job_description':Textarea(attrs={'cols':200, 'rows':10}),
'start_date':TextInput(attrs={
'class': 'datepicker',
'data-date-format': 'mm/dd/yyyy',
}),
'end_date':TextInput(attrs={
'class': 'datepicker',
'data-date-format': 'mm/dd/yyyy',
}),
}
def __init__(self, *args, **kwargs):
#super(jobpostForm, self).__init__(*args, **kwargs)
#self.fields['start_date'].widget.attrs['class'] = 'datepicker'
super(jobpostForm, self).__init__(*args, **kwargs)
#self.fields['ref_id'].widget = forms.HiddenInput()
self.helper = FormHelper()
self.helper.form_class = 'horizontal-form'
self.helper.form_id = 'id-jobpostform'
self.helper.form_class = 'blueForms'
self.helper.form_method = 'post'
self.helper.form_action = '/portal/next/post/'
self.helper.add_input(Submit('submit_addcontent', 'Preview'))
self.helper.add_input(Submit('submit_addcontent', 'Submit'))
super(jobpostForm, self).__init__(*args, **kwargs)
I want to perform different functionality on submit and preview.How can i access them in my view?
A django form really handles two things:
Displaying the intitial form on a GET request
Processing POST requests with data
You can approach your situation in multiple ways. One way would be to have both buttons submit your form. The preview button would fill in a hidden field named preview. Your form would process the submitted data. If the data included a value in the POST field named preview it would render a preview. Otherwise, it would process the form normally.

Using a model instance with django-crispy and a formset

I've got a modelform_formset that I'm rendering with django-crispy. Inside the layout of form used I have the following:
self.helper.layout = Layout(
Field('remove', css_class="inline"),
HTML('{{ form.instance.user.get_full_name|title }} ({{ form.instance.user }})'),
Field('is_admin')
)
Note the {{form.instance}} - That's not getting rendered properly binding. Is there a way to get the value for the specific model?
I would recommend you do this. This captures the case where you don't have an instance :D
class XYXForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(XYXForm, self).__init__(*args, **kwargs)
label = "New Object"
if self.instance:
label = '{0} {1}'.format(
self.instance.user.get_full_name.capitalize(),
self.instance.user)
self.helper = FormHelper()
self.helper.form_id = 'community_form'
self.helper.form_method = 'post'
self.helper.layout = Layout(
Field('remove', css_class="inline"),
HTML(label),
Field('is_admin')
)
HTH

use Crispy form with ModelForm

I've been running into crispy form, and it seems to do exactly what I want: render forms with bootstrap layout.
Now, the example talk about using forms.Form. This is ok, I can create mine by writing the code like this:
class TemplateCreateForm(forms.Form):
title = forms.CharField(label=(u'Task name'))
description = forms.CharField(label=(u'Task description'))
url_start = forms.CharField(label=(u'Start page url'))
url_end = forms.CharField(label=(u'Final page url'))
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Submit'))
super(TemplateCreateForm, self).__init__(*args, **kwargs)
But, how to do the update? because if I put this in the view:
form = TemplateCreateForm(request.POST or None, instance=template)
it does not work because instance is only for ModelForm.
Now, can I substitute the forms.Form with ModelForm and use crispy form for ModelForm?
I did this
class TemplateCreateForm(ModelForm):
title = forms.CharField(label=(u'Task name'))
description = forms.CharField(label=(u'Task description'))
url_start = forms.CharField(label=(u'Start page url'))
url_end = forms.CharField(label=(u'Final page url'))
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Submit'))
super(TemplateCreateForm, self).__init__(*args, **kwargs)
class Meta:
model = Template
exclude = ('user')
Here I added the Meta class.
Now: it works, but is it correct to use it like this?
The update works as well in this way.
What's the correct way to use forms for doing the update?
I'm the lead developer of django-crispy-forms. I'm not sure I follow your question as it's a bit poorly formatted. What exactly are you trying to do?
django-crispy-forms does work with ModelForms, the same way as with simple forms. It sits on top of Django, so it doesn't mess with it. It only controls your form rendering, but doesn't change how validation works, how to create form instances and so on.
EDIT:
I'm adding an example on how to do a ModelForm with crispy-forms.
class ExampleModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ExampleModelForm, self).__init__(*args, **kwargs)
# If you pass FormHelper constructor a form instance
# It builds a default layout with all its fields
self.helper = FormHelper(self)
# You can dynamically adjust your layout
self.helper.layout.append(Submit('save', 'save'))
class Meta:
model = ExampleModel
I believe your first problem is that you were subclassing forms.Form instead of forms.ModelForm. That's why I said that your problem was Django related, not crispy-forms related.
Later in your view:
form = ExampleModelForm()
In your template:
{% load crispy_forms_tags %}
{% crispy form %}