在django admin中使用jquery动态创建一个django表单,像动态内联表单一样 [英] creating a django form in formset dynamically like inline forms in django admin with jquery

查看:694
本文介绍了在django admin中使用jquery动态创建一个django表单,像动态内联表单一样的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个模型发布者 Book 类似于下面



models.py

 类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

 类PublisherForm(ModelForm):
model = Publisher
$ b $ 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.fie lds ['name']。error_messages = {'required':'请输入姓名'}

self.fields ['age']。widget.attrs = {'id':'inputId', 'class':'input-block-level,'placeholder':'Age'}
self.fields ['age']。error_messages = {'required':'请输入年龄'}

views.py



在此视图中,我将发送发布商ID,因为 Book 模型没有发布商模型的外键

  from .forms导入BookForm 

@login_required
def create_multiple_books(request,publisher_id):$ b $ class RequiredFormSet BaseFormSet):
def __init __(self,* args,** kwargs):
super(RequiredFormSet,self).__ init __(* args,** kwargs)
for 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表单在book_formset.forms中:
obj = form.save(commit = False)
obj.publisher_id = publisher_id
obj.save()
返回重定向(reverse('created' ))
else:
book_formset = BookFormset()
c = {'book_formset':book_formset,$ b $'publisher_id':publisher_id,
}
c。 update(csrf(request))
return render_to_response('add_books.html',c,context_instance = RequestContext(request))

template.html



因此,在下面的模板中将表单呈现为形式。 as_p 它的工作正常并且有多个记录正在为发布者ID 创建成功

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

< script type =text / javascript>
$(document).ready(function(){
//代码改编自http://djangosnippets.org/snippets/1389/

函数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,
替换));
if(el.id)el.id = el.id.replace(id_regex,replacement);
if(el.name)el.name = el.name.replace(id_regex ,替换);
}

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

if(formCount> 1){
//删除项目/表单
$(btn).parents('。item')。remove );

var forms = $('。item'); //获取所有表单

//更新表单总数(比以前减少1个)
$('#id_'+ prefix +'-TOTA L_FORMS').val(forms.length);

var i = 0;
//通过表单设置它们的索引,名称和ID
(formCount = forms.length; i< formCount; i ++){
$(forms.get(i) ).children()。children()。each(function(){
updateElementIndex(this,prefix,i);
});
}

} // End if
else {
alert(你必须至少输入一个todo项目!);
}
返回false;



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

//如果(formCount< 10){
//从第一个表单克隆一个表单(没有事件处理程序),则最多只能提交10个待处理项目

var row = $(。item:first)。clone(false).get(0);
//在最后一个表格后插入
$(row).removeAttr('id')。hide()。insertAfter(。item:last)。slideDown(300);

//在新行中删除我们不想要的部分/格式
//例如错误消息
$(。errorlist,row).remove();
$(row).children()。removeClass('error');

//重新标记/重命名所有相关位
$(row).children()。children()。each(function(){
updateElementIndex(this,prefix, );
if($(this).attr('type')=='text')
$(this).val('');
});

//为删除项目/表单链接添加一个事件处理程序
$(row).find('。delete')。click(function(){
return deleteForm (this,prefix);
});

//更新总表格数
$('#id_'+ prefix +'-TOTAL_FORMS')。val(formCount + 1);

} //如果
else {
alert(对不起,您最多只能输入10个项目。
}
返回false;


//注册单击事件处理程序
$(#add)。click(function(){
return addForm(this,'form' );
});
$ b $(。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>待办事项< / h2>
{{book_formset.management_form}}
{%for book_formset.forms%}
< div class =item>
{{form.as_p}}
< p style =>< a class =deletehref =#>删除< / a>< / p>
< / div>
{%endfor%}

< p>< a id =addhref =#>添加另一个项目< / a>< / p>

< input type =submitvalue =Submit/>

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

但是,当我从表单显示html字段并呈现如下

  {%for 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%}

表单显示成功,当我点击链接时添加另一个项目用上面的jquery生成一个新表单,当我尝试通过输入所有细节提交并单击提交时,jquery添加的下一个表单显示验证错误,如名称,年龄是必需的?(这只发生在这种情况下,单独显示字段而不是form.as_p(),如果我们渲染作为form.as_p()其工作正常和记录正在创建数据库)

所以我真的无法弄清楚为什么它成功时,我呈现形式为 form.as_p(),为什么不当我提供具有错误的单个字段



我是否缺少任何东西/需要以及上述JavaScript代码中生成其他表单的任何内容?



因为当我们渲染字段单独生成表单通过点击添加其他表单按钮显示验证错误?



我真的浪费了很多时间来计算上述javascript,所以最后,当我们将表单集表单呈现为 form.as_p()时,上面的函数可以正常工作了。


/ code>,但为什么当我们单独渲染表单字段时上述功能不起作用?



任何人都可以请让我知道如何解决上述问题(也可能是上面的代码将有用的形式,很多用户动态创建表单,就像我们在django admin中的内联表单一样)

编辑



K感谢schillingt,

所以根据你的回答,下面我修改了javascript,和下面的html一样

  {%for 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%}

表单在表单验证



但是我面对的是以下不同的问题


  1. 当我们点击添加另一个项目按钮时,一个新的表单已经成功创建。

  2. 我们提交了空数据的表单,验证错误消息正确显示在相应的字段

第一张


  1. 现在,当我们尝试添加另一个表单时,所有先前的表单(包括错误消息)都会再次显示

就像我们有两个表单并且当我们点击 submit 没有数据,验证错误信息为这两个表单生成,现在立即当我们点击添加另一个项目,完全四个表格已创建,我的意思是先前创建的两个表单重复,包括验证消息

第二期


  1. 所以现在立即当我们尝试删除表单时,我的意思是当我们点击 delete 表单按钮时,所有表单(如 4格式)在这种情况下会被删除吗?

你请让我知道如何解决这个问题?解决方案

解决方案

问题是,代码来获取所有的元素来改变前缀计数器实际上并未选择任何元素。



更改deleteForm:

 <$ c 


$ b

到$ :

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

并在addForm中更改:

   



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


$ b

这将选择POST中表单中包含的所有元素到服务器。


I have a two models Publisher and Book like below

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

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

Here in this view i will send the publisher id, because Book model does not had a foreign Key to Publisher model

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

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

<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

 {% 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)

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

Am i missing anything/need to and anything in the above javascript code that generates additional form?

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,

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 ?

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)

Edit

K thanks schillingt,

So according to ur answer below the have i modified the javascript, and html like below

 {% 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

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.
  2. And when we submitted the forms with empty data, the validation error messages are displayed correctly below the respective fields

Issue one

  1. And now when we try to add another form, all the previous forms including error messages are redisplaying again

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

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 ?

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:

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

to:

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

and change in 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.

这篇关于在django admin中使用jquery动态创建一个django表单,像动态内联表单一样的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