How to change empty_label for modelForm choice field? - python

I have a field in one of my models like the following:
PAYROLL_CHOICES = (
('C1', 'Choice1'),
('C2', 'Choice2')
etc.....
)
payrollProvider = models.CharField(max_length=2, choices=PAYROLL_CHOICES)
When I create a model form for this field, Django correctly generates an HTML select box, but includes a default blank value of "---------".
I would like to know how to change this default value to some other text, such as "please choose value".
I believe I should be able to set this in my model form's init via the following, as documented in this answer and several others:
self.fields['payrollProvider'].empty_label = "please choose value"
However, this isn't working for me. When I include that line in my form's init, "--------" still shows up as the initial choice in the select box. I'm pasting the relevant forms.py below, but it seems that others have also been unable to access / modify empty_label. At this link, the questioner describes a way to delete the default empty_label value (which I was able to do successfully via his method) but what I really want to do is to modify the empty_label that is displayed.
Any ideas?
Here's the code for the form in forms.py, with the empty_label code that isn't successful at changing the default "----------":
class PayrollCredentialForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PayrollCredentialForm, self).__init__(*args, **kwargs)
self.fields['payrollUsername'].widget.attrs.update({'class' : 'yp-signup'})
self.fields['payrollPassword'].widget.attrs.update({'class' : 'yp-signup'})
self.fields['payrollProvider'].widget.attrs.update({'class' : 'yp-signup'})
self.fields['payrollUsername'].widget.attrs.update({'placeholder' : ' Payroll Username'})
self.fields['payrollPassword'].widget.attrs.update({'placeholder' : ' Payroll Password'})
self.fields['payrollProvider'].empty_label = "please choose value"
class Meta:
model = Company
fields = ('payrollProvider', 'payrollUsername', 'payrollPassword')
widgets = {
'payrollPassword': forms.PasswordInput(),
}

dokkaebi, that won't work properly. You'll receive the following select code:
<select name="payrollProvider" id="id_payrollProvider">
<option value="" selected="selected">---------</option>
<option value="" selected="selected">please choose value</option>
<option value="C1">Choice1</option>
<option value="C2">Choice2</option>
</select>
The only relatively convenient way that came to my mind is to do something like this in the form:
class PayrollCredentialForm(forms.ModelForm):
class Meta:
model = Company
def __init__(self, *args, **kwargs):
super(PayrollCredentialForm, self).__init__(*args, **kwargs)
self.fields["payrollProvider"].choices = [("", "please choose value"),] + list(self.fields["payrollProvider"].choices)[1:]

Actually, now (as of Django 1.8 and higher) override of an empty_label works:
class PayrollCredentialForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PayrollCredentialForm, self).__init__(*args, **kwargs)
self.fields['payrollProvider'].empty_label = 'Please, choose value'
Also, if you working with Django Admin, there is an option to set empty value for a list view:
class PayrollCredentialAdmin(admin.ModelAdmin):
list_display = ('payrollProvider_value', )
def payrollProvider_value(self, instance):
return instance.payrollProvider
payrollProvider_value.empty_value_display = 'Empty value'
What if field should be readonly?
There is a catch if field modified in such way should be readonly.
If overridden form field will be specified in readonly_fields attribute inside PayrollCredentialAdmin class, it would result in KeyError exception in PayrollCredentialForm (because readonly field won't be included in form's self.fields). To handle that, it's required to override formfield_for_dbfield instead of using readonly_fields:
def formfield_for_dbfield(self, db_field, **kwargs):
field = super(PayrollCredentialAdmin, self).formfield_for_dbfield(
db_field, **kwargs
)
db_fieldname = canonical_fieldname(db_field)
if db_fieldname == 'payrollProvider':
field.widget = forms.Select(attrs={
'readonly': True, 'disabled': 'disabled',
})
return field
Might be useful.
Update for Django 1.11:
Comments below brought assumption that such override is no longer valid for newer version of Django.

The problem is that you are trying to specify something that is not available for the type of Select field.
The empty_label option is for forms.ModelChoiceField, which happens to use a Select widget, but is not the same kind of field as your CharField that you are providing options for.
https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield
You can see this also in a previous question here: https://stackoverflow.com/a/740011/1406860
You could try and override the html of the modelform to add the first option as "please choose value". Alternatively, you could use a template filter to do the same thing. Lastly, you could and ("", "please choose value") to PAYROLL_CHOICES, and if you don't want it to be submitted without a payrollProvider just set blank=False for the field in the model.
JD

In your forms.py file,
This would definitely work.. Try this...
class Meta:
model = StaffDetails
fields =['photo','email', 'first_name','school','department', 'middle_name','last_name','gender', 'is_active']
def __init__(self, *args, **kwargs):
super(StaffDetailsForm, self).__init__(*args, **kwargs)
self.fields['Field_name'].empty_label = 'Please Select'
It worked for me.. just replace the field names with yours...

