Django - custom controls HTML in crispy forms - django

I try to use Crispy forms for ModelForm in Django. However, I need custom HTML code for all fields.
Here is sample field code generated by crispy forms:
<div class="span12 field-box">
<div id="div_id_user" class="control-group">
<label for="id_user" class="control-label requiredField">
User select
<span class="asteriskField">*</span>
</label>
<div class="controls">
<select id="id_user" class="select" name="user">
<option value="" selected="selected">---------</option>
<option value="1">User 1</option>
<option value="2">User 2</option>
</select>
</div>
</div>
</div>
but I would need code generated like:
<div class="span12 field-box">
<label>User:</label>
<div class="ui-select span5">
<select>
<option value="1">User 1</option>
<option value="2">User 2</option>
</select>
</div>
</div>
I think it would be enough if I will be able to just add custom class to div.controls - But I have no idea how to achieve that.
Here is crispy form code:
self.helper.layout = Layout(
Div(
Field('user'),
css_class="span12 field-box",
),
)

How about setting a class in field layout object:
Field('user', css_class='span5')

Related

How to send variables from X-DATA to the server. Alpine JS & HTMX & Django?

I have got the next snippet. There are a few select blocks. What I would like to do is to collect both variables and send to the Django server. But the request.GET is empty. What is wrong?
<div class="flex" x-data="{ foo: '', bar: ''}">
<div class="flex justify-between">
<div class="mt-1">
<select x-model="foo"
name="foo"
id="foo"
class="w-64 h-14 text-sm border-gray-300 focus:bg-transparent">
<option value="">Type of FOO</option>
<option value="FOO_1">FOO_1</option>
<option value="FOO_2">FOO_2</option>
<option value="FOO_1">FOO_3</option>
</select>
</div>
<div class="mt-1">
<select name="bar"
x-model="bar"
id="bar"
class="w-64 h-14 text-sm border-gray-300 focus:bg-transparent">
<option value="">BAR Types</option>
<option value="BAR_1">BAR_1</option>
<option value="BAR_2">BAR_2</option>
<option value="BAR_3">BAR_3</option>
</select>
</div>
<input type="text" name="foo" x-model="foo" hidden />
<input type="text" name="bar" x-model="bar" hidden />
<button
hx-get="{% url 'server:parse-values' %}"
hx-target="#element"
hx-swap="innerHTML"
hx-include="[name='foo', name='bar']">
<span
class="cursor-pointer px-3 py-3 border-2">Parse details</span>
</button>
</div>
</div>
But when I click on the button the Django backend does not receive foo and bar parameters.
Any thoughts?
To include multiple inputs you need to separate them with a comma like this:
hx-include="input[name='foo'], input[name='bar']"
That would be the equivalent of using querySelectorAll to grab your inputs:
document.querySelectorAll("input[name='foo'], input[name='bar']")
I added the additional input so it excludes the select lists, but you can probable remove the hidden inputs from your example and just include the lists.
I should also add that if you move the htmx attributes from the button to the x-data div and change that into a form. Then change the button to type submit. All the form data will be automatically sent to the server.
<form class="flex" x-data="{ foo: '', bar: ''}"
hx-get="{% url 'server:parse-values' %}"
hx-target="#element"
hx-swap="innerHTML"
>
<div class="flex justify-between">
<div class="mt-1">
<select x-model="foo"
name="foo"
id="foo"
class="w-64 h-14 text-sm border-gray-300 focus:bg-transparent">
<option value="">Type of FOO</option>
<option value="FOO_1">FOO_1</option>
<option value="FOO_2">FOO_2</option>
<option value="FOO_1">FOO_3</option>
</select>
</div>
<div class="mt-1">
<select name="bar"
x-model="bar"
id="bar"
class="w-64 h-14 text-sm border-gray-300 focus:bg-transparent">
<option value="">BAR Types</option>
<option value="BAR_1">BAR_1</option>
<option value="BAR_2">BAR_2</option>
<option value="BAR_3">BAR_3</option>
</select>
</div>
<button type="submit">
<span class="cursor-pointer px-3 py-3 border-2">Parse details</span>
</button>
</form>

Django Boostrap TimePicker/Forms not working

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

Radio buttons are rendering on top of rest of form

