In the Django admin interface there is the nice ability to dynamically add new items to foreign key fields and i want to make a similar one using bootstrap modal for popup window and Ajax for form submission and validation.
This is my use case :
This is the Main form for adding Item. Item have a ref and a category.
And this is the second form for adding a new category.
I have no problem with showing the modal and submission the form to add new category. Instead the problem is with the form validation (in case the user submit an empty form), and with refreshing the select content to add the new added category.
This is my code:
class ItemForm(forms.ModelForm):
ref = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}),max_length=255)
category = forms.ModelChoiceField(queryset=ItemCategory.objects.all(), empty_label="(choose from the list)")
class ItemCategoryForm(forms.ModelForm):
category = forms.CharField(
max_length=255,
required=True,
help_text='Add a new category')
def add(request):
if request.method == 'POST':
form = ItemForm(request.POST)
if form.is_valid():
item= Item()
item.ref = form.cleaned_data.get('ref')
item.save()
return redirect('/item_list/')
else:
form = ItemForm()
form1 = ItemCategoryForm()
return render(request, 'item/add.html', {'form': form, 'form1':form1})
def add_category(request):
if request.method == 'POST':
form1 = ItemCategoryForm(request.POST)
if form1.is_valid():
vulnCategory = ItemCategory()
ItemCategory.category = form1.cleaned_data.get('category')
ItemCategory.save()
if request.is_ajax():
#TODO: What Should I redirect
else:
#TODO: What Should I redirect
else:
#TODO: What Sould I do to return errors without reloding the page and to refresh the list of categories
url(r'^add/$', 'add', name='add'),
url(r'^add_category/$', 'add_category', name='add_category'),
And I have also added this jQuery function to load the result
$(".add").click(function () {
$.ajax({
url: '/items/add_category/',
data: $("form").serialize(),
cache: false,
type: 'post',
beforeSend: function () {
$("#add_category .modal-body").html("<div style='text-align: center; padding-top: 1em'><img src='/static/img/loading.gif'></div>");
},
success: function (data) {
$("#add_category .modal-body").html(data);
}
});
});
PS: I know that it may be duplicated, but non of the answers get me to the point.
Below the solution that i have make for inline adding a related Category for an item.
Using the same forms, urls as the question and adding
@login_required
def add_category(request):
data = {}
if request.method == 'POST' :
form = ItemCategoryForm(request.POST)
if form.is_valid():
itemCategory= ItemCategory()
itemCategory.name = form.cleaned_data.get('name')
itemCategory.save()
data['new_itemcategory_value'] = itemCategory.name;
data['new_itemcategory_key'] = itemCategory.pk;
data['stat'] = "ok";
return HttpResponse(json.dumps(data), mimetype="application/json")
else:
data['stat'] = "error";
return render(request, 'item/add_category_modal.html', {'form': form})
else:
form = ItemCategoryForm()
return render(request, 'item/add_category_modal.html', {'form': form})
The tricky part of my solution was separating the modal and the man form in two different html files, and handling the adding and selecting of the new item using jQuery.
This Js code has to be included in the two html file:
// This function is for showing the modal
$(function () {
$(".add_category_show_button").click(function () {
$.ajax({
type: 'GET',
url: '/item/add_category/',
data: $("form").serialize(),
cache: false,
success: function (data, status) {
$('#add_category_modal_id').html(data);
$('#add_category_modal_id').modal()
}
});
}); });
// This second function is for submitting the form inside the modal and handling validation
$(function () {
$(".add_category_submit_button").click(function () {
$.ajax({
type: 'POST',
url: '/item/add_category/',
data: $("form").serialize(),
cache: false,
success: function (data, status) {
if (data['stat'] == "ok") {
$('#add_category_modal_id').modal('hide');
$('#add_category_modal_id').children().remove();
$('#id_category')
.append($("<option></option>")
.attr("value", data['new_itemcategory_key'])
.text(data['new_itemcategory_value']))
.val(data['new_itemcategory_key']);
}
else {
$('#add_category_modal_id').html(data);
$('#add_category_modal_id').modal('show');
}
}
});
}); });
This is how I've done it in the past. Note this is a very abridged version and assumes it's all ajax requests to give you an idea of what you could do. You should be able to expand fron here.
if form.is_valid():
do_form_work()
# Compile a list of lists (or tuples) of the categories
# e.g. [[x.pk, x.name] for x in categories]
categories = get_categories()
json = json.dumps(categories)
return HttpRepsonse(json, {'content_type' : 'application/json'})
else:
# 'template' here is a partial template of just the form
render(self.request, template, context, status=500)
If the form isn't valid you can use the 'error' method on the ajax post to catch the 500 code, and re-display the form with all the form errors. That way you can keep the form and modal open so the user can correct things
If the form is valid, you can take the returning json and re-build your options in the select list.
The ModelChoiceField validates the choice in the form against an object and if it is empty, the form will not validate. You can debug this by using {{ form.non_field_errors }} and {{ field.errors }} to point out exactly why the form isn't getting validated.
As a suggestion, when I had a similar use case I used Dajax and Dajaxice and they worked wonderfully for me. I used ChoiceFields instead but they work even with ModelChoiceFields.
Here's an example with ChoiceField and Form Submission
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With