Django - birthdate fildset as form widget - django

If I have in forms.py:
birthdate = forms.DateTimeField()
and html:
<fieldset class='birthday-picker'>
<select class='birth-year' name='birth[year]'></select>
<select class='birth-month' name='birth[month]'></select>
<select class='birth-day' name='birth[day]'></select>
<input type='hidden' name='birthdate' />
</fieldset>
Do I need create a new widget or there is an answer for this? If there is no answer i will be grateful for every advice how to do this

There is a built in widget that already does that.
from django.forms import extras
class SomeForm(forms.ModelForm):
birthdate = forms.DateField(widget=extras.SelectDateWidget)

If the html doesn't have to be exactly like this, you might get away easier with a pure javascript solution like the jquery datepicker or use djangos own admin datewidget.

for version >= 1.9
from django import forms
class Some_Form(forms.Form):
birthday = forms.DateField(
widget=forms.SelectDateWidget
)
or
use jquery-ui
$(document).ready(function(){
$('.datepicker').datepicker({ dateFormat: 'yy-mm-dd' });
})

Related

Django & AJAX to show DB objects upon user's input submission

I'm pretty new in the Web development world, have been using Django so far.
I've been trying to figure out how to render data back to page after clicking on a submit button, so I see I'll need to use AJAX for that purpose.
I've created a very simple app just to understand the basics of AJAX.
However, googling, I couldn't really find a basic straight-forward implementation so I kinda got lost...
What I'm trying to achieve:
I have a model called Country:
class Country(models.Model):
name = models.CharField(primary_key=True, max_length=35)
continent = models.CharField(max_length=10)
capital = models.CharField(max_length=35)
currency = models.CharField(max_length=10)
And a super simple main page that asks the user to insert some country name.
The idea is to bring to the page all the info from the DB.
So it would look like this:
Main page HTML body:
<body>
<h2><em>Please type a country name:</em></h2><br><br>
<div class="container">
<form id="get_info" method="post">
{{ form }}
{% csrf_token %}
<input id="submit_button" type="submit" name="submit" value="Get info">
</form>
</div>
</body>
views.py:
from django.shortcuts import render
from country_trivia import forms
def main_page(request):
get_info_form = forms.GetInfo()
return render(request, 'country_trivia/index.html', {'form': get_info_form})
forms.py:
from django import forms
class GetInfo(forms.Form):
country_name = forms.CharField(label="")
I've seen some examples using forms, but I'm not even sure if it's needed, as I've seen some other examples that count on 'onclick' even listeners, then "grab" the text in the search field and pass it via AJAX...
How should I build my AJAX object for that simple purpose, and how should I integrate it?
Do I need to use forms at all?
I don't post anything to DB, just query it and print out data...
Thanks!!

Alternative ways of styling class-based form fields

In Django, one applies CSS styling to class-based form fields in forms.py (or equivalent).
My question: is it impossible to do it any other way inside a Django project?
I'll accept the answer even if the answer is "it's impossible". Hacks and tricks are acceptable as well. Illustrative examples would be great.
p.s. here's an example of a Django form where I've styled in the class-based form:
class SampleForm(forms.Form):
description = forms.CharField(max_length=250)
def __init__(self,*args,**kwargs):
super(SampleForm, self).__init__(*args,**kwargs)
self.fields['description'].widget.attrs['class'] = 'btn bcg'
self.fields['description'].widget.attrs['style'] = 'background-color:#F8F8F8; width:98%; color: #1f8cad;'
self.fields['description'].widget.attrs['autocomplete'] = 'off'
You can use template tags.
css.py
from django import template
register = template.Library()
#register.filter(name='css')
def css(field, css):
return field.as_widget(attrs={"style":css})
in your template:
{% load css %}
{{ item.field|css: 'width: 100px' }}
the result could be
<input id="id_field" name="field" style="width: 100px" type="text" />
As you can see, in style is your variable (width: 100px). You can also do it with class.

Replacement for the default ManyToMany Widget of Forms

I know who to replace the ManyToMany Widget in the django admin interface:
class SomeModelAdmin(admin.ModelAdmin):
filter_horizontal = ('users',)
But how to replace the default widget in all views?
I guess this could be implemented without changing one line in my code or in django.
It would be great if I could the something like the Author part (two boxes for a ManyToMany input) in this picture with a widget:
I can use ctrl + click, but my users can't. They want an easier way.
Is there a way to replace all select-multiple widgets without many changes in my code?
You can use a base class which ovride formfield_for_manytomany
from django.contrib.admin import widgets
class ManyToManyAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
kwargs['widget']= widgets.FilteredSelectMultiple(
db_field.verbose_name,
db_field.name in self.filter_vertical
)
return super(admin.ModelAdmin, self).formfield_for_manytomany(
db_field, request=request, **kwargs)
class SomeModelAdmin(ManyToManyAdmin):
pass
If I do not understand your question, please clarify in the comments.
The Django Form Widgets Documentation offers several pre-fab widgets that you can use to specify how a field in your form is rendered.
Without modifying the code in my views, I can simply add a widget keyword arg to the appropriate form fields.
forms.py (v1)
class TestForm(forms.Form):
CHOICES = (
(1, "choice1"),
(2, "choice2"),
(3, "choice3"),
)
field = forms.ChoiceField(choices=CHOICES)
This renders as a dropdown select:
<select id="id_field" name="field">
<option value="1">choice1</option>
<option value="2">choice2</option>
<option value="3">choice3</option>
</select>
Now adding the widget keyword arg to my field:
forms.py (v2)
field = forms.ChoiceField(choices=CHOICES, widget=forms.CheckboxSelectMultiple)
This renders as a list of checkboxes:
<ul id="id_field">
<li>
<label for="id_field_0"><input id="id_field_0" name="field" type="checkbox" value="1" /> choice1</label>
</li>
<li>
<label for="id_field_1"><input id="id_field_1" name="field" type="checkbox" value="2" /> choice2</label>
</li>
<li>
<label for="id_field_2"><input id="id_field_2" name="field" type="checkbox" value="3" /> choice3</label>
</li>
</ul>
Edit
It is also possible to add widgets that are used by Django Admin. See Django multi-select widget? for more information about this. Simply import the appropriate widget:
from django.contrib.admin.widgets import FilteredSelectMultiple
and use that instead. Note that in this particular case you will also need to include the form's media to get the desired effect.
You need to replace default widgets in django forms.
This works for me:
from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
# replace MultipleChoise in Forms
forms.MultipleChoiceField.widget = FilteredSelectMultiple("verbose name", is_stacked=False)
# replace MultipleChoise in ModelForms
forms.ModelMultipleChoiceField.widget = FilteredSelectMultiple("verbose name", is_stacked=False)
Add code above to file forms_setup.py in your main project
Then in settings.py import file forms_setup.py:
from .forms_setup import *

Django template render each form field based on criteria