Only ModelChoiceField (generated for ForeignKey fields) supports the empty_label parameter, and in that case it's tricky to get at as those fields are usually generated by django.forms.models.ModelFormMetaclass within a call to django.forms.models.modelform_factory.
ModelFormMetaclass uses the empty_label param to add another choice to the list, with empty_label as the display and '' as its value.
The simplest way to do what you want is just to add an empty choice to your choices list:
PAYROLL_CHOICES = (
('', 'please choose value'),
('C1', 'Choice1'),
('C2', 'Choice2'),
etc.....
)

Another simple way worked for me is:
country = forms.ModelChoiceField(queryset=Country.objects.filter(), empty_label='--Select--')
However, my Django version is 2.2.7

Just add a tuple to your model.field.choices with a value of None:
payrollProvider = models.CharField(max_length=2, choices=PAYROLL_CHOICES)
PAYROLL_CHOICES = (
(None, 'please choose'),
('C1', 'Choice1'),
('C2', 'Choice2')
etc.....
)
From the docs (v4.0):
Unless blank=False is set on the field along with a default then a label containing "---------" will be rendered with the select box. To override this behavior, add a tuple to choices containing None; e.g. (None, 'Your String For Display'). Alternatively, you can use an empty string instead of None where this makes sense - such as on a CharField.

Adapted from Javed answer. Since I have tons of fields in my form I just want to replace all labels in the html by placeholders so for select tags I use their field label.
class PacienteForm(forms.ModelForm):
class Meta:
model=Paciente
fields=('__all__')
def __init__(self, *args, **kwargs):
super(PacienteForm, self).__init__(*args, **kwargs)
for f in self.fields:
if hasattr(self.fields[f], 'choices'):
choices=self.fields[f].choices
if type(choices) == list:
choices[0]=('',self.fields[f].label)
self.fields[f].choices=choices

Add Empty String with "Please Select" to choices as shown below:
class DateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class Months(models.TextChoices):
EMPTY_LABEL = '', 'Please Select' # Here
JANUARY = 'JAN', 'January'
FEBRUARY = 'FEB', 'February'
MARCH = 'MAR', 'March'
self.fields['month'].choices = Months.choices

Related

In Django how to add a placeholder to a form based on model charfield

I have a form:
class SideEffectForm(ModelForm):
class Meta:
model = SideEffect
fields = ['se_name']
def __init__(self, *args, p, **kwargs):
super().__init__(*args, **kwargs)
if p == 'antipsychotic':
self.fields['se_name'].choices = [
("insomnia_and_agitation", "Insomnida and Agitation"),
("akathisia", "Akathisia"),
("dystonia", "Dystonia"),
]
That is based on this model:
class SideEffect(TimeStampedModel):
SE_CHOICES = [
("insomnia_and_agitation", "Insomnida and Agitation"),
("akathisia", "Akathisia"),
("anticholinergic_effects", "Anticholinergic Side Effects")
]
se_name = models.CharField("",max_length=200, choices=SE_CHOICES, blank=False)
concern = models.IntegerField("",default=50)
case = models.ForeignKey(Case, on_delete=models.CASCADE)
And this is the view:
class CaseView(LoginRequiredMixin, TemplateView):
model = Case
template_name = "se_balance/se_balance.html"
def get(self, *args, **kwargs):
p = self.request.GET.get("p", None)
sideeffect_formset = SideeffectFormSet(queryset=SideEffect.objects.none(),
form_kwargs={'p': p})
return self.render_to_response(
{ "page_title": p.capitalize(),
"sideeffect_formset": sideeffect_formset,
"sideeffect_formsethelper": SideEffectFormSetSetHelper,
}
)
def post(self, *args, **kwargs):
p = self.request.GET.get("p", None)
case_instance = Case(pharm_class_name=p)
sideeffect_formset = SideeffectFormSet(data=self.request.POST, form_kwargs={'p': p})
case_instance.save()
if sideeffect_formset.is_valid():
print('seform valid')
sideeffect_name = sideeffect_formset.save(commit=False)
for sideeffect in sideeffect_name:
sideeffect.case = case_instance
sideeffect.save()
return redirect(
reverse(
"results",
kwargs = {"case_id": case_instance.case_id} )
)
As it stands the form displays the first option. I would however like to have a place holder e.g. 'Please select a side effect'. I could do this by having it as one of the options (e.g. (None, 'This is the placeholder prompt')) but would prefer not to as then would need to implement measures to stop the placeholder being saved as a valid user entry. I have tried a range of suggestions on the site but none have been suitable.
You stated:
I could do this by having it as one of the options (e.g. (None, 'This is the placeholder prompt')) but would prefer not to as then would need to implement measures to stop the placeholder being saved as a valid user entry.
No, that's not true, when we set (None, 'This is the placeholder prompt') there's no need to write the custom logic for not saving selected option in the database as valid user entry.
An Excerpt from the Django-doc about this issue.
Unless blank=False is set on the field along with a default then a label containing "---------" will be rendered with the select box. To override this behavior, add a tuple to choices containing None; e.g. (None, 'Your String For Display'). Alternatively, you can use an empty string instead of None where this makes sense - such as on a CharField.
Solution:
You can simply do this (None, 'please select the side effect'), but as stated while using CharField you should use ('', 'please select the side effect') this instead and also you've already set blank=False.
Try this:
class SideEffectForm(ModelForm):
class Meta:
model = SideEffect
fields = ['se_name']
def __init__(self, *args, p, **kwargs):
super().__init__(*args, **kwargs)
if p == 'antipsychotic':
self.fields['se_name'].choices = [
('','Please select a side effect'),
("insomnia_and_agitation", "Insomnida and Agitation"),
("akathisia", "Akathisia"),
("dystonia", "Dystonia"),
]
After, doing this, see source code through Ctrl+U and you'll see below code of html:
<select ...>
<option value="" selected>please select the side effect</option>
<option value="insomnia_and_agitation">Insomnida and Agitation</option>
<option value="akathisia">Akathisia</option>
<option value="dystonia">Dystonia</option>
</select>
And selected value doesn't save in database, so if you submit form with it, you'll see the error of required.

