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.
Related
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'
I have form like this:
class TitlePropose(forms.Form):
title = forms.CharField(max_length=128)
code= forms.CharField(max_length=32)
def __init__(self, contest, *args, **kwargs):
super(TitlePropose, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = self.__class__.__name__.lower()
self.helper.form_action = ''
self.helper.layout = Layout(,
Field('title'),
Field('code'),
)
def clean_title(self):
if OtherModel.objects.filter(contest=contest, title=self.cleaned_data['title']).count() > 0:
raise forms.ValidationError("Title unavailable")
else:
return self.cleaned_data['title']
I try to access variable "contest" from clean_title method, without any success. I'm passing this variable in form class contructor:
#contest is just some object
new_title_form = TitlePropose(contest=contest.uuid)
Any suggestion, how can I get access 'contest' in clean_title?
This is standard Python class stuff. If you want to store an object so that other methods can access it, you make it an instance attribute by adding it to self.
def __init__(self, *args, **kwargs):
self.contest = kwargs.pop('contest')
super(TitlePropose, self).__init__(*args, **kwargs)
def clean_title(self):
if OtherModel.objects.filter(contest=self.contest, ...
I'm trying to get a placeholder to show up for a form field but I'm doing something wrong with my form fields. Any thoughts? I've got the following classes:
class EditField(forms.CharField):
def __init__(self, *args, **kwargs):
super(EditField, self).__init__(*args, **kwargs)
self.widget = forms.Textarea(attrs={'id':'editor'})
self.label = _('content')
class EditField_typeA(EditField):
def __init__(self, *args, **kwargs):
super(EditField_typeA, self).__init__(*args, **kwargs)
def clean(self, value):
if not (len(re.sub('[ ]{2,}', ' ', value)) < settings.FORM_MIN):
raise forms.ValidationError(_('content must be %s') % settings.FORM_MIN)
return value
class FinalForm(forms.Form):
foo = FooField()
bar = BarField()
text = EditField_typeA()
def __init__(self, data=None, user=None, *args, **kwargs):
super(FinalForm, self).__init__(data, *args, **kwargs)
## THIS IS THE PART THAT ISN'T WORKING
self.fields['text'].widget.attrs['placeholder'] = 'Fill this in'
if int(user.reputation) < settings.CAPTCHA_IF_REP_LESS_THAN and not (user.is_superuser or user.is_staff):
spam_fields = call_all_handlers('create_anti_spam_field')
if spam_fields:
spam_fields = dict(spam_fields)
for name, field in spam_fields.items():
self.fields[name] = field
self._anti_spam_fields = spam_fields.keys()
else:
self._anti_spam_fields = []
I'm guessing that I'm using widget wrong, or in the wrong place. Can't find the right part of the widget docs to explain what I'm doing wrong though.
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
Is there any easy way to assign class=radio to all elements whose widget is radioselect in a form ?
I know I can write such that
rb = forms.ChoiceField( widget=forms.RadioSelect(attrs='class':'radio'))
for all radio buttons in the form but I think there should be other ways. Because I probably use it for all radio buttons and it is not funny to write this for all.
You can create your own widget, like this:
class MyRadioSelect(forms.RadioSelect):
def __init__(self, *args, **kwargs):
attrs = kwargs.pop("attrs", {})
if "class" in attrs:
attrs["class"] = "%s radio" % attrs["class"]
else:
attrs["class"] = "radio"
kwargs["attrs"] = attrs
super(MyRadioSelect, self).__init__(*args, **kwargs)
like this?
class ClassyRadioSelect(forms.RadioSelect):
def __init__(self, *args, **kwargs):
#yes, i've to look up how to process args and kwargs properly
attrs = kwargs.get('attrs', {})
attrs['class'] = ' '.join((attrs.get('class',''), 'radio'))
kwargs['attrs'] = attrs
super(ClassyRadioSelect, self).__init__(*args, **kwargs)
class ClassyChoiceField(forms.ChoiceField):
def __init__(self, choices=(), required=True, widget=None, label=None,
initial=None, help_text=None, *args, **kwargs):
if not widget:
widget = ClassyRadioSelect()
super(ClassyChoiceField, self).__init__(choices, required, widget, label
, initial, help_text, *args,
**kwargs)
class MyForm(forms.Form):
classy_field1 = ClassyChoiceField()
classy_field2 = ClassyChoiceField()