kingadmin后台(3)、修改对象页面开发

目录

urls.py

from django.conf.urls import url
from king_admin import views

urlpatterns = [
    url(r'^(\w+)/(\w+)/(\d+)/change$', views.change_obj, name='change_obj'),  
]

form_handles.py

若当前项目有众多app和models,需要手写modelform也是比较大的工作量。

动态生成modelform:根据传入的类参数动态生成对应的modelform。

from django.forms import ModelForm


def create_model_form(admin_class,add=False):

    class Meta:
        model = admin_class.model  # 指定类
        fields = '__all__'  # 指定字段
        exclude = admin_class.readonly_fields  # 排除指定的字段,也不会生成form对象

        # 若为新增对象,则移除所有exclude字段
        if add:
            exclude=[]

    def __new__(cls, *args, **kwargs):
        """在dynamic_form实例化的时候会调用会先将form传入作为第一参数"""
        print(cls.base_fields)  # 当前记录的所有input标签{'name':name_field_obj,...}
        # 1、循环当前form对象的所有字段名和字段对象
        for field_name, field_obj in cls.base_fields.items():

            # 2、给当前字段对象 增加样式
            field_obj.widget.attrs.update({'class':'form-control'})

            if field_obj.label=='Content':
                # 给字段单独设置textarea样式
                field_obj.widget.attrs.update({'cols':'100','rows':'10','class':'form-control'})

            # 3、筛选出教师角色为教师和顾问的对象,以此修改显示的内容
            if field_name == 'teachers':
                field_obj._queryset = field_obj._queryset.filter(role__title='讲师')
            if field_name == 'consultant':
                field_obj._queryset = field_obj._queryset.filter(role__title='销售')
                pass

            # 4、给只读字段单独设置disabled样式
            if field_name in admin_class.readonly_fields:
                # 注意设置为disabled后,form表单不会提交数据
                if add:
                    pass
                else:
                    field_obj.widget.attrs.update({'disabled': 'true'})

        return ModelForm.__new__(cls)

    # 根据传入的admin_class创建form对象
    dynamic_form = type("DynamicModelForm", (ModelForm,), {'Meta': Meta,'__new__':__new__})
    return dynamic_form

views.py

def change_obj(request, app_name, model_name, id):
    """动态生成并返回modelform"""
    admin_class = site.enable_admins[app_name][model_name]
    # 1、根据id取出对象
    obj = admin_class.model.objects.filter(id=int(id)).first()
    
    # 2、动态创建modelform
    dynamic_form = create_model_form(admin_class)

    if request.method == 'POST':
        # 4、用户提交post数据,暂存到form实例中
        form_obj = dynamic_form(request.POST, instance=obj) 
        
        # 5、验证提交的数据的合法性
        if form_obj.is_valid():
        
            # 6、输入的数据符合规则才能保存到数据库
            form_obj.save()  
            return redirect('/kingadmin/%s/%s' % (app_name, model_name))
        
        errors = form_obj.errors
        return render(request, 'kingadmin/change_info.html', locals())
    
    # 3、向form中填充对象的数据,并返回到前端页面展示
    form_obj = dynamic_form(instance=obj)
    return render(request, 'kingadmin/change_info.html', locals())

change_info.html

