如何快速的进行数据的添加以及修改呢?modelform来实现是可以达到效果的,在这里就是应用了modelform,每一个表都不同,所以需要创建不同的modelform。
def get_model_form_class(self, is_add,request,pk, *args,**kwargs): """ 获取添加、修改功能的modelform :return: """ if self.model_form_class: return self.model_form_class class AddModelForm(BaseModelForm,forms.ModelForm): class Meta: model = self.model_class fields = '__all__' return AddModelForm
是否注意到创建modelform时继承了BaseModelForm,实际上就是将request对象传入modelform中
class BaseRequestModelForm(object): def __init__(self, request, *args, **kwargs): self.request = request super(BaseRequestModelForm, self).__init__(*args, **kwargs) class BaseModelForm(BaseRequestModelForm,forms.ModelForm): def __init__(self,request,*args,**kwargs): super().__init__(request,*args,**kwargs) #####给modelform字段加样式 for name,field in self.fields.items(): attrs_dict={'class':'form-control'} if 'DateTimeField' in field.__repr__(): attrs_dict = {'class': 'form-control', 'date_time': 'datetimepicker', 'size': '16'} field.widget.attrs.update(attrs_dict)
这就是在之前提到过的在执行视图函数之前装饰器的作用。
def wrapper(self, func): # 将视图函数加上装饰器,这样可以在处理视图之前之后都可以加上一定的功能 @functools.wraps(func) # 保留原函数的信息 def inner(request, *args, **kwargs): self.request = request # 将request传给当前对象,在处理视图函数之前 BaseRequestForm(request) BaseRequestModelForm(request) return func(request, *args, **kwargs) return inner
那么为什么要这样做呢?给一个场景就是客户付款的订单只是自己所有的订单,并不包括其他客户的订单。
class PaymentRecordModelForm(BaseModelForm): class Meta: model=models.PaymentRecord exclude=['confirm_date','confirm_user'] def __init__(self,request,*args, **kwargs): #form中没有传入request对象 super().__init__(request,*args, **kwargs) current_user_id = self.request.session['user_info']['id'] customer=models.Customer.objects.filter(name=models.UserInfo.objects.filter(id=current_user_id).first().username).first() self.fields['order'].queryset = models.Order.objects.filter(customer=customer)
接下来就是添加、修改的视图函数
def add_view(self,request,*args,**kwargs): # 处理所有添加功能,使用ModelForm来实现 Add_Model_Form = self.get_model_form_class(True,request,None,*args,**kwargs) if request.method == 'GET': form = Add_Model_Form(request=request) return render(request, 'stark/change.html', {'form': form,'starkclass':self}) form = Add_Model_Form(request=request,data=request.POST) if form.is_valid(): obj=self.save(request,form,False,*args,**kwargs) # obj=form.save() pop_post_id=request.GET.get('pop_id') if pop_post_id: res={'pop_post_id':pop_post_id,'pk':obj.pk,'obj':str(obj)} return render(request,'stark/pop.html',res) return redirect(self.reverse_changelist_url(*args,**kwargs)) return render(request, 'stark/change.html', {'form': form})
def change_view(self, request,pk,*args,**kwargs): """ 处理所有修改表单 :param request: :param pk: :return: """ Edit_Model_Form = self.get_model_form_class(False,request,pk,*args,**kwargs) obj = self.model_class.objects.filter(pk=pk).first() if not obj: return HttpResponse('该用户不存在') if request.method == 'GET': form = Edit_Model_Form(request,instance=obj) return render(request, 'stark/change.html', {'form': form}) form = Edit_Model_Form(request=request,data=request.POST,instance=obj) if form.is_valid(): self.save(request,form,True,*args,**kwargs) return redirect(self.reverse_changelist_url(*args,**kwargs)) filter_horizontal=self.get_filter_horizontal() el=EditList(self,request,pk,filter_horizontal,*args,**kwargs)#修改和添加共用一个页面,所以form没有进行封装 return render(request, 'stark/change.html', {'form': form,'el':el})
注意在保存时并没有直接保存,而是预留了保存的钩子函数,这样在保存前还可以做一些其它动作。
####对于一些排除字段,重新此方法进行添加 def save(self,request,form,is_modify,*args,**kwargs): return form.save()
在这里添加和修改共用了一个模板页面,添加中包括了pop功能,和djangoadmin类似,凡是foreignkey字段都可以进行点击添加。修改页面中有filter_horizontal功能,主要就是针对ManyToMany字段。
<form class="change" method="post" novalidate> {% csrf_token %} {% for filed in form %} <div class="form-group" style="position: relative"> {% if field.name in el.filter_horizontal %} <label>{{ filed.label }}</label> {% m2m_all_data form field el.stark_class %} <span class="errors pull-right" style="color: red">{{ filed.errors.0 }}</span> {% else %} <label>{{ filed.label }}</label> {% gen_is_pop filed starkclass %} <span class="errors pull-right" style="color: red">{{ filed.errors.0 }}</span> {% endif %} </div> {% endfor %} <button type="submit" class="btn btn-primary">保存</button> </form>
在添加页面中对后台传递的form字段进行判断,如果是ManyToMany就执行m2m_all_data 标签方法。
@register.inclusion_tag('stark/m2m.html') def m2m_all_data(form,field,stark_class): return {'m2m_data':m2m_data(form,field,stark_class),'m2m_un_data':m2m_un_data(form,field,stark_class),'field':field}
@register.simple_tag() def m2m_data(form,field,stark_class): """ 出项在左侧的可选项 :param form: :param field: :param stark_class: :return: """ field_name=field.name field_obj=stark_class.model_class._meta.get_field(field_name) if isinstance(field_obj,ManyToManyField): data_set = set(field_obj.remote_field.model.objects.all()) if form.instance.id: selected_data_set=set(getattr(form.instance,field_obj.name).all()) queryset=data_set-selected_data_set return queryset return data_set @register.simple_tag() def m2m_un_data(form,field,stark_class): """ 出项在右侧已选项 :param form: :param field: :return: """ field_name = field.name field_obj=stark_class.model_class._meta.get_field(field_name) if isinstance(field_obj,ManyToManyField): if form.instance.id: selected_data_set = getattr(form.instance, field_obj.name).all() return selected_data_set else: return []
这里利用了inclusion_tag方法
{% load stark %} <div class="row"> <div class="col-lg-6"> <input type="search" oninput="M2mSearch(this)" class="form-control"> <select id="id_{{ field.name }}_from" multiple class="multiselect" class="form-control"> {% for row in m2m_data %} <option ondblclick="MoveElement(this,'id_{{ field.name }}_to')" value="{{ row.id }}">{{ row }}</option> {% endfor %} </select> <p><a onclick="MoveAllElements('id_{{ field.name }}_from','id_{{ field.name }}_to')">全选</a></p> </div> <div class="col-lg-6"> <input type="search" oninput="M2mSearch(this)" class="form-control"> <select id="id_{{ field.name }}_to" selected_data="selected_m2m" multiple class="multiselect" name="{{ field.name }}" class="form-control"> {% for row in m2m_un_data %} <option ondblclick="MoveElement(this,'id_{{ field.name }}_from')" value="{{ row.id }}">{{ row }}</option> {% endfor %} </select> <p><a onclick="MoveAllElements('id_{{ field.name }}_to','id_{{ field.name }}_from')">移除</a></p> </div> </div>
需要用到的script代码
function readysubmit() { $('select[selected_data] option').prop('selected', true); } function MoveElement(ths, target_id) { var $target_from_id = $(ths).parent().attr('id'); var op = $('<option></option>'); op.attr('ondblclick', 'MoveElement(this,"' + $target_from_id + '")'); op[0].value = $(ths).val(); op[0].text = $(ths).text(); $('#' + target_id).append(op); $(ths).remove() } function MoveAllElements(from_id, target_id) { $('#' + from_id).children().each(function () { MoveElement($(this), target_id) }) } function M2mSearch(ths) { var $searchText = $(ths).val().toUpperCase(); $(ths).next().children().each(function () { var $matchText = $(this).text().toUpperCase().search($searchText); if ($matchText != -1) { $(this).show() } else { $(this).hide() } }) }
另外一个就是pop功能了,主要解决Foreignkey的问题
@register.simple_tag() def gen_is_pop(bfield,starkclass): if isinstance(bfield.field,ModelChoiceField): namespace=starkclass.site.namespace related_app_lebel=bfield.field.queryset.model._meta.app_label related_model_name=bfield.field.queryset.model._meta.model_name url_name='%s:%s_%s_add'%(namespace,related_app_lebel,related_model_name) add_url=reverse(url_name) add_url=add_url+'?pop_id=id_%s'%bfield.name return mark_safe("""<a οnclick="pop('%s')" style="position: absolute;right: -30px;top: 20px"><span style="font-size: 28px">+</span></a>"""%add_url) return bfield
后台返回的数据先返回给另一个html页面,在这个页面中执行父窗口中的js代码
{% extends 'layout.html' %} {% block js %} <script> (function () { opener.get_data('{{ pop_post_id }}','{{ pk }}','{{ obj }}'); window.close() })() </script> {% endblock %}
父窗口中的js代码实际就是get_data()函数了
function pop(url) { window.open(url,'PopName',"width=600,height=400,top=100,left=100") } function get_data(pop_post_id,pk,obj) { var $option=$('<option>'); $option.html(obj); $option.val(pk); $option.attr('selected','selected'); $('#'+pop_post_id).append($option) }
这就是添加、修改页面的主要功能了。