如何消除CRUD操作中与作业非亲非故的字段赋值

如何消除CRUD操作中与作业非亲非故的字段赋值

何以缓慢解决CRUD操作中与业务无关的字段赋值,crud赋值

图片 1 

提升功用一贯是个定位的话题,编制程序中有一项也是能够提到作用的,那就是注意做一件业务,让别的未有强紧凑联系的与之分开。这里分享下咱们做CRUD时相遇的普遍数据处理场景:

  • 数据库表字段整个安顿为非空,就算那几个字段在作业上是足以为空的,之所以将数据库表字段整个统一筹划为非空,这里有亮点也许有瑕玷,我们以为亮点大于弱点,所以选拔了它

     优点:

     缺点:

  •  
    系统字段的赋值,比方创制人,成立人id,创造时间,编辑人,编辑人id,编辑时间等,那么些都要求在实质上插入数据库前赋值给Model。那么些种类字段与具体的政工一般未有太大的涉嫌关系,只是起到注脚数据被如何人在怎么时间拍卖的,当那个非业务相关的代码充斥在代码中时,就显得某些多余,并且那类代码多了也会议及展览示冗余,最后带来的结果正是非关键代码比例大。

图片 2
 
上面关于暗许值与null语义难点不要求减轻,因为我们以为具备默许值带来的独到之处远大于可空字段带来的烦心,大家来看暗中同意值与系统字段一般情状下什么样管理:

  • 在操作ORM时,将模型全数可空的字段都手动赋值成默许值,int的赋值为0等。
  • 在安插数据库时,将非空字段加上私下认可值,让数据库来管理那几个未插入值的字段,假设利用mybatis的话,mapper中提到的插入操作有三个:insert,insertSelective,前边那么些insertSelective就是拍卖非空字段的,即插入的模子对于无需赋值的字段就保障null值,数据库在插入时生成的sql语句也不会含有那一个字段,那样就足以选拔上数据库的暗许值了。假诺刚好数据库的结构当初陈设时髦未布署暗中同意值,又无法改的图景就非常差了,情状回到地点手动赋值,恐怕会现出类似如下的代码:编写几个函数通过反射来深入分析每种字段,假诺为null就修改为暗许值:

 public static <T> void emptyNullValue(final T model) {
        Class<?> tClass = model.getClass();
        List<Field> fields = Arrays.asList(tClass.getDeclaredFields());
        for (Field field : fields) {
            Type t = field.getType();
            field.setAccessible(true);
            try {
                if (t == String.class && field.get(model) == null) {
                    field.set(model, "");
                } else if (t == BigDecimal.class && field.get(model) == null) {
                    field.set(model, new BigDecimal(0));
                } else if (t == Long.class && field.get(model) == null) {
                    field.set(model, new Long(0));
                } else if (t == Integer.class && field.get(model) == null) {
                    field.set(model, new Integer(0));
                } else if (t == Date.class && field.get(model) == null) {
                    field.set(model, TimeHelper.LocalDateTimeToDate(java.time.LocalDateTime.of(1990, 1, 1, 0, 0, 0, 0)));
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

  然后在代码调用insert前调用函数来消除:

ModelHelper.emptyNullValue(request);

 
如何管理系统字段呢,在创立编辑数据时,需求获得当前用户,然后依照逻辑分别更新创造人消息以及编辑人音讯,大家特地编排三个反光机制的函数来拍卖系统字段:

 
注:上面包车型地铁系统字段的辨别,是靠系统预约完毕的,例如creator约定为创作者等,可依据不一样的场馆做多少包容,假如系统规划的好,一般在四个系统下全部表的品格应该是均等的。

public static <T> void buildCreateAndModify(T model,ModifyModel modifyModel,boolean isCreate){

        Class<?> tClass = model.getClass();
        List<Field> fields = Arrays.asList(tClass.getDeclaredFields());
        for (Field field : fields) {
            Type t = field.getType();
            field.setAccessible(true);
            try {
                if(isCreate){
                    if (field.getName().equals(modifyModel.getcId())) {
                        field.set(model, modifyModel.getUserId());
                    }
                    if (field.getName().equals(modifyModel.getcName())) {
                        field.set(model, modifyModel.getUserName());
                    }
                    if (field.getName().equals(modifyModel.getcTime())) {
                        field.set(model, new Date());
                    }
                }
                if (field.getName().equals(modifyModel.getmId())) {
                    field.set(model, modifyModel.getUserId());
                }
                if (field.getName().equals(modifyModel.getmName())) {
                    field.set(model, modifyModel.getUserName());
                }
                if (field.getName().equals(modifyModel.getmTime())) {
                    field.set(model, new Date());
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

 
最后在多少管理前,依照创制或然编辑去调用函数来给系统字段赋值,那类代码都夹杂在职业代码中。

ModifyModel modifyModel = new ModifyModel();
        modifyModel.setUserId(getCurrentEmployee().getId());
        modifyModel.setUserName(getCurrentEmployee().getName());
        if (request.getId() == 0) {
            ModelHelper.buildCreateAndModify(request, modifyModel, true);
            deptService.insert(request);
        } else {
            ModelHelper.buildCreateAndModify(request, modifyModel, false);
            deptService.updateByPrimaryKey(request);
        }

图片 3

  我们得以应用参数注入来消除。参数注入的思想正是在spring
mvc接收到前台要求的参数后,进一步对接受到的参数做拍卖以达到预期的意义。大家来创制ManageModelConfigMethodArgumentResolver,它供给贯彻HandlerMethodArgumentResolver,这些接口看起来相比较轻巧,包蕴五个基本措施:

  •  决断是不是是必要注入的参数,一般通过判别参数上是不是有独特的讲解来贯彻,也得以扩展一个别样的参数剖断,可依赖具体的工作做调治,小编这里只以是不是有新鲜注释来决断是不是供给参数注入。

@Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(ManageModelConfig.class);
    }
  •  参数注入,它提供了一个扩展入口,让大家有时机对接收到的参数做越来越的管理。

@Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        Object manageModel =getRequestResponseBodyMethodProcessor().resolveArgument(parameter, mavContainer, webRequest, binderFactory);

        ServletRequest servletRequest = webRequest.getNativeRequest(ServletRequest.class);
        Employee currentUser = (Employee) servletRequest.getAttribute(DEFAULT_ATTRIBUTE_GET_USER_FROM_REQUEST);
        if (null == currentUser)
        {
            return manageModel;
        }
        ManageModelConfig parameterAnnotation = parameter.getParameterAnnotation(ManageModelConfig.class);
        ModelHelper.setDefaultAndSystemFieldsValue(manageModel, currentUser,parameterAnnotation.isSetDefaultFieldsValue());
        return manageModel;
    }

    这段函数有几处主题逻辑:

  • 赢得参数对象,因为我们管理的是ajax央浼的参数,最简易的流入方法就是收获实质上参数通过反射去管理暗许字段以及系统的值。ajax哀告与form表单post提交的数据绑定略有分化,可参照以前小说分享的列表页动态搜索的参数注入(列表页的动态条件寻找)。获取当前恳请参数对象,大家能够借助如下四个指标合作来变成:
    • RequestMappingHandlerAdapter
    • RequestResponseBodyMethodProcessor

private RequestMappingHandlerAdapter requestMappingHandlerAdapter=null;
    private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null;

    private RequestResponseBodyMethodProcessor getRequestResponseBodyMethodProcessor() {
        if(null==requestMappingHandlerAdapter)
        {
            requestMappingHandlerAdapter=new RequestMappingHandlerAdapter();
        }

        if (null==requestResponseBodyMethodProcessor) {
            List<HttpMessageConverter<?>> messageConverters = requestMappingHandlerAdapter.getMessageConverters();
            messageConverters.add(new MappingJackson2HttpMessageConverter());
            requestResponseBodyMethodProcessor = new RequestResponseBodyMethodProcessor(messageConverters);
        }
        return requestResponseBodyMethodProcessor;
    }

    通过如下代码就足以取到参数对象了,其实正是让spring
mvc重新深入分析了三回参数。

Object manageModel =getRequestResponseBodyMethodProcessor().resolveArgument(parameter, mavContainer, webRequest, binderFactory);
  • 怎么着赢安妥前用户,大家在成功登陆系统后,将近日用户的音讯存款和储蓄在request中,然后就足以在函数中获得当前用户,也能够运用其余方案,比如ThreadLocal,缓存等等。

ServletRequest servletRequest = webRequest.getNativeRequest(ServletRequest.class);
        Employee currentUser = (Employee) servletRequest.getAttribute(DEFAULT_ATTRIBUTE_GET_USER_FROM_REQUEST);
  • 调用管理函数化解私下认可字段以及系统的赋值,能够依附配置来决定是不是管理字段暗许值。

ManageModelConfig parameterAnnotation = parameter.getParameterAnnotation(ManageModelConfig.class);
        ModelHelper.setDefaultAndSystemFieldsValue(manageModel, currentUser,parameterAnnotation.isSetDefaultFieldsValue());

  最终将大家的参数注入逻辑运行起来,这里采纳在xml中配备:

 <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
        <mvc:argument-resolvers>
            <bean class="cn.wanmei.party.management.common.mvc.method.annotation.ManageModelConfigMethodArgumentResolver"/>
        </mvc:argument-resolvers>
    </mvc:annotation-driven>

图片 4
  
 再看action中的调用:只供给在参数前边扩充申明@ManageModelConfig,假若急需管理默许值,则将启用暗许值的选项设置成true就能够,上面包车型大巴贯彻部分完全看不到任何与事务非亲非故的代码。

@RequestMapping(value = "/addOrUpdateUser")
    @ResponseBody
    public Map<String, Object> addOrUpdateUser(@ManageModelConfig(isSetDefaultFieldsValue=true) EmployeeDto request) {
        Map<String, Object> ret = new HashMap<>();
        ValidateUtil.ValidateResult result= new ValidateUtil().ValidateModel(request);
        boolean isCreate=request.getId() == 0;

       try {
           if (isCreate)
           {
               employeeService.insert(request);
           }
           else
           {
               employeeService.updateByPrimaryKey(request);

           }
           ret.put("data", "ok");
       }catch (Exception e){
           ret.put("err", e.getMessage());
       }

        return ret;
    }

图片 5

经过自定义完成HandlerMethodArgumentResolver,来捕获ajax乞请的参数,利用反射机制动态的将系统字段以及须要管理暗许值的字段自动赋值,制止人工干预,起到了代码精简,逻辑干净,难点联合管理的指标。须要当心的是这么些完成都以整合当下系统规划的,比方大家感觉id字段>0就象征是立异操作,为空也许等于小于0就代表是制造,系统字段也是预定名称的等等。

 

进步作用平素是个固定的话题,编制程序中有一项也是足以提到功能的,这正是当心做一件…

图片 6 

目录:

ModelForm

请参考官方文书档案

进步功能向来是个固定的话题,编制程序中有一项也是足以提到效用的,那就是小心做一件事情,让别的未有强紧凑联系的与之分开。这里享受下大家做CRUD时遇见的周边数据管理场景:

前言(难题描叙)

一、Form组件初识

参谋博客一
武沛奇先生博客
参照博客二

Django的Form首要持有一下几大效劳:

  • 生成HTML标签
  • 申明用户数据(呈现错误新闻)
  • HTML Form提交保留上次提交数据
  • 起初化页面展现内容

models.py文件内容如下:

from django.db import models

# Create your models here.
class UserType(models.Model):
    title = models.CharField(max_length=32)

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    email = models.EmailField()
    usertype = models.ForeignKey(UserType,null=True,blank=True)
  • 数据库表字段从头至尾设计为非空,纵然那一个字段在业务上是可认为空的,之所以将数据库表字段从头至尾布置为非空,这里有长处也可以有缺点,我们感到亮点大于缺点,所以选拔了它

设计(完毕全体陈设、完毕)

1、在app下创制一个forms.py文件

# 先导入以下三个模块
from django.forms import Form
from django.forms import fields
from django.forms import widgets
from .models import *

class UserForm(Form):
    username = fields.CharField(
        required=True,       #规定该字段不能为空
        error_messages={'required':'用户名不能为空'}     # 自定义错误信息,'required'是定义该字段为空时的错误信息
    )
    password = fields.CharField(
        required=True,
        error_messages={'required':'密码不能为空'}
    )
    email = fields.EmailField(
        required=True,
        error_messages={'required':'邮箱不能为空','invalid':'邮箱格式错误'}
    )     # 'invalid'是定义该字段值无效时的错误信息
#    ip = fields.GenericIPAddressField(
#       required=True,
#      error_messages={'required':'IP不能为空','invalid':'IP格式错误'}
# )
    usertype_id = fields.ChoiceField(choices=UserType.objects.values_list('id','title'))
        # choices=列表,也就是多选的,对应html的select标签

注意:

  1. 新建的表单类必须继承Form类;
  2. 种种字段暗中同意便是required=True;
  3. 字段未有自定义error_messages的值时,暗中同意值是克罗地亚语的一无可取提醒;自定义的error_messages值中得以有四个错误消息提醒,“required”是在该字段为空是唤醒,“invalid”是在该字段有值,然则值无效时提示,如邮箱或IP地址不对;
  4. 固然想经过`UserInfo.objects.create(form.cleaned_data)`这种方式飞快插入数据到数据库,则在定义Form表单字段时务必与models中表字段一样,特别注意:由于Django会models中的models.ForeignKey()字段名活动加_id,所在Form表单字段在概念时,须要手动给字段名称增多_id**,方便插入数据,如上:usertype_id
  5. fields.GenericIPAddressField()以此字段类型是专程给IP输入用的,在那之中有一个参数protocol暗中同意等于both,表示辅助IPV4和IPV6三种协议,纵然只想让其支持IPV4商量,只需求protocol=’ipv4′即可。

     优点:

  思路

2、在views.py中导入forms.py

from app01.forms import *

def adduser(request):
    form = UserForm()    # 实例化得到一个没有值的form表单对象
    if request.method == 'POST':
        form = UserForm(data=request.POST)    
            # 将前端获取的数据传入到forms.UserForm类,实例化得到一个包含字段值的表单对象form
        if form.is_valid():  # is_valid()得到一个布尔值,判断传入的字段值是否全部有效
            print(form.cleaned_data)   #form.cleaned_data是字典类型,得到全部字段的有效值,即干净的数据
            UserInfo.objects.create(**form.cleaned_data)  # 直接插入数据库,form字段必须与models中定义的一致
            return redirect('/index/')
        else:
            print(form.errors)   # 得到错误信息,并生成html标签,哪个字段值有误,就会对应产生哪个字段的错误信息
    return render(request,'adduser.html',locals())

注意:

  1. form.is_valid()措施得到二个布尔值,推断在此在此以前端得到表单数据是成套一蹴而就;
  2. form.cleaned_data猎取全体认证有效的数码,是一个字典类型,如:{'username': 'admin', 'ip': '1.1.1.1', 'usertype_id': '3', 'email': '555@qq.com', 'password': 'admin'},字典中的key对应UserForm类中的每一种字段名;
  3. form.errors证实有误的不当提醒消息,与谬误字段一一对应,如:form.errors.user可以博得user字段的全体错误消息,是贰个列表类型,form.errors.user[0]赢得user字段的首先个错误消息。form.errors.email[0]收获email字段的第二个错误音信;
  1. 得到值时,不用决断那几个字段是或不是为null,间接可用于逻辑运算。
  2. mysql DBA推荐此方案,只怕是有利品质,这里自个儿绝不求证过。

  达成效果与利益

3、在templates模版文件中渲染

<h1 class="h1">添加用户</h1>
<form action="" method="post" novalidate>
        {% csrf_token %}
        <p>用户名:{{ form.username }} {{ form.errors.username.0 }}</p>
        <p>密码:{{ form.password }} {{ form.errors.password.0 }}</p>
        <p>邮箱:{{ form.email }} {{ form.errors.email.0 }}</p>
      <!--  <p>IP:{{ form.ip }} {{ form.errors.ip.0 }}</p>  -->
        <p>用户类型:{{ form.usertype_id }} {{ form.errors.usertype_id.0 }}</p>
        <input type="submit">
</form>

     缺点:

  设计方案

4、浏览器中显示

图片 7

GET格局呼吁页面

  1. 未曾填写数据时提交,form.errors得到的值是<ul class="errorlist"><li>email<ul class="errorlist"><li>邮箱不能为空</li></ul></li><li>pwd<ul class="errorlist"><li>密码不能为空</li></ul></li><li>ip<ul class="errorlist"><li>IP不能为空</li></ul></li><li>user<ul class="errorlist"><li>用户名不能为空</li></ul></li></ul>,页面如下图:

    图片 8

    尚未填写数据时提交

  1. 数量格式填写出错开上下班时间,form.errors获取的值是<ul class="errorlist"><li>email<ul class="errorlist"><li>邮箱格式错误</li></ul></li><li>ip<ul class="errorlist"><li>IP格式错误</li></ul></li></ul>,页面如下图:

    图片 9

    邮箱或IP格式错误时提交

  2. 数据格式填写正确时,form.cleaned_data收获的值是字典{'email': 'abc@163.com', 'pwd': 'admin', 'ip': '1.1.1.1', 'usertype_id': '1', 'user': 'admin'}

    图片 10

    数据格式全部填写正确时

  1. 事情含义未有null清楚,比方int字段暗中认可值设置成0,0就未有null语义清晰。
  2. 在动用ORM插入数据时,须要管理非空字段值为null的主题素材。

  代码结构

5. 在输入框中展现私下认可值

急需在实例化form表单对象时加入initial参数,如下:

    form = UserForm(initial={'username':obj.username,
                             'password':obj.password,
                             'email':obj.email,
                             'usertype_id':obj.usertype.id})

正如示例,在编辑用户新闻时,需求输入框中有暗中认可值:

def edituser(request,pk):
    # 参数pk是urls传入的id
    obj = UserInfo.objects.get(id=pk)
    form = UserForm(initial={'username':obj.username,
                             'password':obj.password,
                             'email':obj.email,
                             'usertype_id':obj.usertype.id})
    if request.method == 'POST':
        form = UserForm(data=request.POST)
        print(request.POST)
        if form.is_valid():
            UserInfo.objects.filter(id=pk).update(**form.cleaned_data)
            return redirect('/index/')

    return render(request, 'edituser.html', locals())

图片 11

输入框中呈现暗中认可值

  •  
    系统字段的赋值,比方创造人,创立人id,创造时间,编辑人,编辑人id,编辑时间等,那一个都亟待在事实上插入数据库前赋值给Model。那几个系统字段与实际的作业一般未有太大的关联关系,只是起到评释数据被如什么人在什么时间拍卖的,当那些非业务相关的代码充斥在代码中时,就显得有点多余,何况那类代码多了也会显得冗余,最后带来的结果就是非关键代码比例大。

EF有什么拦截器

6. 化解Form组件中下拉框数据不能够实时从数据库获取的难题

是因为类中静态字段只在代码枞上到下加载的时候实施一遍,上述的代码中的usertype_id的值是从数据库中取的,它只是在加载UserForm类时进行了一回,每回实例化并不会实施。所以,就算想让usertype_id那几个字段实时从数据库中拿走数据(或许说,想让usertype_id在历次实例化时都从数据库取三回数据),就非得将获得该字段的值写在__init__()构造方法中,如下:

from django.forms import Form
from django.forms import fields
from django.forms import widgets
from .models import *

class UserForm(Form):
    username = fields.CharField(
        required=True,
        error_messages={'required':'用户名不能为空'}
    )
    password = fields.CharField(
        required=True,
        error_messages={'required':'密码不能为空'}
    )
    email = fields.EmailField(
        required=True,
        error_messages={'required':'邮箱不能为空','invalid':'邮箱格式错误'}
    )
    # ip = fields.GenericIPAddressField(
    #     required=True,
    #     error_messages={'required':'IP不能为空','invalid':'IP格式错误'}
    # )
    usertype_id = fields.ChoiceField(choices=[])

    def __init__(self,*args,**kwargs):
        super(UserForm, self).__init__(*args,**kwargs)
        self.fields['usertype_id'].choices=UserType.objects.values_list('id','title')

注意:
self.fields富含了富有的字段,django内部会将装有字段都深拷贝一份,并装在self.fields中。所以通过self.fields['usertype_id'].choices=UserType.objects.values_list('id','title')就能够在每一遍实例化时,从数据库取二遍值赋给usertype_id

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图