Django - remove/hide labels for checkboxes - django

I would have thought this would be pretty simple but I have plugged away at this for some time and I can't figure it out.
I have a multiple choice checkbox in a model form which is in a formset. All I want to do is remove the labels from the checkboxes. I can easily remove the label from the field but I can't figure out how to remove the labels from the checkboxes.
I am trying to convert the display of the form to a grid.
From this:
To this:
Here's my form code:
class ResourceEditAddForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.portfolio = kwargs.pop('portfolio')
super(ResourceEditAddForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
self.fields["portfolio"].initial = self.portfolio
self.fields["portfolio"].required = False
self.fields["skills"].queryset = self.portfolio.getSkills()
class Meta:
model = Resource
fields = ['portfolio',
'name',
'skills',
'pct_availability',
'cost_per_day',
'email',
'timezone',
'update_request_time',
'calendar',
'start_date',
'end_date']
widgets = {
'portfolio': forms.HiddenInput(),
'start_date': DateInput(),
'end_date': DateInput(),
'update_request_time': TimeInput(),
'skills': forms.CheckboxSelectMultiple(),
}
ResourceEditAddFormSet = modelformset_factory(
Resource,
form=ResourceEditAddForm,
extra=0
)
I could build a manual form to achieve this but I want to keep using model forms as there's a few fields other than skills which are managed fine by the form.
If anyone can tell me how to hide the labels "animation", "art", etc. next to the checkboxes in forms, css or whatever that would be much appreciated.

You can simply hide your label from models.py. Labels on the form come from verbose_name attributes:
class Resource(models.Model):
skills = models...(...,verbose_name="")
or
try this under ResourceEditAddForm's __init__ function:
def __init__(self, *args, **kwargs):
...
self.fields['skills '].label = ''
...

I appear to have resolved this by slightly customising the widget:
skills_widget = forms.CheckboxSelectMultiple
skills_widget.option_template_name = "django/forms/widgets/input.html"
model = Resource
fields = ['portfolio',
'name',
'skills',
'end_date']
widgets = {
'portfolio': forms.HiddenInput(),
'skills': skills_widget
},
So this replaces the full template for the checkbox (which wraps the input in a label) with just the input part.

Related

Formset Factory Make Fields Required

I am using a modelformset_factory in Django to have a user fill out an unknown number of fields.
I have made the fields required but in the HTML rendering Django does not add required to the HTML element, looking around online this seems to be a common issue but I have not seen any valid answers that apply for what I want and I feel like this should be simple.
How do I make Django add the required tag to appropriate HTML elements for a Formset?
class ItemForm(forms.ModelForm):
class Media:
js = (formset_js_path,)
class Meta:
model = PurchaseOrderItems
fields = ['name', 'vendor', 'quantity', 'price', 'description']
labels = {
'name': 'Item',
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# self.fields['description'] .widget.attrs['required'] = 'required'
self.empty_permitted = False
self.fields['description'] = forms.CharField(
required=False,
label='Description',
)
def clean(self):
"""
:return:
"""
cleaned_data = super().clean()
# print(cleaned_data)
ItemFormSet = modelformset_factory(
PurchaseOrderItems,
form=ItemForm,
extra=0,
min_num=1,
validate_min=True,
can_delete=True,
)
Here is the HTML rendered for the name field, no required yet in my model it certainly is, so if this form is submitted I get DB errors because of empty values:
<input type="text" name="form-0-name" maxlength="150" class="form-control" id="id_form-0-name">
According to the release notes:
Required form fields now have the required HTML attribute. Set the new
Form.use_required_attribute attribute to False to disable it. The
required attribute isn’t included on forms of formsets because the
browser validation may not be correct when adding and deleting
formsets.
So if you want to disable, from your view you must submit the form in this way. This will affect all your fields.
form = ItemForm(use_required_attribute=False)
However, if you only want to affect some you must do the previous step and also in your form add this
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update({'required': ''})
self.fields['vendor'].widget.attrs.update({'required': ''})
self.fields['quantity'].widget.attrs.update({'required': ''})
self.fields['price'].widget.attrs.update({'required': ''})
self.fields['description'].widget.attrs.update({'required': 'False'})
On the other hand I see that you are not using widgets in your form you should also use them to make it work.
widgets = {
'name': forms.TextInput(),
'vendor': forms.TextInput(),
'quantity': forms.TextInput(),
'price': forms.TextInput(),
'description': forms.TextInput(),
}
I put all the fields as TextInput, but look for what is indicated according to the type of data here Field types.

Django modelform widget attributes replaced or disregarded

I've been scratching my head over this for the last little while. I have been able to change the modelfield's field queryset and widget attributes, well somewhat!
class InvoiceItemForm(ModelForm):
UOM = forms.ChoiceField (choices = site_defaults.UOM)
class meta:
model = InvoiceItem
fields = ['name', 'costcode', 'rate', 'quantity',]
labels = {'name': 'Item', 'rate': 'Cost Per Unit', 'quantity': 'Base Quantity'}
widgets = {'UOM': forms.Select(choices = site_defaults.UOM )}
def __init__(self, current_user, current_project, *args, **kwargs):
''' Rendering custom ModelForm '''
super(InvoiceItemForm, self).__init__(*args, **kwargs)
the_title = None
the_instance = kwargs.get('instance', None)
if the_instance:
the_costcode = the_instance.costcode
if the_costcode:
the_title = the_costcode.title
self.fields['costcode'].queryset = CostCode.objects.filter(project = current_project, item = 0)
self.fields['costcode'].widget = forms.TextInput(attrs={'class': 'site-flex-select-large', 'value': the_title})
When this is rendered, the costcode field takes the right instance. Also, the class is shown as site-flex-select-large, but the title is shown as the instance.id and not the_title which is the instance.title (a text field is displayed with value of 192 instead of the title of the invoice item).
Why is Django ignoring some changes and accepting some other changes to the field?
I'm not sure if it is a relevant detail or not, but the modelform is used in an inlineformset:
expenses_forms = self.InvoiceItem_InlineFormSet(instance = the_invoice, prefix='expenses', form_kwargs={'current_user': user, 'current_project': project})
A fields widget is not the place that you should be setting initial values for fields. You should set this in the "initial" kwarg to the form's __init__ method, you can pass it to the call to super. You then can set the costcode widget in the Meta
class InvoiceItemForm(ModelForm):
UOM = forms.ChoiceField (choices = site_defaults.UOM)
class Meta:
model = InvoiceItem
fields = ['name', 'costcode', 'rate', 'quantity',]
labels = {'name': 'Item', 'rate': 'Cost Per Unit', 'quantity': 'Base Quantity'}
widgets = {
'UOM': forms.Select(choices = site_defaults.UOM ),
'costcode': forms.TextInput(attrs={'class': 'site-flex-select-large'})
}
def __init__(self, current_user, current_project, *args, **kwargs):
the_instance = kwargs.get('instance', None)
if the_instance:
the_costcode = the_instance.costcode
if the_costcode:
initial = kwargs.get('initial', {})
initial['costcode'] = the_costcode.title
kwargs['initial'] = initial
super(InvoiceItemForm, self).__init__(*args, **kwargs)
EDIT: like Willem says, the costcode field is a TextInput so it does not make sense to set a queryset attribute on it unless you change it to a select
The value is not taken from the attrs, it is taken from the value of that field. You can set the .initial attribute of the field, like:
def __init__(self, current_user, current_project, *args, **kwargs):
''' Rendering custom ModelForm '''
super(InvoiceItemForm, self).__init__(*args, **kwargs)
the_title = None
the_instance = kwargs.get('instance', None)
if the_instance:
the_costcode = the_instance.costcode
if the_costcode:
the_title = the_costcode.title
self.fields['costcode'].queryset = CostCode.objects.filter(project=current_project, item=0)
self.fields['costcode'].initial = the_title
self.fields['costcode'].widget = forms.TextInput(attrs={'class': 'site-flex-select-large'})
That being said, by using a TextInput, it will, as far as I know, just ignore the queryset, and it will not properly validate the data. I think you better use a Select widget [Django-doc] here, and then use some CSS/JavaScript to make it searchable through text.

Django Generic Model Formsets

So I have a site where users are capable of registering devices and then registering commands to those devices. These commands have an associated widget (button, slider, etc) that determine the unique properties that the command has.
I am trying to figure out the most generic way to use the model formsets in my application. I have things working, where I create a model_formset for each ModelForm, and get data from each model to place in the formset, and then show it in my template.
What I would really like to do is something like this:
command_formset = modelformset_factory(Command, extra=0, fields='__all__')
formset = command_formset(queryset=Command.objects.filter(device_id=device_id))
Where I get all of my commands (which returns both buttons and sliders) and then make a single command_formset, which has all command objects.
In this case it does part of what I am looking for, where I can query my model and get all button and slider commands, but the formset only includes the fields from command - so the min and max value for the slider are lost.
Is something like this possible? Here is my more complete code:
Models
class Command(models.Model):
command_name = models.CharField(max_length=100)
command = models.CharField(max_length=100)
command_url = models.CharField(max_length=100)
command_type = models.CharField(max_length=100)
device = models.ForeignKey(Device, on_delete=models.CASCADE, default=1) # Each command has one device
def __str__(self):
return self.command_name
class ButtonCommand(Command):
button_color = models.CharField()
class SliderCommand(Command):
min_value = models.IntegerField()
max_value = models.IntegerField()
Forms
class CommandForm(ModelForm):
class Meta:
model = Command
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in iter(self.fields):
self.fields[field].widget.attrs.update({'class': 'form-control'})
class ButtonCommandForm(ModelForm):
class Meta:
model = ButtonCommand
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in iter(self.fields):
self.fields[field].widget.attrs.update({'class': 'form-control'})
class SliderCommandForm(ModelForm):
class Meta:
model = SliderCommand
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in iter(self.fields):
self.fields[field].widget.attrs.update({'class': 'form-control'})
View
command_formset = modelformset_factory(Command, extra=0, fields='__all__')
button_command_formset = modelformset_factory(ButtonCommand, form=ButtonCommandForm, extra=0,
fields=('command_name', 'command', 'command_type', 'command_url'))
slider_command_formset = modelformset_factory(SliderCommand, extra=0,
fields=('command_name', 'command', 'command_type', 'command_url',
'min_value', 'max_value'))
device_form = DeviceForm(initial=device)
formset = command_formset(queryset=Command.objects.filter(device_id=device_id))
button_formset = button_command_formset(queryset=ButtonCommand.objects.filter(device_id=device_id),
prefix="button_form")
slider_formset = slider_command_formset(queryset=SliderCommand.objects.filter(device_id=device_id),
prefix="slider_form")
Edit Additional View Code
template = 'device_control/device_management.html'
return TemplateResponse(request, template, {'device': device,
'device_form': device_form,
'button_formset': button_formset,
'slider_formset': slider_formset,
'formset' : formset})
Another Related Question
input_type = request.POST['command_type']
if input_type == 'button':
form = ButtonCommandForm(request.POST)
elif input_type == 'slider':
form = SliderCommandForm(request.POST)
else:
form = None
Hopefully I am not overwhelming, but the above is a very similar question that seems much simpler. User posts a form, both of which inherit from Command. While the above is quite simple, if I eventually have 20+ different types of CommandForms, this will get fairly nasty.
I am really hoping that I am missing some way that Django can tell which child form should be used to build the form.

Django Crispy Forms rearrange layout and retain Bootstrap inline formatting?

I am currently working on a Django project and am using cripsy-forms to format and display the HTML forms in conjunction with Bootstrap. If I allow Crispy-Forms to use the default layout order, it will (for the most part) format the correct HTML layout to utilize inline forms with Bootstrap.
In order to rearrange the order of the fields on the form, I am attempting to utilize the Layout helper. When I add the Layout helper, Crispy-Forms loses it's inline labeling ability and all labels are displayed above their field counterparts.
How can I get Crispy Forms to re-order the layout and still retain Bootstrap inline formatting?
class MyEntryForm(ModelForm):
def __init__(self, *args, **kwargs):
super(MyEntryForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'id-MyEntryForm'
self.helper.help_text_inline = True
self.helper.error_text_inline = True
self.form_show_labels = True
self.helper.form_method = 'post'
self.helper.form_action = 'submit'
self.helper.form_tag = False
self.helper.add_input(Submit('submit', 'Submit'))
self.helper.form_class = 'form-inline'
self.helper.field_template = 'bootstrap3/layout/inline_field.html'
self.helper.layout = Layout(
'field1',
'field3',
'field2',
)
Have you tried using the fields attribute? The generated Form class will have a form field in the order specified in the fields attribute.
class MyEntryForm(ModelForm):
class Meta:
model = "mymodel"
fields = ['field1', 'field3', 'field2']

Django how to render ModelForm field as Multiple choice

I have a model form like this:
from django.forms import widgets
class AdvancedSearchForm(forms.ModelForm):
class Meta:
model= UserProfile
fields = ( 'name', 'location', 'options')
Where 'options' is a list of tuples and is automatically rendered in template as drop down menu. However I want users to be able to choose multiple options in the search form.
I know I need to add a widget to the form class as I looked at the docs and it seems to me that it needs to be something like this to the class:
widgets = {
'options': ModelChoiceField(**kwargs)
}
However I get this error
name 'MultipleChoiceField' is not defined
So Finally could not figure out how exactly to implement that. So appreciate your help.
ModelChoiceField is not a widget, it's a form field, but to use multiple version of it, you need to override field:
class AdvancedSearchForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(AdvancedSearchForm, self).__init__(*args, **kwargs)
self.fields['options'].empty_label = None
class Meta:
model= UserProfile
fields = ( 'name', 'location', 'options')
then to override the widget to checkboxes, use CheckboxSelectMultiple
widgets = {
'options': forms.CheckboxSelectMultiple()
}