简体   繁体   English

动态地在formset中创建django表单,就像使用jQuery在django admin中的内联表单一样

[英]creating a django form in formset dynamically like inline forms in django admin with jquery

I have a two models Publisher and Book like below 我有两个模型PublisherBook如下

models.py models.py

class Publisher(models.Model):
    name = models.CharField(max_length=255)

class Book(models.model):
    name = models.CharField(max_length=255)
    price = models.DecimalField()
    generic = generic.GenericForeignKey()
    publisher_id = models.PositiveIntegerField()

forms.py forms.py

class PublisherForm(ModelForm):
    model = Publisher

class BookForm(ModelForm):
    model = Book
    exclude = ('generic', 'publisher_id',)

    def __init__(self, *args, **kwargs):

        super(BookForm, self).__init__(*args, **kwargs)
        self.fields['name'].widget.attrs = {'id':'inputId', 'class':'input-block-level, 'placeholder':'Name'}
        self.fields['name'].error_messages = {'required': 'Please enter name'}

        self.fields['age'].widget.attrs = {'id':'inputId', 'class':'input-block-level, 'placeholder':'Age'}
        self.fields['age'].error_messages = {'required': 'Please enter age'}  

views.py views.py

Here in this view i will send the publisher id, because Book model does not had a foreign Key to Publisher model 在此视图中,我将发送发布者ID,因为Book模型没有发布者模型的外键

from .forms import BookForm

@login_required
def create_multiple_books(request, publisher_id):
    class RequiredFormSet(BaseFormSet):
        def __init__(self, *args, **kwargs):
            super(RequiredFormSet, self).__init__(*args, **kwargs)
            for form in self.forms:
                form.empty_permitted = False

    BookFormset = formset_factory(BookForm, max_num=10, formset=RequiredFormSet)
    if request.method == 'POST':
        book_formset = BookFormset(request.POST, request.FILES)
        if book_formset.is_valid():
            for form in book_formset.forms:
                obj = form.save(commit=False)
                obj.publisher_id = publisher_id
                obj.save()
            return redirect(reverse('created'))
    else:
        book_formset = BookFormset()            
    c = {'book_formset': book_formset,
         'publisher_id':publisher_id,
        }
    c.update(csrf(request))
    return render_to_response('add_books.html',c,context_instance = RequestContext(request))  

template.html template.html

So in the below template rendered the forms as form.as_p its working fine and multiple records are creating to that publisher id successfully 因此,在下面的模板中,将表单呈现为form.as_p它的工作正常,并且成功向该publisher id创建了多个记录

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>

<script type="text/javascript">
$(document).ready(function() {
  // Code adapted from http://djangosnippets.org/snippets/1389/

  function updateElementIndex(el, prefix, ndx) {
    var id_regex = new RegExp('(' + prefix + '-\\d+-)');
    var replacement = prefix + '-' + ndx + '-';
    if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex,
 replacement));
    if (el.id) el.id = el.id.replace(id_regex, replacement);
    if (el.name) el.name = el.name.replace(id_regex, replacement);
  }

  function deleteForm(btn, prefix) {
    var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());

    if (formCount > 1) {
      // Delete the item/form
      $(btn).parents('.item').remove();

      var forms = $('.item'); // Get all the forms

      // Update the total number of forms (1 less than before)
      $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);

      var i = 0;
      // Go through the forms and set their indices, names and IDs
      for (formCount = forms.length; i < formCount; i++) {
        $(forms.get(i)).children().children().each(function() {
          updateElementIndex(this, prefix, i);
        });
      }

    } // End if
    else {
        alert("You have to enter at least one todo item!");
    }
    return false;
  }


  function addForm(btn, prefix) {
    var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());

    // You can only submit a maximum of 10 todo items 
    if (formCount < 10) {
      // Clone a form (without event handlers) from the first form
      var row = $(".item:first").clone(false).get(0);
      // Insert it after the last form
      $(row).removeAttr('id').hide().insertAfter(".item:last").slideDown(300);

      // Remove the bits we don't want in the new row/form
      // e.g. error messages
      $(".errorlist", row).remove();
      $(row).children().removeClass('error');

      // Relabel/rename all the relevant bits
      $(row).children().children().each(function() {
        updateElementIndex(this, prefix, formCount);
        if ( $(this).attr('type') == 'text' )
          $(this).val('');
      });

      // Add an event handler for the delete item/form link 
      $(row).find('.delete').click(function() {
        return deleteForm(this, prefix);
      });

      // Update the total form count
      $('#id_' + prefix + '-TOTAL_FORMS').val(formCount + 1); 

    } // End if
    else {
      alert("Sorry, you can only enter a maximum of ten items.");
    }
    return false;
  }

  // Register the click event handlers
  $("#add").click(function() {
    return addForm(this, 'form');
  });

  $(".delete").click(function() {
    return deleteForm(this, 'form');
  });


});
</script>
</head>
<body>
<form action="" method="POST">{% csrf_token %}
    <div class="section">
        {{ todo_list_form.as_p }}
    </div>

    <h2>Todo Items</h2>
    {{ book_formset.management_form }}
    {% for form in book_formset.forms %}
    <div class="item">
      {{ form.as_p }}
      <p style=""><a class="delete" href="#">Delete</a></p>
    </div>
    {% endfor %}

    <p><a id="add" href="#">Add another item</a></p>

    <input type="submit" value=" Submit " />