I'm using vanilla bootstrap with Python on Django. I've configured a form with radio buttons, however the buttons render on top of the fields as shown in the screenshot below.
I've notice that Django puts the radio buttons into a list and I thought that could be the cause, so I tried using CSS to disable the tag but the radio buttons still float on top just without the list bullets.
Forms.py
class MerchantGroupForm(forms.Form):
DO_WHAT_CHOICES=[('merge','Merge'),
('update','Update')]
#do_what = forms.ChoiceField(choices=DO_WHAT_CHOICES, widget=forms.RadioSelect(attrs={'class': "custom-radio-list"}))
do_what = forms.ChoiceField(choices=DO_WHAT_CHOICES, widget=forms.RadioSelect)
merge_merchantgroup = forms.ModelChoiceField(required=False, queryset=MerchantGroup.objects.all().order_by('name'), empty_label="Merge with")
name = forms.CharField(required=False)
default_ledger = GroupedModelChoiceField(required=False, queryset=Ledger.objects.all().order_by('coa_sub_group__name','name'), choices_groupby = 'coa_sub_group')
disable_AI = forms.BooleanField(required=False, label='Disable AI')
<form action="/monzo/merchantgroups/update/799/" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="ZWtsz3JDsnUtu1mj6NO3SDlBuyJyEpDgbZUDC6elfTPK2DCwWevD2BpirSZJOhiM">
<div class="form-group">
<label for="id_do_what_0">Do what:</label>
<ul id="id_do_what" class="custom-radio-list form-control">
<li><label for="id_do_what_0"><input type="radio" name="do_what" value="merge" class="custom-radio-list form-control" required id="id_do_what_0">
Merge</label>
</li>
<li><label for="id_do_what_1"><input type="radio" name="do_what" value="update" class="custom-radio-list form-control" required id="id_do_what_1">
Update</label>
</li>
</ul>
</div>
<div class="form-group">
<label for="id_merge_merchantgroup">Merge merchantgroup:</label>
<select name="merge_merchantgroup" class="form-control" id="id_merge_merchantgroup">
<option value="" selected>Merge with</option>
<option value="203">ATM</option>
<option value="799">Amazon</option>
<option value="200">Post Office</option>
<option value="201">Virgin Media</option>
<option value="202">www.modelsport.co.uk</option>
</select>
</div>
<div class="form-group">
<label for="id_name">Name:</label>
<input type="text" name="name" value="Amazon" class="form-control" id="id_name">
</div>
<div class="form-group">
<label for="id_default_ledger">Default ledger:</label>
<select name="default_ledger" class="form-control" id="id_default_ledger">
<option value="">---------</option>
<optgroup label="Accounts">
<option value="20">Jacks Account</option>
<option value="32">Jacks Monzo</option>
<option value="21">Janes Account</option>
<option value="126">Janes Monzo</option>
<option value="22">Joint Account</option>
</optgroup>
<optgroup label="Bills">
<option value="7">Council tax</option>
<option value="6">Electricity & Gas</option>
</optgroup>
<optgroup label="Cash">
<option value="23">Jacks Float</option>
<option value="24">Janes Float</option>
</optgroup>
<optgroup label="Food & Home">
<option value="8">Basic food</option>
<option value="9">Coffee & Snacks</option>
<option value="11">Home Supplies</option>
<option value="10">Take away</option>
</optgroup>
</optgroup>
<optgroup label="Property">
<option value="19">Property</option>
</optgroup>
<optgroup label="Purchases">
<option value="31">Computer & Electrical</option>
<option value="30">Hobbies</option>
<option value="29" selected>Toys</option>
</optgroup>
<optgroup label="Reserves">
<option value="27">Profit & Loss Accounts</option>
</optgroup>
<optgroup label="Taxation">
<option value="5">Income tax</option>
</optgroup>
<optgroup label="Taxation">
<option value="25">Tax Liability</option>
</optgroup>
<optgroup label="Travel">
<option value="17">Public Transport</option>
<option value="18">Taxis</option>
</optgroup>
</select>
</div>
<div class="form-group">
<label for="id_disable_AI">Disable AI:</label>
<input type="checkbox" name="disable_AI" class="form-control" id="id_disable_AI" checked>
</div>
<input type="submit" class="btn btn-primary">
</form>
If the screenshot you showed have template the previous .html code then Django does nothing because you don't use Django form to render your form. It's just a plain HTML/Bootstrap form.
This is how to use Django Form.
Also if you want to Group radios on the same horizontal row with Bootstrap then you can add .form-check-inline to any .form-check as Bootstrap inline radio button page says.
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="merge"
value="merge">
<label class="form-check-label" for="merge">Merge</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="update"
value="update">
<label class="form-check-label" for="update">update</label>
</div>
NB : I think that your first .form-group is not well structured.
You can visit Bootstrap form to do it fine.
Removing the form-control class did the trick. According to Bootstrap docs this class is only for certain fields, guess radio is not one of them.

crispy form field type attribute ignored