Django crispy forms - Set hidden field value

I've got the following django crispy form:
class ConsultForm(forms.ModelForm):
class Meta:
model = Consults # Your User model
fields = [ 'TEMPLATE','EMAIL', 'DATE']
labels = {
'EMAIL' : 'Your Email',
'DATE' : 'Todays date',
# 'captcha': "Enter captcha"
}
helper = FormHelper()
helper.form_method = 'POST'
helper.form_action = "/contact/"
helper.form_id = 'form' # SET THIS OR BOOTSTRAP JS AND VAL.JS WILL NOT WORK
helper.add_input(Submit('Submit', 'Submit', css_class='btn-primary'))
helper.layout = Layout(
Field('TEMPLATE', type="hidden"),
Field('DATE', type="hidden"))
I want to pass a value with the hidden field TEMPLATE. I've read https://django-crispy-forms.readthedocs.io/en/latest/api_helpers.html , but can't see how to do this. How can I get this done?
You can set Form field initial values like this:
class ConsultForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.initial['TEMPLATE'] = 'my_initial_value'
You can also change the value of the field at other places in your code like:
form = ConsultForm(instance=instance)
form.initial['TEMPLATE'] = 'new_value'
With formhelper (with crispy Universal Layout Objects like Field) you set attributes as you already did, like:
Field('TEMPLATE', id="template", value="something" template="my-template.html")
If that's what you were asking for.
Or if the above does not work easy then there is a layout object called Hidden in crispy. You can create hidden input fields with that:
Hidden('name', 'value')
You use it as Hidden('TEMPLATE', 'mysomethingvalue')
Like:
Button('name', 'value')
To make it fully clear:
helper.layout = Layout(
Hidden('TEMPLATE', 'myvalue'),
Hidden('DATE', 'anydate'))

How to fill a Django custom multiplechoicefield with choices

I have a form with a search box that uses jQuery to fill a multipleChoiceField. I need to use a custom multipleChoiceField so I can control the validation and only check if the choice exists, not if it was one of the original choices as a modelMultipleChoiceField with a queryset would. However, the custom multipleChoiceField renders on the page as empty until you enter something in the search box to fill it with choices via jQuery. I would like it to render with a few choices to begin with instead.
class ArticleMultipleChoiceField(forms.MultipleChoiceField):
def __init__(self, *args, **kwargs):
super(ArticleMultipleChoiceField, self).__init__(*args, **kwargs)
include_articles = [article.id for article in Article.objects.order_by('-sub_date')[:5]]
self.choices = Article.objects.filter(id__in=include_articles).order_by('-sub_date')
In this form, I get the error "Article object is not iterable". I have also tried changing that self.choices to self.data, self.queryset, and self.initial, and in all those 3 cases, I keep getting an empty multiple choice field instead.
How can I use a queryset to provide the initial set of choices here?
Here is the form it is used in:
class StorylineAddArticleForm(forms.Form):
articleSearchBox = forms.CharField(label="Search to narrow list below:")
include_articles = [article.id for article in Article.objects.order_by('-sub_date')[:5]]
articles = ArticleMultipleChoiceField()
def __init__(self, *args, **kwargs):
super(StorylineAddArticleForm, self).__init__(*args, **kwargs)
self.fields['articleSearchBox'].required = False
self.helper = FormHelper(self)
self.helper.layout = Layout(
Field('articleSearchBox'),
Field('articles'),
ButtonHolder(
Submit('submit', 'Add', css_class='button white')
)
)
Also, this is being rendered by Crispy Forms.
choices doesn't accept a QuerySet as an argument, it needs a list or tuple of two-tuples with acceptable values. See the documentation on choices here: https://docs.djangoproject.com/en/2.0/ref/models/fields/#field-choices .
In this case you need to turn your Article queryset into a list or tuple of the above format.

