Django - Javascript动态内联FormSet与自动完成 [英] Django - Javascript dynamic inline FormSet with autocomplete

查看:186
本文介绍了Django - Javascript动态内联FormSet与自动完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



模型



我想尝试制作一种调度程序事件编辑器,可以附加参与者。 $ _ code> class Session(models.Model):
start_time = models.DateTimeField()
end_time = models.DateTimeField()

类参与(models.Model):
session = models.ForeignKey(Session)
Particip = models.ForeignKey(User)
status = models.CharField(max_length = 1,choices = STATUSES)

在编辑器中,我想要一个自动完成搜索输入,我可以从中找到用户添加到会话



预览





这里我输入了laurent,我'通过点击其中一个结果名称添加一个人

参与者的颜色取决于他们的状态



我有一个表单定义了 Session 对象开始&结束时间

现在我想我应该有一个内嵌格式集 参与 s



问题




  • 您是否建议我为参与者使用内联表单集 / li>
  • 如何动态地添加/删除参与者行?


解决方案

这个问题似乎很简单,但是正确的回答将涉及几个答案。



我将使用jQuery逐一给出我的解决方案。



自动填充



这是简单的部分。您可以使用像 select2 jqueryui autocomplete 和一个查找用户,如

  def search_users(request):
search = request.GET.get('term')
users = User.objects.filter(
Q(first_name__icontains = search)
| Q(last_name__icontains = search)

ulist = list({'id':u.id,'value':u'%s%s'%(u.first_name,u.last_name)}
for u in user)
return JsonResponse(ulist)

此视图与默认的jQuery UI自动填充插件兼容



动态格式集



这是棘手的一个关键是利用 management_form form.DELETE 。这是我的解决方案:




  • 为参与者使用内联表单(含一个额外的表格)

  • 打印 management_form

  • 通过克隆隐藏的空表单(额外的一个)添加自动完成选择后,使用jQuery添加表单行,并增加 id_form-TOTAL_FORMS

  • 通过隐藏它们并检查隐藏的删除复选框来删除具有jQuery的表单行

  • <

    模板



     < form method = post> {%csrf_token%} 
    {{sessionform}}
    < div>
    {{participant_formset.management_form}}
    < label for =part_search>搜索:< / label>< input id =part_search/>
    < ul id =participation_set>
    {%for members_formset%}中的tform
    {{tform.id}}
    < li>
    < span class =参与者>
    {{tform.participant}} {{tform.instance.participant.name}}
    < / span>
    < span class =status> {{tform.status}}< / span>
    < span class =delete ui-icon ui-icon-circle-minus>
    {{tform.DELETE}}
    < / span>
    < / li>
    {%endfor%}
    < / ul>
    < / div>
    < / form>



    CSS



      / *删除按钮* / 
    #participation_set .delete {
    display:inline-block;
    vertical-align:middle;
    cursor:pointer;
    }

    / *隐藏删除复选框* /
    #participation_set。删除输入{
    display:none;
    }

    / *删除表单* /
    #participation_set li.deleted {
    display:none;
    }

    / *最后一个隐藏的表单克隆* /
    #participation_set li:last-child {
    display:none;
    }



    jQuery



      / *!这将添加一个表单行
    *调用自动完成选择
    * /
    函数add_aform(inst,item){
    if($(':input [name $ = ] [value ='+ item.id +']')。length){
    return false;
    }
    var total = $('#id_'+ inst +'-TOTAL_FORMS')。val();
    var sul ='#'+ inst;
    var li = $(sul +'li:last-child');
    var new_li = li.clone()。appendTo(sul);
    li.find('span.participant')。append(item.label);
    li.find(':input [name $ =participant]')。val(item.id);
    new_li.find(':input')。each(function(){
    var new_name = $(this).attr('name')
    .replace(' - '+总共1)+' - ',' - '+总+' - ');
    $(this).attr('name',new_name);
    });
    new_li.find('label')。each(function(){
    var tmp = $(this).attr('for')
    .replace(' - '+ - 1)+' - ',' - '+ total +' - ');
    $(this).attr('for',new_for);
    });
    new_li.find('。delete')。点击(del_aform);
    $('#id_'+ inst +'-TOTAL_FORMS')。val(++ total);
    }

    / *!
    * /
    函数del_aform(){
    $(this).parents('利 ')addClass(' 删除');
    $(this).find(':checkbox')。attr('checked',true);
    }

    我知道我也可以使用 empty_form 实例,并使用 __前缀__ 替换ids,它简化了JavaScript以提供更好的可维护性,但是我没有找到一种将代码分解为真实的方法表单和空的。



    查看



    该视图是非常标准的,使用 inlineformset_factory extra 设置为1(以获得唯一的隐藏表单来克隆)。还不要忘记使用参与者


    HiddenInput 小部件

    I'm trying to make a kind of scheduler event editor with the ability to attach participants.

    Models

    class Session(models.Model):
      start_time = models.DateTimeField()
      end_time = models.DateTimeField()
    
    class Participation(models.Model):
      session = models.ForeignKey(Session)
      participant = models.ForeignKey(User)
      status = models.CharField(max_length=1, choices=STATUSES)
    

    In the editor I'd like to have an autocomplete search input from which I can find users to add to the session

    Preview

    Here I have typed "laurent" and I'm going to add a person by clicking on one of the resulting names
    Participant colors depend on their status

    I have a form for the Session object defined with start & end times
    Now I think I should have an inline formset for Participations

    Questions

    • Do you suggest that I use an inline formset for the participants ?
    • How can I dynamically add/delete participant rows ?

    解决方案

    The question seems very simple but a proper response would involve several answers.

    I will give my solutions point by point, using jQuery.

    Autocomplete

    This is the simple part. You can use a plugin like select2 or jqueryui autocomplete and a view that finds users like

    def search_users(request):
        search = request.GET.get('term')
        users = User.objects.filter(
          Q(first_name__icontains=search)
        | Q(last_name__icontains=search)
        )
        ulist = list({'id': u.id, 'value': u'%s %s' % (u.first_name, u.last_name)}
            for u in users)
        return JsonResponse(ulist)
    

    This view is compatible with the default jQuery UI Autocomplete plugin

    Dynamic Formset

    This is the tricky one. The key is to take advantage of management_form and form.DELETE. Here is my solution:

    • Use an inline formset for the participants (with one extra form)
    • Print the management_form
    • Add form lines with jQuery after autocomplete selection by cloning a hidden empty form (the extra one) and incrementing id_form-TOTAL_FORMS
    • Delete form lines with jQuery by hiding them and checking a hidden delete checkbox

    Template

    <form method="post">{% csrf_token %}
    {{ sessionform }}
    <div>
    {{ participant_formset.management_form }}
      <label for="part_search">Search: </label><input id="part_search" />
        <ul id="participation_set">
    {% for tform in participant_formset %}
        {{ tform.id }}
          <li>
            <span class="participant">
              {{ tform.participant }}{{ tform.instance.participant.name }}
            </span>
            <span class="status">{{ tform.status }}</span>
            <span class="delete ui-icon ui-icon-circle-minus">
              {{ tform.DELETE }}
            </span>
          </li>
    {% endfor %}
        </ul>
    </div>
    </form>
    

    CSS

    /* Delete button */
    #participation_set .delete {
      display: inline-block;
      vertical-align: middle;
      cursor: pointer;
    }
    
    /* Hidden delete checkbox */
    #participation_set .delete input {
      display: none;
    }
    
    /* Deleted form */
    #participation_set li.deleted {
      display: none;
    }
    
    /* Last hidden form to clone */
    #participation_set li:last-child {
      display: none;
    }
    

    jQuery

    /*! This adds a form line
     * Call it on autocomplete select
     */
    function add_aform(inst, item) {
      if ($(':input[name$="participant"][value=' + item.id + ']').length) {
        return false;
      }
      var total = $('#id_' + inst + '-TOTAL_FORMS').val();
      var sul = '#' + inst;
      var li = $(sul + ' li:last-child');
      var new_li = li.clone().appendTo(sul);
      li.find('span.participant').append(item.label);
      li.find(':input[name$="participant"]').val(item.id);
      new_li.find(':input').each(function () {
        var new_name = $(this).attr('name')
          .replace('-' + (total - 1) + '-', '-' + total + '-');
        $(this).attr('name', new_name);
      });
      new_li.find('label').each(function () {
        var tmp = $(this).attr('for')
          .replace('-' + (total - 1) + '-', '-' + total + '-');
        $(this).attr('for', new_for);
      });
      new_li.find('.delete').click(del_aform);
      $('#id_' + inst + '-TOTAL_FORMS').val(++total);
    }
    
    /*! This removes a form line
     * Call it on click from delete buttons (placed inside each li)
     */
    function del_aform() {
      $(this).parents('li').addClass('deleted');
      $(this).find(':checkbox').attr('checked', true);
    }
    

    I know I could also use an empty_form instance and use __prefix__ to replace the ids which simplifies the javascript for a better maintainability, but I didn't find a way to factorize the code between the true form and the empty one.

    View

    The view is pretty standard using inlineformset_factory with extra set to 1 (to get the only hidden form to clone). Also don't forget to use a HiddenInput widget for the field participant

    这篇关于Django - Javascript动态内联FormSet与自动完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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