</form>
</body>
</html>

But when i display the html fields from the form and render like below 但是当我显示表单中的html字段并呈现如下所示时

 {% for form in book_formset.forms %}
        <div class="item"> 
             <div class="with_name_design">{{ form.name }}</div>
             {% if form.name.errors %}
                  {{form.name.errors}}
             {% endif %}  
             <div class="with_age_design">{{ form.age }}</div> 
             {% if form.age.errors %}
                  {{form.age.errors}}
             {% endif %}    
         </div>
{% endfor %}     

The form is displaying succesfully and when i clicked on the link Add another item a new form is generating with above jquery, and when i tried to submit by entering all the details and clicked on submit,the next form which was added by the jquery is displaying validation errors like name, age is required ?(This is happening only in this case that is displaying the fields seperately instead of form.as_p(), and if we render as form.as_p() its working fine and records are creating in to database) 表单成功显示,当我单击链接Add another item ,上面的jquery生成了一个新表单,当我尝试通过输入所有详细信息提交并单击提交时,由jquery添加的下一个表单是显示name, age is required等验证错误name, age is required吗?(仅在这种情况下才会单独显示字段而不是form.as_p(),并且如果我们将其呈现为form.as_p(),则其工作正常且记录正在创建中到数据库)

So i really could n't able to figure out why it is succeded when i rendered the form as form.as_p() and why not when i rendered individual fields with their errors 所以我真的无法弄清为什么当我将表单呈现为form.as_p()时成功了,为什么当我individual fields with their errors呈现individual fields with their errors时为什么form.as_p()呢?

Am i missing anything/need to and anything in the above javascript code that generates additional form? 我在上面的javascript代码中是否缺少任何需要的东西以及生成其他形式的东西?

because when we render the fields individually, the form generated by clicking on the Add another form button is displaying validation errors ? 因为当我们分别渲染字段时,通过单击“ 添加另一个表单”按钮生成的表单显示了验证错误?

I really wasted a lot of time in figuring out the above javascript, as i got it some where by googling around, 我确实浪费了很多时间来弄清楚上面的javascript,因为我通过谷歌搜索得到了一些地方,

So finally the above functionlality is working when we render the formset forms as form.as_p() , but why the above functionality is not working when we render the form fields individually ? 所以最后,当我们将formset表单呈现为form.as_p() ,上述功能起作用了,但是为什么当我们分别呈现表单字段时,上述功能不起作用了?

Can anyone please let me know how to solve the above issue(Also may be above code will be useful form many users to create the forms dynamically like we have inline forms in django admin) 任何人都可以让我知道如何解决上述问题(也可以是上面的代码将对许多用户有用的形式有用,以动态创建表单,就像我们在django admin中有内联表单一样)

Edit 编辑

K thanks schillingt, 谢谢谢林特,

So according to ur answer below the have i modified the javascript, and html like below 因此,根据您在下面的答案,我是否修改了javascript和html,如下所示

 {% for form in book_formset.forms %}
        <div class="item"> 
             <div class="with_name_design">{{ form.name }}</div>
             {% if form.name.errors %}
                 <span>{{form.name.errors.0}}</span>
             {% endif %}  
             <div class="with_age_design">{{ form.age }}</div> 
             {% if form.age.errors %}
                  <span>{{form.age.errors.0}}</span>
             {% endif %}    
         </div>
{% endfor %} 

and form has been rendered with errors after form validation form validation后, form validation呈现错误

But i am facing the different issue as below 但是我面临如下不同的问题

  1. When we click on Add another item button, a new form has been creating successfully. 当我们单击Add another item按钮时,已经成功创建了一个新表单。
  2. And when we submitted the forms with empty data , the validation error messages are displayed correctly below the respective fields 当我们提交带有empty data的表单时, validation错误消息会正确显示在相应fields下方

Issue one 问题一

  1. And now when we try to add another form , all the previous forms including error messages are redisplaying again 现在,当我们尝试add another form ,所有先前的表单(包括错误消息)将重新显示

like if we have two forms and when we click on submit without data, the validation error messages are generating for both the forms, and now immediately when we click on Add another item , totally four forms have been created, i mean the previously created two forms are repeated including the validation messages 就像我们有two表单一样,当我们单击没有数据的submit时,这两个表单都会生成验证错误消息,现在,当我们单击Add another item ,立即创建了总共four forms ,我的意思是先前创建的两个four forms重复表格,包括验证消息

Issue two 第二期

  1. So now immediately when we try to delete a form, i mean when we click on delete form button, all the forms(like 4 forms ) in this case are deleted ? 所以现在当我们尝试删除表单时,我的意思是当我们单击delete表单按钮时,在这种情况下,所有表单(如4 forms )都被删除了吗?

So how can you please let me know how to solve this ? 那么,如何让我知道如何解决这个问题呢?

The problem is that the code to fetch all of the elements to change the prefix counter aren't actually selecting any elements. 问题在于,获取所有元素以更改前缀计数器的代码实际上并未选择任何元素。

Change in deleteForm: 更改deleteForm:

forms.get(i)).children().children().each

to: 至:

forms.get(i)).find('input,select,textarea').each

and change in addForm: 并更改addForm:

$(row).children().children().each

To: 至:

$(row).find('input,select,textarea').each

This will select all of the elements that would be included in the form on the POST to the server. 这将选择将包含在到服务器的POST上的表单中的所有元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM