Reputation: 39
I have a django reporting form , basically like the one here:
class EntryForm(ModelForm):
year = forms.IntegerField()
month = forms.ModelChoiceField(queryset = ... )
report_category = form.ModelChoiceField(queryset = ... ) # FKey to some category model
report_text = forms.CharField()
Initially, user would type in the year value into the text input box, select month, then select category and type the report.
now I want the month dropdown to be populated with the list of months that have no reports yet. After January 2014 report submitted, then next time the user type in the year 2014, the dropdown will be populated with only 11 months (minus January).
I know how to get the querysets done, but I'm still confused as to how to make jQuery/AJAX part for the change on dropdown, after the text input lose focus.
Upvotes: 1
Views: 3025
Reputation: 3672
Here is a working example:
from django.db import models
#min and max values for integer fields
from django.core.validators import MinValueValidator, MaxValueValidator
class CategoryModel(models.Model):
category = models.CharField(max_length=20, unique=True)
def __unicode__(self):
return u"%s" % self.category
class EntryModel(models.Model):
year = models.IntegerField(validators=[MinValueValidator(1900), MaxValueValidator(2100)])
month = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(12)])
report_category = models.ForeignKey(CategoryModel, blank=True, null=True, on_delete=models.SET_NULL)
report_text = models.CharField(max_length=100, blank=True, null=True, default=None)
from django import forms
from models import CategoryModel, EntryModel
class EntryForm(forms.ModelForm):
months = (
('1','January'),
('2','February'),
('3','March'),
('4','April'),
('5','May'),
('6','June'),
('7','July'),
('8','August'),
('9','September'),
('10','October'),
('11','November'),
('12','December'),
)
month = forms.ChoiceField(choices=months)
#hidden control used to populate the month drop down list
month_hidden=forms.ChoiceField(choices=months)
report_category = forms.ModelChoiceField(queryset = CategoryModel.objects.all())
class Meta:
model=EntryModel
from django.conf.urls import patterns, url
from views import EntryCreateView
urlpatterns=patterns('so_23711764.views',
url(r'^/?$', EntryCreateView.as_view(), name='display_entry'),
url(r'^reload_controls.html$', 'reload_controls_view', name='reload_controls'),
)
# Create your views here.
from django.views.generic.edit import CreateView
from models import EntryModel
from forms import EntryForm
#use json for the ajax request
from django.http import HttpResponse
from django.utils import simplejson
class EntryCreateView(CreateView):
model = EntryModel
form_class=EntryForm
template_name = "so_23711764/index.html"
#~ fields = ['name']
#view called with ajax to reload the month drop down list
def reload_controls_view(request):
context={}
#get the year that the user has typed
year=request.POST["year"]
#get months without reports (months to be displayed in the drop down list)
context["months_to_display"]=list(EntryModel.objects.filter(year=year, report_category__isnull=True).values_list('month', flat=True).distinct())
return HttpResponse(simplejson.dumps(context), mimetype="application/json")
<!-- css of the application -->
<link rel="stylesheet" href="{{STATIC_URL}}css/so_23711764.css?{% now 'U' %}" />
<form id="entry_form" method="post" action="{% url display_entry %}">
{% csrf_token %}
{{ form.as_p }}
<!-- in urls.py, set the path to the view reload_controls -->
<div id="reload_controls_view" style="display: none;">{% url reload_controls %}</div>
</form>
<!-- jquery -->
<script type="text/javascript" src="{{ STATIC_URL }}jquery.min.js"></script>
<!-- csrf file to avoid 403 (FORBIDDEN) on ajax views -->
<script type="text/javascript" src="{{ STATIC_URL }}csrf.js"></script>
<!-- js related to the application -->
<script type="text/javascript" src="{{ STATIC_URL }}js/so_23711764.js?{% now 'U' %}"></script>
/* bind the event to the form, so it still works after controls are reloaded with ajax */
$('#entry_form').on('blur', '#id_year', function()
{
reload_controls(this.value);
});
/* update the month drop down list with ajax */
function reload_controls(year)
{
$.ajax
({
type: 'POST',
url: $("#reload_controls_view").text(),
dataType: 'json',
data: "year="+year,
success: function(result)
{
//empty month drop down list
$("#id_month").empty()
//add months with no report
$.each( result.months_to_display, function( index, value )
{
//use the hidden drop down list to populate the month field
month=$("#id_month_hidden option[value='" + value + "']").text()
//add months to the drop down list
$('#id_month').append('<option value="'+value+'">'+month+'</option>')
});
},
error: function(xhr, status, error)
{
window.console&&console.log(xhr.responseText);
}
});
}
/* hide month_hidden control */
#id_month_hidden, label[for="id_month_hidden"]
{
display: none;
}
Now, If I access the page http://127.0.0.1:8000/so_23711764/
(django development server), I get:
With this entrymodel table:
If I type "2014" for the year, when the control loses the focus, I get :
Hope it helps :).
Upvotes: 3