How to extend CheckboxInput for a list of checkboxes

I am currently using a MultiCheckboxField like this:
class MultiCheckboxField(SelectMultipleField):
"""
A multiple-select, except displays a list of checkboxes.
Iterating the field will produce subfields, allowing custom rendering of
the enclosed checkbox fields.
"""
widget = widgets.ListWidget(prefix_label=False)
option_widget = widgets.CheckboxInput()
to generate a list of checkboxes. I would like to extend this list in such a way as to allow some of the list entries to have an associated TextInput field. When the box is checked, the corresponding text input is required.
I am new to Flask and WTForms, and I am having some trouble trying to figure out just how to attack the problem. I would be grateful for any suggestions that might provide some kind of direction.
See the FieldList and FormField with custom widgets
http://wtforms.readthedocs.org/en/latest/fields.html#field-enclosures
You can use a custom validator like this:
class RequiredIfChoice(validators.DataRequired):
# a validator which makes a field required if
# another field is set and has a truthy value
def __init__(self, other_field_name, desired_choice, *args, **kwargs):
self.other_field_name = other_field_name
self.desired_choice = desired_choice
super(RequiredIfChoice, self).__init__(*args, **kwargs)
def __call__(self, form, field):
other_field = form._fields.get(self.other_field_name)
if other_field is None:
raise Exception('no field named "%s" in form' % self.other_field_name)
for value, label, checked in other_field.iter_choices():
if label == self.desired_choice and checked:
super(RequiredIfChoice, self).__call__(form, field)
and in your form:
class MyForm(Form):
"""
Your form.
"""
multi = MultiCheckboxField('Multibox', choices=[(1, 'First'), (2, 'Second')], coerce=int)
multitext = StringField('SubText', [RequiredIfChoice('multi', 'Second')])
For a slightly similar question look at this Q&A.

Modify presentation of element in a TabularInline

For this particular administration page, I'd like to turn the 'current value' (outlined in a red circle) into a link going back to the administration page for this particular object.
But I can't find where to go to make this change. I know that I need to somehow override how this
is displayed but I can't figure it out.
What do I need to override to do what I want?
Admin model definition:
class FirmwareConfigElementsChoiceInline(admin.TabularInline):
model = FirmwareConfigElements
extra = 1
class FirmwareConfigAdmin(admin.ModelAdmin):
save_as = True
list_display = ('name', 'description')
inlines = [FirmwareConfigElementsChoiceInline]
Using Filip's great help I've gotten to this:
class FirmwareConfigElementsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
klass = FirmwareConfigElementsForm
super(klass, self).__init__(*args, **kwargs)
if self.instance.type == 'incfw':
value = self.instance.value
url = '#' # TODO: get the URL for the value
hyperlink = '%s' % (url, value)
label = self.fields['type'].label.replace(value, hyperlink)
self.fields['type'].label = label
But in the above code, self.fields['type'].label has the contents Type and not Include another FW Config - BASE:IBM-HS22/HS22V as I was expecting.
I've explored it in the debugger but I can't figure out how to get to the particular label that I want to change.
Inline admin models have a template property you can use to supply a custom template. From there, you'll need to modify the code to add the url.
You'll need to provide a custom ModelForm for the FirmwareConfigElements model, which you'll set as the value for the FirmwareConfigElementsChoiceInline.form class attribute.
Here you'll want to override the ModelForm.__init__() instance method to assign a new label for the field you want to override if the form is bound:
class FirmwareConfigElementsForm(models.ModelForm):
def __init__(self, *args, **kwargs):
klass = FirmwareConfigElementsForm
super(klass, self).__init__(*args, **kwargs)
if form.is_bound and 'value' in self.data:
value = self.data['value']
url = '' # TODO: get the URL for the value
hyperlink = '%s' % (url, value)
label = self.fields['type'].label.replace(value, hyperlink)
self.fields['type'].label = label
class FirmwareConfigElementsChoiceInline(admin.TabularInline):
model = FirmwareConfigElements
extra = 1
form = FirmwareConfigElementsForm
Now, if you want the label to change dynamically as the user changes the form data, then it gets a lot uglier and you'll have to resort to referencing JavaScript media and performing the above on the fly.

Categories

Resources