<form class="form-horizontal" method="post" novalidate onsubmit="add_select()" STYLE="margin-left: 2%">
    {% csrf_token %}
    
    {#   在此处循环生成form的字段对象  #}
    {% for input_obj in form_obj %}
    <div class="form-group">
      <label for="inputEmail3" class="control-label">{{ input_obj.label }}</label>
    
      <div class="multiple_select_box_container">
    
        {% if input_obj.name in admin_class.filter_horizontal %}
            <!--特殊字段,开发多选框-->
            
        {% else %}

            {{ input_obj }}{% show_error_tips input_obj errors%}
        {% endif %}
      </div>
    </div>
    {% endfor %}
</form>

错误信息提示自定义标签

@register.simple_tag
def show_error_tips(input_obj, errors):
    """根据当前input对象和errors取出当前的input对应的错误信息"""
    
    # 1、取出字段名
    name_attr = input_obj.name
    # 2、判断错误信息中是否有该字段
    if name_attr in errors:
        # 3、取出该字段的错误信息
        error_msg = errors[name_attr][0]
        # 4、制作错误信息标签,返回到页面
        return mark_safe('<span class="error_msg">%s</span>' % error_msg)
    else:
        # 5、错误信息中没有当字段,则返回空
        return ''

filter_horizontal多选框功能开发

左侧为所有可选择课程,右侧为已选择课程,要实现的功能是双击课程则转移到对面的列表中**。效果见下图:

kingadmin.py
filter_horizontal = ['consult_course']  # ManyToMany字段
前端页面
<div class='multiple_select_box_container'>
    {% if input_obj.name in admin_class.filter_horizontal %}
    <!--特殊字段,开发多选框-->
    <div class="col-md-3" id="left_select_container">
        <h5>可选课程</h5>
        
        <select class="form-control" id="left_select_box" multiple="multiple">
            <!--获取所有可选课程-->
            {% get_available_fields admin_class input_obj.name id 1 %}
        </select>
    
        <input type="button" class="btn btn-success" id="add_all_btn" value="add all" style="margin-top: 15px">
    </div>
    
    <div class="col-md-3" id="right_select_container">
        <h5 style="background: #79aec8;color: #fff;">已选择课程</h5>
         <select multiple="multiple" class="form-control" name="{{ input_obj.name }}"
                 id="right_select_box" autocomplete="off">
             <!--获取已选择的课程-->
           {% get_available_fields admin_class input_obj.name id 0 %}
        </select>
     <input class="btn btn-danger" id="remove_all_btn" value="remove all" type="button" style="margin-top: 15px">
    </div>
    
    <span style='margin-left: -490px'>
         {% show_error_tips input_obj errors%}
    </span>
    
    {% else %}
</div>

自定义标签渲染option标签

@register.simple_tag
def get_available_fields(admin_class, field_name, id, left=True):
    """获取客户的咨询课程"""
    model_class = admin_class.model
    html_str = ''
    # 1、查看是否是新增对象页面
    if id:
        # 2、获取客户对象
        obj = admin_class.model.objects.filter(id=int(id)).first()
        
        # 3、取出 客户咨询课程 的集合
        right_set = set(getattr(obj, field_name).all())
    else:
        right_set = set()

    # 4、获取consult_courses字段对象
    f_obj = model_class._meta.get_field(field_name)
    
    # 5、取出字段对象关联的表的所有对象信息,此处为所有的Course对象集合
    total_obj_set = set(f_obj.rel.to.objects.all())
    
    # 6、通过集合运算取出没有选择的课程对象
    left_set = total_obj_set - right_set

    # 7、根据传入的参数确定当前请求是返回的是 左侧 还是 右侧 多选菜单标签
    if left:
        for course_obj in left_set:
            html_str += '<option value=%s >%s</option>' % (course_obj.id, course_obj)
    else:
        for course_obj in right_set:
            html_str += '<option value=%s >%s</option>' % (course_obj.id, course_obj)

    return mark_safe(html_str)
js动态效果

功能

  • 1、双击某个课程则移动至对面的多选框;
  • 2、左侧的add all 按钮 添加所有的课程到右侧;
  • 3、右侧的remove all 按钮 移除所有课程
<script>
// 功能1、采用事件委派监听所有的option的双击事件
$('.multiple_select_box_container').on('dblclick','option',function () {
    
    // 2、取出左侧 或 右侧 多选框 整个标签
    var grand_parent_node = $(this).parent().parent();
    // 3、判断当前的点击事件是发生在左侧还是右侧
    if(grand_parent_node.attr('id')=='left_select_container'){
        var click_node = $(this);
        # 4、确定双击的是左侧标签中的option,则移除当前option
        $(this).remove();
        # 5、在右侧多选框中的子标签中添加此option标签,实现动态显示
        grand_parent_node.next().children('[id=right_select_box]').append(click_node)
    }
    else if(grand_parent_node.attr('id')=='right_select_container'){
        # 同4、5
        var click_node = $(this);
        $(this).remove();
        grand_parent_node.prev().children('[id=left_select_box]').append(click_node);
    }
});


// 功能2:点击添加所有课程
$('#add_all_btn').click(function () {
  $('#right_select_box').append( $('#left_select_box option').remove() )
});

// 功能3:点击移除所有课程
$('#remove_all_btn').click(function () {
    $('#left_select_box').append( $('#right_select_box option').remove() )
});


// 表单提交时,将右侧待提交的所有option的selected属性设置为True,因为由于页面点击操作可能修改右侧option的selected属性
function add_select() {
  $('#right_select_box option').prop('selected',true)
}


// 过滤对象功能
// 1、监听input框的value改变状态
$('#id_consult_course_input').on('input propertychange',function () {
    
    # 2、取出输入值
    search_str = $(this).val();
    
    # 3、循环遍历左侧多选框中的option标签
    $('#left_select_box option').each(function (i,ele) {
        # 4、找到符合条件的option则显示,否则隐藏
        
        if(ele.text.search(search_str)==-1){ 
            ele.hidden=true
        }
        else{
            ele.hidden=false
        }
    })
});

</script>

只读字段设置

1、在设置form对象的时候排除只读字段:

def create_model_form(admin_class,add=False):

    class Meta:
        model = admin_class.model  # 指定类
        fields = '__all__'  # 指定字段
        exclude = admin_class.readonly_fields # 排除指定的字段,也不会生成form对象

2、前端渲染

{% block readonly_fields_display  %}
    {% readonly_fields_display admin_class id %}
{% endblock %}

3、自定义标签手动生成只读字段标签

@register.simple_tag
def readonly_fields_display(admin_class, id):
    readonly_fields = admin_class.readonly_fields
    ele = ''
    obj = admin_class.model.objects.filter(id=int(id)).first()
    for field_name in readonly_fields:
        ele += ''' <div class="form-group">
                <label for="inputEmail3" class="col-sm-2 control-label">%s</label>
                <div class="col-sm-10">
                  <p style="height: 34px;line-height: 34px">%s</p>
                </div>
              </div>''' % (field_name, obj.__dict__.get(field_name, ''))  # 获取当前对象的字段值

    return mark_safe(ele)

猜你喜欢

转载自www.cnblogs.com/fqh202/p/9461517.html