I am trying to use crispy forms in my django app and I can not seem to get the type attribute to set on a field.
#forms.py
class ReminderForm(ModelForm):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.helper=FormHelper()
self.helper.layout = Layout(
Fieldset('Reminder',
Row(
Div(Field('message'),css_class="col"),
),
Row(
Div(Field('messagetype'),css_class="col-md-6"),
Div(Field('account'),css_class="col-md-6"),
),
Row(
Div(Field('reminddate',type="date"),css_class="col-md-6"),
Div(Field('duedate', type="date"),css_class="col-md-6"),
),
)
)
class Meta:
model=Reminder
exclude=['sentdate','confirmdate','completedate']
my modal form
{% load cms_tags crispy_forms_tags %}
<div class="modal fade" id="{{formid}}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{title}}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form >
{% crispy form %}
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
rendered html for the inputs below show reminddate and duedate style with type="text"
<div class="modal-body">
<form>
<fieldset> <legend>Reminder</legend> <div class="form-row "> <div class="col"> <div id="div_id_message" class="form-group"> <label for="id_message" class="">
Message
</label> <div class=""> <textarea name="message" cols="40" rows="10" class="textarea form-control" id="id_message"></textarea> </div> </div> </div>
</div>
<div class="form-row "> <div class="col-md-6"> <div id="div_id_messagetype" class="form-group"> <label for="id_messagetype" class="">
Messagetype
</label> <div class=""> <select name="messagetype" class="select form-control" id="id_messagetype"> <option value="">---------</option> <option value="email">Email</option> <option value="sms">Text Message</option> <option value="internal">Internal Website</option>
</select> </div> </div> </div>
<div class="col-md-6"> <div id="div_id_account" class="form-group"> <label for="id_account" class="">
Account
</label> <div class=""> <select name="account" class="select form-control" id="id_account"> <option value="" selected="">---------</option> <option value="4">jim.pm</option> <option value="2">joe.sp</option> <option value="3">sally.om</option> <option value="1">sparrow</option>
</select> </div> </div> </div>
</div>
<div class="form-row "> <div class="col-md-6"> <div id="div_id_reminddate" class="form-group"> <label for="id_reminddate" class=" requiredField">
Reminddate<span class="asteriskField">*</span> </label> <div class=""> <input type="text" name="reminddate" class="datetimeinput form-control" required="" id="id_reminddate"> </div> </div> </div>
<div class="col-md-6"> <div id="div_id_duedate" class="form-group"> <label for="id_duedate" class=" requiredField">
Duedate<span class="asteriskField">*</span> </label> <div class=""> <input type="text" name="duedate" class="datetimeinput form-control" required="" id="id_duedate"> </div> </div> </div>
</div> </fieldset> </form>
</div>
rendred form
How do I correctly set the type attribute? Other attributes(custom attributes) work fine.
#Martin Beran pointed me in the right direction. Since I am trying to stay with the {% crispy form %} method... I found that using the widgets override in the Meta class provided what I needed!
class Meta:
model=Reminder
exclude=['sentdate','confirmdate','completedate']
widgets = {
'reminddate': forms.DateInput(attrs={'type':'date'}),
'duedate': forms.DateInput(attrs={'type':'date'})
}
Form with bootstrap date control
Oh, you want type=date, that's a tricky one... you should make your own widget then, but if you are lazy, like me, you make a workaround with a template filter:
from django import forms
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
#register.filter
def set_input_type(field, field_type=None):
if field_type:
pass
elif isinstance(field.field.widget, forms.DateInput):
field_type = 'date'
elif isinstance(field.field.widget, forms.TimeInput):
field_type = 'time'
elif isinstance(field.field.widget, forms.SplitDateTimeWidget):
for subfield in field.field.widget.widgets:
if isinstance(subfield, forms.DateInput):
subfield.input_type = 'date'
elif isinstance(subfield, forms.TimeInput):
subfield.input_type = 'time'
elif isinstance(field.field.widget, forms.DateTimeInput):
# field_type = 'datetime-local' # can't work with passing/returning ISO format
# field_type = 'datetime' # is deprecated, doesn't work in many browsers
# use widget=forms.SplitDateTimeWidget() instead
pass
if field_type:
field.field.widget.input_type = field_type
return field
so you can change the type in template like
{{ form.dt|set_input_type:'date' }}
but ofc if you use just {% crispy form %}, you have to go the first (preferable) way and make your own widget :)
also another way is to use JS, like $('#id_reminddate').attr('type', 'date');
btw they probably use type=text, because type=date is ugly in desktop browsers, so you use some JS library like jqueryui for callendars

Django - how to modify crispy forms?

First, I want to generate a page that looks like this:
There are three things that makes it difficult.
1. Label + Input field set should line up horizontally
2. Some of the fields have checkbox
3. Date Field has a special Bootstrap plugin.
Here are the html source code for each types:
generic type:
<div class="col-lg-2 col-md-2 col-sm-4 col-xs-6" style="margin-bottom: 5px">
<label class="input-upper-title">Drill String Name</label>
<input type="text" id="" class="form-control input-field-height-vertical" name="" required="">
</div>
has checkbox:
<div class="col-lg-2 col-md-2 col-sm-4 col-xs-6" style="margin-bottom: 5px">
<label class="input-upper-title">String Length (ft)</label>
<div class="has-checkbox">
<input type="checkbox"><input disabled="disabled" type="text" id="" class="form-control input-field-height-vertical input-calculated" name="" data-parsley-trigger="" required="">
</div>
</div>
has date plugin:
<div class="col-lg-2 col-md-2 col-sm-4 col-xs-6" style="margin-bottom: 5px">
<label class="input-upper-title">Date Run</label>
<div class="form-group">
<div class="input-group date" id="datetimepicker7" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input input-field-height-vertical" data-target="#datetimepicker7"/>
<div class="input-group-append" data-target="#datetimepicker7" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
</div>
</div>
And here is my forms.py:
class BHA_overall_Form(forms.ModelForm):
prefix = 'bha_overall'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
class Meta():
model = BHA_overall
fields = '__all__'
How should I modify my forms.py, using crispy_forms to get the same effect as my html source code does?