I'm using Django 1.7 with bootstrap 3 and want to render each form field the same way, best utilizing bootstrap has-error fields:
<div class="form-group {% if form.field.errors %}has-error has-feedback{% endif %}">
<label class="control-label" for="field">{{ form.field.help_text }}</label>
<div class="input-group">
<span class="input-group-addon"><span class="some-icon"></span></span>
{% render_field form.field class+="form-control" placeholder=form.field.label id="end-date" aria-describedby="inputError2Status" %}
</div>
<label class="control-label" for="field">{{ form.field.errors.as_text }}</label>
</div>
As you can see, the moment the number of fields increase it becomes a LOT of work, not only to write it, but also to maintain it and refactoring becomes a hell... not really DRY. For each field I need to change the form.field variables and id="field" and the icon
How would I write a function, templatetag or something else to make django render all the fields in my form this way? Is it even possible to do this? It would be awesome if the Form in django would have a setting to accept an optionalglypycon class which the renderer will use to render the icon.
-- EDIT --
As indicated by #Serafeim django-crispy-forms could be the solution. But how do I use crispy-forms to produce the above html?
-- EDIT 2 --
After reading up on crispy-forms I managed to come up with the following solution:
My forms.py file looks like this:
class CreateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CreateForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.form_action = 'submit'
self.helper.add_input(Submit('submit', 'Submit'))
self.helper.html5_required = True
self.helper.layout = Layout(
PrependedText('fieldA', '<span class="fa fa-calendar"></span>', placeholder="yyyy-mm-dd"),
PrependedText('fieldB', '<span class="fa fa-clock-o"></span>', placeholder="0:00:00 (h:m:s)"),
'fieldC')
fieldA = forms.DateField(required=True)
fieldB = FDurationField(required=True)
fieldC = forms.CharField(widget=forms.Textarea(attrs={'rows':3}), required=False, max_length=128)
class Meta:
model = AModel
exclude = ('fieldD', 'FieldE', 'fieldF', 'fieldG')
and in my template I only have to call {% crispy form %} which is much better than my initial version. However, I still feel like I have to repeat certain steps, like adding the fields to the Layout... Is it possible to reduce the code even further?
It would be awesome if the Form in django would have a setting to
accept an optional glypycon class which the renderer will use to render
the icon.
Forms are just a collection of fields. A Field is a combination of a widget (which control the HTML rendered) and validation rules.
To control what is rendered on the page, you'd modify the widget.
For example, if I want to specify a css class or a placeholder, I would do this at the widget level:
email = forms.EmailField(label='Email Address',
required=False,
widget=forms.TextInput(attrs={'class': 'form-control',
'placeholder': 'user#example.com'}))
You can create your own custom widgets and fields and obtain fine control over how forms are rendered; so what you are asking is absolutely possible; but as this is something that is often repeated, django-crispy-forms has become the defacto standard way of getting this done.

Django DateInput() Widget appears in Chrome, but not Firefox or IE

Issue
I have a DateInput() widget that appears fine in Chrome, but does not appear in Firefox or IE. I'm using Django 1.6.5 and the latest Chrome (Version 35.0.1916.153) and FireFox(30.0)
Works Correctly in Chrome (Calendar Selector appears)
Does not work correctly in Firefox or IE (Calendar Selector doesn't appear)
forms.py
class DateInput(forms.DateInput):
input_type = 'date'
class TimeForm(forms.ModelForm):
class Meta:
model = Time
fields = ['date_select']
widgets = {
'date_select': DateInput()
}
html
<form method='POST' action=''>{% csrf_token %}
{{ time_form.as_p }}
{{ program_form.as_p }} {# can ignore this part #}
<input type='submit' class="btn btn-lg btn-danger">
</form>
models.py
class Time(models.Model):
date_select = models.DateField()
def __unicode__(self):
return smart_unicode(self.date_select)
This is my first app since the Polls tutorial so let me know if there's more relevant code that I should post here. Thanks for your time.
EDIT AFTER ANSWERS AND COMMENTS
I wanted to include what I did in response to the great comments and answers. I went with the jQuery UI solution by using the code from http://jqueryui.com/datepicker/ To implement it into my project, I added:
html
<!-- Custom CS for JQuery UI -->
<link rel="stylesheet" href="/static/css/jquery-ui-1.10.4.min.css">
<!-- JQuery UI for things like Calendar Widget -->
<script src="/static/js/jquery-ui-1.10.4.min.js"></script>
<!-- Custom JS -->
<script src="/static/js/custom.js"></script>
custom.js
// For JQuery UI Calendar
$(function() {
$( "#id_date_select" ).datepicker();
});
forms.py
class TimeForm(forms.ModelForm):
class Meta:
model = Time
fields = ['date_select']
date_select = forms.DateField()
Django's default date widget is rendered as <input type="date"> in HTML. Chrome is the only major browser that has built in calendar for date input types. FF and IE read it as default text input.
The solution would be to create custom widget in django, that uses some javascript to generate the datepicker. This should point you to the right direction https://docs.djangoproject.com/en/1.6/ref/forms/widgets/#customizing-widget-instances. You could also use some library like jQueryUI(http://jqueryui.com/datepicker/) so you don't have to code it all by yourself.