I am working on a project and have ran into an issue with my forms. In particular, I want the form to use Bootstrap4 (from CDN) elements, yet when I try to use the widgets feature of ModelForm, it isn't working quite right
forms.py:
class Meta(object):
model = ScheduleItem
fields = (
'name',
'time_start',
'time_end',
'day',
)
widgets = {
'name': forms.TextInput(
attrs={
'class': 'form-control',
'placeholder': 'test'
}
),
'time_start': forms.TimeInput(
attrs={
'class': "form-control"
}
)
}
def __init__(self, *args, **kwargs):
super(ScheduleItemForm, self).__init__(*args, **kwargs)
self.fields['name'].label = ""
self.fields['time_start'].label = ""
self.fields['time_end'].label = ""
self.fields['day'].label = ""
html page: (The first two are ones using the form and the latter are not using it)
<form action="" method="POST">
{% csrf_token %}
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">Description</label>
{{schedule_item_form.name|as_crispy_field}}
</div>
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">Start Time</label>
{{schedule_item_form.time_start|as_crispy_field}}
{% comment %} <input type="time" class="form-control" aria-label="Username" name="start_time" required> {% endcomment %}
</div>
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">End Time</label>
<input type="time" class="form-control" aria-label="Username" name="end_time" required>
</div>
<div class="input-group mb-3">
<label class="input-group-text" for="inputGroupSelect01">Day of the Week</label>
<select class="form-select" id="inputGroupSelect01">
<option selected>Choose...</option>
<option value="0">Monday</option>
<option value="1">Tuesday</option>
<option value="2">Wednesday</option>
<option value="3">Thursday</option>
<option value="4">Friday</option>
<option value="5">Saturday</option>
<option value="6">Sunday</option>
</select>
</div>
<button type="submit" class="btn btn-primary mb-2 mt-2">Add item to schedule</button>
</form>
Output:
HTML Output
Preferably, I would like all the inputs to look akin to the bottom two. Why does my form look so wacky? Thank you!
What I did to fix this was remove the {{|as_crispy_field}} as it was clashing with the 'form-control' tag in the widgets in my form
Related
I have the following form:
class UpdateForm(forms.Form):
name = forms.CharField(max_length = 15,
widget=forms.TextInput(
attrs={
"class": "form-control",
'required': True,
'placeholder': 'Device name'
}
))
turn_server = forms.ChoiceField(
choices = TURN_CHOICES,
widget=forms.Select(
attrs={
"class": "form-control form-control-sm",
}
))
fps = forms.IntegerField(
widget=forms.NumberInput(
attrs={
'id':'ex1',
'data-slider-id':'ex1Slider',
'type':"text",
'data-slider-min':"10",
'data-slider-max':"60",
'data-slider-step':"1",
}
))
bitrate = forms.IntegerField(
widget=forms.NumberInput(
attrs={
'id':'ex2',
'data-slider-id':'ex2Slider',
'type':"text",
'data-slider-min':"200",
'data-slider-max':"2000",
'data-slider-step':"1",
}
))
class Meta:
model = Device
fields = ['name','fps','bitrate','turn_server']
to be used in the HTML template:
<form action="." method="POST">
{% csrf_token %}
{% for field in form %}
<div class="form-group row{% if field.errors %} invalid{% endif %}">
<label class="col-sm-3 col-form-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
<div class="col-sm-6">
{{ field }}
{% for error in field.errors %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
</div>
{% endfor %}
<button type="submit" class="btn btn-sm btn-light">Save</button>
</form>
and the view.py:
def control(request):
template = 'template.html'
context = {}
context['form'] = UpdateForm(initial={
'name': 'TestName',
'fps': '45',
'turn_server':'TestServer',
'bitrate':'500'
})
return render(request, template, context)
For some reason I can't figure it out, the form rendered in the template does not render with the initial values I have defined with initial. For example the fps resulted input html element is the following:
<input type="text" name="fps" value="10" id="ex1" data-slider-id="ex1Slider" data-slider-min="10" data-slider-max="60" data-slider-step="1" required="" data-value="10" style="display: none;">
Oddly, If I print in the views.py the form that is passed to the context I get the correct form data (ex value=45):
<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" value="TestName" class="form-control" required placeholder="Device name" maxlength="15" id="id_name"></td></tr>
<tr><th><label for="id_turn_server">Turn server:</label></th><td><select name="turn_server" class="form-control form-control-sm" id="id_turn_server">
<option value="1">frankfurt</option>
<option value="2">amsterdam</option>
<option value="3">new_york</option>
</select></td></tr>
<tr><th><label for="ex1">Fps:</label></th><td><input type="text" name="fps" value="45" id="ex1" data-slider-id="ex1Slider" data-slider-min="10" data-slider-max="60" data-slider-step="1" required></td></tr>
<tr><th><label for="ex2">Bitrate:</label></th><td><input type="text" name="bitrate" value="500" id="ex2" data-slider-id="ex2Slider" data-slider-min="200" data-slider-max="2000" data-slider-step="1" required></td></tr>
You can try to define the initial data from the form:
name = forms.CharField(initial='Your name')
Take a look at the documentation here
I'm trying to implement CRUD operations in django, however, I'm stuck in the edit operation.
After passing the data to edit template I need to show the selected values of operating system dropdown list, how can I achieve that?
I utilized the manual rendering of django form with the following code in the template:
<div class="form-group row">
<label class="col-sm-3 col-form-label">Operating System</label>
<div class="col-sm-9">
<select class="form-control" name="os_id" id="os_id" required>
<option value="">--Select--</option>
{% for os in os %}
<option value="{{ os.id}}">{{ os.key_name}}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">Title</label>
<div class="col-sm-9">
<textarea class="form-control" id="title" name="title" rows="3" required>{{ servicedesk.title }}</textarea>
</div>
</div>
here's the view code:
def edit(request, id):
servicedesk=Servicedesk.objects.get(id=id)
softwares=Software.objects.all
os=OperatingSystem.objects.all
context = {'servicedesk':servicedesk, "softwares":softwares, "os":os}
return render(request, 'servicedesks/edit.html', context)
and the model:
class OperatingSystem(models.Model):
key_name = models.CharField(max_length=100,null=True)
key_description = models.CharField(max_length=255,null=True)
class Meta:
db_table = "operating_systems"
def __str__(self):
return self.key_name
class Software(models.Model):
key_name = models.CharField(max_length=100,null=True)
key_description = models.CharField(max_length=255,null=True)
class Meta:
db_table = "softwares"
def __str__(self):
return self.key_name
class Servicedesk(models.Model):
os_id=models.ForeignKey(OperatingSystem, on_delete=models.SET(0))
software_id = models.ForeignKey(Software, on_delete=models.SET(0))
title = models.CharField(max_length=255,null=True)
I tried this but it's not working:
<div class="form-group row">
<label class="col-sm-3 col-form-label">Operating System {{os_id}}</label>
<div class="col-sm-9">
<select class="form-control" name="os_id" id="os_id" required>
<option value="">--Select--</option>
{% for os in os %}
{% if os.id == servicedesk.os_id %}
<option value="{{os.id}}" selected>{{os.key_name}}</option>
{% endif %}
<option value="{{ os.id}}">{{ os.key_name}}</option>
{% endfor %}
</select>
</div>
</div>
When an error is rendered for a non-radio button, I see this:
<div id="div_id_field_a" class="control-group error">
<label for="id_field_a" class="control-label ">Field A</label>
<div class="controls">
<select class="select" id="id_field_a" name="field_a">
<option value="" selected="selected"></option>
<option value="0">0</option>
<option value="1">1</option>
</select>
<span id="error_1_id_field_a" class="help-inline"><strong>Select one</strong></span>
</div>
</div>
When an error is rendered for a radio button, I see this:
<div id="div_id_field_b" class="control-group error">
<label for="id_field_b_0" class="control-label ">Field b</label>
<div class="controls">
<p id="error_1_id_field_b" class="help-block"><strong>Enter a value</strong></p>
<label class="radio"><input type="radio" name="field_b" id="id_field_b_1" value="True">Yes</label>
<label class="radio"> <input type="radio" name="field_b" id="id_field_b_2" value="False">No</label>
</div>
</div>
How can I get the error message to appear after the radio button controls, which is what happens when a combo box used? Or at least not render the error inside a p tag and instead a span tag?
Here's how I render the form:
{% load crispy_forms_tags %}
<form id='main-form'>
{% csrf_token %}
{% crispy form form.helper %}
</form>
and the form:
class MyForm(ReoBaseForm):
field_b = forms.ChoiceField(required=False, choices=((True, 'Yes'), (False, 'No')),
label='Field b', widget=forms.RadioSelect())
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
I am using django-crispy-forms and Bootstrap 3 in my template rendering.
Here is how my form looks like:
As you can notice, the fields are not aligned correctly. I want them to be inline. please provide suggestions on how to fix this:
My crispy form code is below:
class SortFieldsForm(forms.Form):
latest_year=forms.BooleanField(label="latest year")
newest_entry=forms.BooleanField(label="newest post")
price_order=forms.ChoiceField(
widget=forms.RadioSelect,
label="price order",
choices=(('lowest_price','lowest '),('highest_price','highest'),),
initial="lowest_price",
)
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_class = 'form-inline'
self.helper.field_template='bootstrap3/layout/inline_field.html'
self.helper.form_method = 'post'
self.helper.form_action = '.'
super(SortFieldsForm, self).__init__(*args, **kwargs)
self.helper.layout = Layout(
InlineRadios('price_order'),
'newest_entry',
'latest_year',
Submit('submit', 'Submit', css_class='button white'),
)
And the generated HTML code:
<form action="." id="id-exampleForm" class="form-inline" method="post" >
<div id="div_id_price_order" class="form-group">
<label for="id_price_order" class="control-label requiredField">
price order
<span class="asteriskField">*</span>
</label>
<div class="controls ">
<label class="radio-inline">
<input type="radio" checked="checked" name="price_order" id="id_price_order_1" value="lowest_price" >lowest</label>
<label class="radio-inline">
<input type="radio" name="price_order" id="id_price_order_2" value="highest_price" >highest</label>
</div>
</div>
<div id="div_id_newest_entry" class="checkbox">
<label for="id_newest_entry" class=" requiredField">
<input class="checkboxinput checkbox" id="id_newest_entry" name="newest_entry" type="checkbox" />
newest post
</label>
</div>
<div id="div_id_latest_year" class="checkbox">
<label for="id_latest_year" class=" requiredField">
<input class="checkboxinput checkbox" id="id_latest_year" name="latest_year" type="checkbox" />
latest year
</label>
</div>
<input type="submit" name="submit" value="Submit" class="btn btn-primary button white" id="submit-id-submit"/>
</form>
This has nothing to do with crispy-forms itself, but rather that you have a label on the radio inputs that is pushing the actual inputs down. You need to add a margin-top style to div_id_newest_entry, submit-id-submit, and div_id_latest_year. For example (your use case may vary a bit), in your CSS file:
#div_id_newest_entry,
#div_id_latest_year,
#submit-id-submit {
margin-top: 25px;
}
Maybe someone could explain this to me.
With the following models:
class ContactEmail(models.Model):
# Documentation
__doc__ = _(u'Stores an e-mail address for a contact.')
# Enums
CATEGORIES = (
(0, _(u'Personal')),
(1, _(u'Professional')),
)
# Attributes
category = models.IntegerField(choices=CATEGORIES, verbose_name=_(u'category'), help_text=_(u'This values indicates wheter the address is for personal or professional use.'))
email_address = models.EmailField(max_length=255, unique=True, verbose_name=_(u'e-mail address'), help_text=_(u'A valid e-mail address.'))
contact = models.ForeignKey('Contact', related_name=u'emails', verbose_name=_(u'contact'), help_text=_(u'The contact whose the e-mail address is.'))
priority_level = models.PositiveSmallIntegerField(verbose_name=_(u'priority level'), help_text=_(u'An integer used to define a priority level for e-mail addresses of a contact.'))
# Methodes
def __unicode__(self):
return u'%(mail)s' % {u'mail': self.email_address}
# Meta-data
class Meta:
verbose_name = _(u'E-mail')
verbose_name_plural = _(u'E-mails')
unique_together = ('contact', 'priority_level')
class Contact(models.Model):
pass
And the following ModelForms:
class ContactCreateForm(forms.ModelForm):
# Documentation
__doc__ = _(u'A custom form for Contact model.')
# Methods
def __init__(self, *args, **kwargs):
super(ContactCreateForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
if name != 'image':
if field.widget.attrs.has_key('class'):
field.widget.attrs['class'] += ' form-control'
else:
field.widget.attrs.update({'class':'form-control'})
# Meta-data
class Meta:
model = Contact
exclude = ['second_names', 'suffix', 'dob', 'skype_account',]
class ContactEmailCreateForm(forms.ModelForm):
# Documentation
__doc__ = _(u'A custom form for ContactEmail model.')
# Methods
def __init__(self, *args, **kwargs):
super(ContactEmailCreateForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
if field.widget.attrs.has_key('class'):
field.widget.attrs['class'] += ' form-control'
else:
field.widget.attrs.update({'class':'form-control'})
# Meta-data
class Meta:
model = ContactEmail
I'm trying to set up a create contact form that includes a formset for Emails (intention is to use django-dynamic-formset to dynamically adds form just like the Admin does - and actuall it works). Here's the view:
class ContactCreateView(LoginRequiredMixin, CreateView):
template_name = u'frontend/contacts/create.html'
model = Contact
form_class = ContactCreateForm
def get_context_data(self, **kwargs):
context = {
'emails' : inlineformset_factory(parent_model=Contact, model=ContactEmail, form=ContactEmailCreateForm, extra=1),
}
context.update(kwargs)
return super(ContactCreateView, self).get_context_data(**context)
django-dynamic-formset requires you to set can_delete=True which is set by default in inlineformset_factory. And this parameter adds a DELETE field to each form of your formset.
Until here, nothing to complain about. Except that it adds this fields to form.visible_fields which is, IMO, kind of disturbing since this field is hidden if there is no form.instance:
# create.html
<fieldset class="emails">
<legend>{% trans "E-mail(s)" %}</legend>
{{ emails.management_form }}
{% for form in emails %}
<div class="inline-form-emails">
{{ form.media }}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="form-group">
<label for="{{ field.html_name }}" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
{{ field.label }} {% if field.field.required %}<span style="color: #a60000;">*</span>{% endif %}
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
{{ field }}
<span class="help-block">{{ field.help_text }}</span>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</fieldset>
As you can see the output:
<fieldset class="emails">
<legend>E-mail(s)</legend>
<input id="id_emails-TOTAL_FORMS" name="emails-TOTAL_FORMS" type="hidden" value="1"><input id="id_emails-INITIAL_FORMS" name="emails-INITIAL_FORMS" type="hidden" value="0"><input id="id_emails-MAX_NUM_FORMS" name="emails-MAX_NUM_FORMS" type="hidden" value="1000">
<div class="inline-form-emails dynamic-form">
<input id="id_emails-0-contact" name="emails-0-contact" type="hidden">
<input id="id_emails-0-id" name="emails-0-id" type="hidden">
<div class="form-group">
<label for="emails-0-category" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
Category <span style="color: #a60000;">*</span>
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
<select class="form-control" id="id_emails-0-category" name="emails-0-category">
<option value="" selected="selected">---------</option>
<option value="0">Personal</option>
<option value="1">Professional</option>
</select>
<span class="help-block">This values indicates wheter the address is for personal or professional use.</span>
</div>
</div>
<div class="form-group">
<label for="emails-0-email_address" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
E-mail address <span style="color: #a60000;">*</span>
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
<input class="form-control" id="id_emails-0-email_address" maxlength="255" name="emails-0-email_address" type="text">
<span class="help-block">A valid e-mail address.</span>
</div>
</div>
<div class="form-group">
<label for="emails-0-priority_level" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
Priority level <span style="color: #a60000;">*</span>
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
<input class="form-control" id="id_emails-0-priority_level" name="emails-0-priority_level" type="text">
<span class="help-block">An integer used to define a priority level for e-mail addresses of a contact.</span>
</div>
</div>
<div class="form-group">
<label for="emails-0-DELETE" class="col-xs-12 col-sm-5 col-md-3 col-lg-3 control-label">
Delete
</label>
<div class="col-xs-12 col-sm-7 col-md-9 col-lg-9">
<input type="hidden" name="emails-0-DELETE" id="id_emails-0-DELETE">
<span class="help-block"></span>
</div>
</div>
<a class="delete-row" href="javascript:void(0)">remove</a></div><a class="add-row" href="javascript:void(0)">add another</a>
</fieldset>
Anyone has a clue ?
As mentioned here, the problem came from the django-dynamic-form library !
Just in case someone fall into the same trap...