8

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.

enter image description here

And this is the second form for adding a new category.

enter image description here

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:

forms.py

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')

views.py

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

urls.py

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.

3
  • You can always implement the similar approach django admin uses, it actually loads the view as you would (eg add a category) renders the template by flagging is_popup which in turn removes parts that are not necessary to load (sidebars, headers etc). If you implement the same way (it's not that hard) then you can have the form submission and everything handled by usual django processes (form validation, saving the model etc). Commented Jul 24, 2014 at 12:24
  • The Django Admin interface open a separate popup window, i want to show the popup in the same page and sending data without refreshing page to protect typed data from being lost. Commented Jul 24, 2014 at 13:32
  • You can modify it to generate a modal instead of a popup (modal is actually a popup...). The popup does not refresh the page nor does it redirect you, it only updates the related field when you insert a new entry. Commented Jul 24, 2014 at 13:43

3 Answers 3

9

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

The view for adding category

@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})

Javascript Code

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');
                 }
             }
         });
     }); });
Sign up to request clarification or add additional context in comments.

Comments

3

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.

1 Comment

Thank you for the answer, i found a solution that is based on the same idea i will post it later in details.
1

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

1 Comment

I found the Dajax Solution for my problem, but they said that is not recommended to use it (couplig staff) (github.com/jorgebastida/django-dajax/…)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.