23.Django(form组件 自定制校验规则、钩子、更改图书管理系统)

自定制校验规则

之前form表单给我们提供了一些校验功能:

这些基本你的校验功能,不足以满足我们日常的需要,用户名不允许出现敏感字符,电话号码的验证,等等。

  • 正则校验器(validators)

    name = form.CharField(
    label='用户名:',
       required=True, #必须填入内容的设置,默认为True
       initial='dong', #设置初始值
       min_length=6,
       max_length=12,
       help_text='用户名不能有特殊字符',
       # 一个参数正则的匹配规则,第二参数不满足这个匹配规则报的错误信息
       validators=[RegexValidator(r'^金ping梅', '必须以金ping梅开头'),
                  RegexValidator(r'xx$', '必须以xx结尾'),],
       errpr_messages={'min_length': '太短了', 'max_length': '太长了',
                      'required': '必须填写内容'},
       widget=forms.widgets.TextInput,
    )

    这种正则校验器比较局限,用起来相对麻烦。

  • 自定制函数校验

    (注意:模块的引入要向下面的规则写)

    import re

    from django.shortcuts import render, HttpResponse
    from django import forms
    from django.core.validators import ValidationError


    def moblie_validate(value):
       # 我们这里就给name字段设置这个函数的的匹配规则
       # 那么这个value就是name字段实际得到的数据
       mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
       if not mobile_re.match(value):
           raise ValidationError('手机号格式错误')  # 自定义验证规则的时候,如果不符合你的规则,需要自己发起错误
           
     

    class RegisterForm(forms.Form):
       """
      form组件生成前端的标签写法,与models构建的表结构非常相似
      """
       name = forms.CharField(
           label='用户名:',
           required=True,  # 必须填入内容的设置,默认为True
           initial='dong',  # 设置初始值
           min_length=8,
           max_length=14,
           help_text='用户名不能有特殊字符',
           # validators=[RegexValidator(r'^金ping梅', '必须以金ping梅开头'), RegexValidator(r'xxx$', '必须以xxx结尾')],
           error_messages={'min_length': '太短了', 'max_length': '太长了', 'required': '必须填写内容'},
           validators=[moblie_validate],
           widget=forms.widgets.TextInput(),
      )

       password = forms.CharField(
           label='密码:',
           min_length=6,
           help_text='密码不得少于8位',
           # widget这个功能可以当成一个工具,这个工具就是确认标签类型以及i增加标签属性的
           error_messages={'min_length': '太短了'},
           widget=forms.widgets.PasswordInput,
      )

 

 

钩子

form组件给你提供了另一个自定制校验规则的接口:局部钩子,全局钩子

  • 局部钩子

    比如,你的用户名不能出现一些敏感词汇,敏感词汇有:

    东京热,苍⽼师,⾦ping梅,..... 这些⽤正则匹配有点⼉⼒不从⼼了。所以我们就得⽤钩 ⼦去解决。

    局部钩子是针对某个字段校验的。

    import re

    from django.shortcuts import render, HttpResponse
    from django import forms
    from django.core.validators import ValidationError


    class RegisterForm(forms.Form):
      """
      form组件生成前端的标签写法,与models构建的表结构非常相似
      """

      name = forms.CharField(
          label='用户名:',
          required=True, # 必须填入内容的设置,默认为True
          initial='dong', # 设置初始值
          min_length=8,
          max_length=14,
          help_text='用户名不能有特殊字符',
          # validators=[RegexValidator(r'^金ping梅', '必须以金ping梅开头'), RegexValidator(r'xxx$', '必须以xxx结尾')],
          error_messages={'min_length': '太短了', 'max_length': '太长了', 'required': '必须填写内容'},
          # validators=[moblie_validate],
          widget=forms.widgets.TextInput(),
      )

      password = forms.CharField(
          label='密码:',
          min_length=6,
          help_text='密码不得少于8位',
          # widget这个功能可以当成一个工具,这个工具就是确认标签类型以及i增加标签属性的
          error_messages={'min_length': '太短了'},
          widget=forms.widgets.PasswordInput,
      )

      def clean_name(self): # 针对于name字段做一个用于校验的局部钩子
          print('进入clean_name局部钩子')
          sensitive_vocabulary = ['京东热', '苍老师', '金ping梅']
          name_value = self.cleaned_data['name']
          for i in sensitive_vocabulary:
              if i in name_value:
                  raise ValidationError(f'含有敏感词汇{i}')
          else:
              return self.cleaned_data['name'] # 必须将所有的验证过后的数据返回

    注意:
        局部钩⼦命名:clean_字段名
        如果有错误:主动抛出异常 ValidationError
        校验成功之后,必须返回self.cleaned_data['字段名']
  • 全局钩子

    全局钩子就是对form类中的所有的字段做一个对比验证, 或者横向验证。我们一般注册时,都需要输入用户名、密码。再次输入密码。这样的需求在全局钩子中做一些验证比较合适。

    import re

    from django.shortcuts import render, HttpResponse
    from django import forms
    from django.core.validators import ValidationError



    class RegisterForm(forms.Form):
      """
      form组件生成前端的标签写法,与models构建的表结构非常相似
      """

      name = forms.CharField(
          label='用户名:',
          required=True, # 必须填入内容的设置,默认为True
          initial='dong', # 设置初始值
          min_length=8,
          max_length=14,
          help_text='用户名不能有特殊字符',
          # validators=[RegexValidator(r'^金ping梅', '必须以金ping梅开头'), RegexValidator(r'xxx$', '必须以xxx结尾')],
          error_messages={'min_length': '太短了', 'max_length': '太长了', 'required': '必须填写内容'},
          # validators=[moblie_validate],
          widget=forms.widgets.TextInput(),
      )

      password = forms.CharField(
          label='密码:',
          min_length=6,
          help_text='密码不得少于8位',
          # widget这个功能可以当成一个工具,这个工具就是确认标签类型以及i增加标签属性的
          error_messages={'min_length': '太短了'},
          widget=forms.widgets.PasswordInput,
      )
      confirm_password = forms.CharField(
          label='再次密码:',
          min_length=6,
          help_text='密码不得少于8位',
          # widget这个功能可以当成一个工具,这个工具就是确认标签类型以及i增加标签属性的
          error_messages={'min_length': '太短了'},
          widget=forms.widgets.PasswordInput,
      )

      def clean_name(self): # 针对于name字段做一个用于校验的局部钩子
          print('进入clean_name局部钩子')
          sensitive_vocabulary = ['京东热', '苍老师', '金ping梅']
          name_value = self.cleaned_data['name']
          for i in sensitive_vocabulary:
              if i in name_value:
                  raise ValidationError(f'含有敏感词汇{i}')
          else:
              return self.cleaned_data # 必须将所有的验证过后的数据返回

      def clean(self): # 全局钩子
          v1 = self.cleaned_data['password'],
          v2 = self.cleaned_data['confirm_password'],
          if v1 == v2:
              return self.cleaned_data # 验证成功必须返回
          else:
              # confirm_password添加错误信息
              self.add_error('confirm_password', '两次密码不一样')
              # 主动抛出一个异常
              raise ValidationError('两次密码不一样')

 

 

图书管理系统:

将增加的那个⻚⾯,的前端的标签,利⽤form组件⾃动⽣成,并且可以进⾏简单的验证。

1、在views创建form组件

class BookFrom(forms.Form):
  """
  form组件生成前端的标签
  """
  book_name = forms.CharField(
      label='书籍名称:',
      required=True,
      initial='书名',
      min_length=1,
      max_length=20,
      help_text='书名不能有特殊字符',
      error_messages={'min_length': '太短了', 'max_length': '太长了', 'required': '必须填写内容'},
      widget=forms.widgets.TextInput(),
  )
  book_date = forms.CharField(
      label='出版日期:',
      widget=forms.widgets.TextInput(attrs={'type': 'date'}), # 标签类型
  )
  book_price = forms.DecimalField(
      label='书籍价格:',
      max_digits=5,
      decimal_places=2,
      help_text='人民币(¥)',
      widget=forms.widgets.TextInput(attrs={'type': 'number'})
  )
  book_publish = forms.ChoiceField(
      label='出版社:',
      initial=1,
      choices=((1, 'xx出版社'), (2, 'qq出版社'), (3, 'aa出版社')),
      widget=forms.widgets.Select(), # 标签类型
  )
  book_author = forms.MultipleChoiceField(
      label='作者:',
      initial=(1, 2), # 设置初始值
      choices=((1, '张三'), (2, '李四'), (3, '王五')),
      widget=forms.widgets.SelectMultiple(), # 标签类型
  )

2、更改前端页面

{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link href="{% static 'bootstrap.min.css' %}" rel="stylesheet">
  <link href="{% static 'dashboard.css' %}" rel="stylesheet">
  <style>
      #frame {
          width: 350px;
          height: 400px;
          border: 1px solid black;
          margin: 180px auto;
      }

      div {
          margin-top: 20px;
          margin-left: 20px;
      }

      .pull {
          margin-top: 50px;
          margin-left: 150px;
      }

      select {
          margin-left: 17px;
      }
  </style>
</head>
<body>
<h1>添加书籍页面</h1>
<div class="row">
  <div class="col-md-5 col-md-offset-3">
      <form action="" method="post" class="form-horizontal">
          {% csrf_token %}
          <div>
              <label for="">{{ form_obj.book_name.label }}</label>
              {{ form_obj.book_name }}
              {{ form_obj.book_name.help_text }}
          </div>
          <div>
              <label for="">{{ form_obj.book_date.label }}</label>
              {{ form_obj.book_date }}
          </div>
          <div>
              <label for="">{{ form_obj.book_price.label }}</label>
              {{ form_obj.book_price }}
              {{ form_obj.book_price.help_text }}
          </div>
          <div>
              <label for="">{{ form_obj.book_publish.label }}</label>
              {{ form_obj.book_publish }}
          </div>
          <div>
              <label for="">{{ form_obj.book_author.label }}</label>
              {{ form_obj.book_author }}
          </div>
      </form>
  </div>
</div>


</body>
</html>

增加bootstrap样式:

在BookFrom类里面写上这个函数:

    def __init__(self, *args, **kwargs):
      super().__init__(*args, **kwargs)
      print(self.fields)
      for obj in self.fields.values():
          obj.widget.attrs.update({'class': 'form-control'})

点击标签名称:inptu自动获取光标

让label的for = {{ form_obj.字段名.id_for_label }}

{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <link href="{% static 'bootstrap.min.css' %}" rel="stylesheet">
  <link href="{% static 'dashboard.css' %}" rel="stylesheet">
  <style>
      #frame {
          width: 350px;
          height: 400px;
          border: 1px solid black;
          margin: 180px auto;
      }

      div {
          margin-top: 20px;
          margin-left: 20px;
      }

      .pull {
          margin-top: 50px;
          margin-left: 150px;
      }

      select {
          margin-left: 17px;
      }
  </style>
</head>
<body>
<h1>添加书籍页面</h1>
<div class="row">
  <div class="col-md-5 col-md-offset-3">
      <form action="" method="post" class="form-horizontal">
          {% csrf_token %}
          <div>
              <label for="{{ form_obj.book_name.id_for_label }}">{{ form_obj.book_name.label }}</label>
              {{ form_obj.book_name }}
              {{ form_obj.book_name.help_text }}
          </div>
          <div>
              <label for="{{ form_obj.book_date.id_for_label }}">{{ form_obj.book_date.label }}</label>
              {{ form_obj.book_date }}
          </div>
          <div>
              <label for="{{ form_obj.book_price.id_for_label }}">{{ form_obj.book_price.label }}</label>
              {{ form_obj.book_price }}
              {{ form_obj.book_price.help_text }}
          </div>
          <div>
              <label for="{{ form_obj.book_publish.id_for_label }}">{{ form_obj.book_publish.label }}</label>
              {{ form_obj.book_publish }}
          </div>
          <div>
              <label for="{{ form_obj.book_author.id_for_label }}">{{ form_obj.book_author.label }}</label>
              {{ form_obj.book_author }}
          </div>
      </form>
  </div>
</div>


</body>
</html>

 

3、获取数据的真是出版社以及作者数据

 

class BookFrom(forms.Form):
    """
    form组件生成前端的标签
    """
    book_name = forms.CharField(
        label='书籍名称:',
        required=True,
        initial='书名',
        min_length=1,
        max_length=20,
        help_text='书名不能有特殊字符',
        error_messages={'min_length': '太短了', 'max_length': '太长了', 'required': '必须填写内容'},
        widget=forms.widgets.TextInput(),
    )
    book_date = forms.CharField(
        label='出版日期:',
        widget=forms.widgets.TextInput(attrs={'type': 'date'}),  # 标签类型
    )
    book_price = forms.DecimalField(
        label='书籍价格:',
        max_digits=5,
        decimal_places=2,
        help_text='人民币(¥)',
        widget=forms.widgets.TextInput(attrs={'type': 'number'})
    )
    book_publish = forms.ModelChoiceField(
        label='出版社:',
        initial=1,
        # choices=((1, 'xx出版社'), (2, 'qq出版社'), (3, 'aa出版社')),
        queryset=models.Publish.objects.all(),
        widget=forms.widgets.Select(),  # 标签类型
    )
    book_author = forms.ModelMultipleChoiceField(
        label='作者:',
        initial=(1, 2),  # 设置初始值
        # choices=((1, '张三'), (2, '李四'), (3, '王五')),
        queryset=models.Author.objects.all(),
        widget=forms.widgets.SelectMultiple(),  # 标签类型
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print(self.fields)
        for obj in self.fields.values():
            obj.widget.attrs.update({'class': 'form-control'})

 

4、将post提交的数据写入数据库

def add(request):
    form_obj = BookFrom()
    if request.method == 'GET':
        return render(request, 'add.html', {'form_obj': form_obj})
    else:
        form_obj = BookFrom(request.POST)
        if form_obj.is_valid():
            authors = form_obj.cleaned_data.pop('author')
            book_obj = models.Book.objects.create(**form_obj.cleaned_data)
            book_obj.author.add(*authors)
            return redirect('home')
        else:
            return render(request, 'add.html', {'form_obj': form_obj})

猜你喜欢

转载自www.cnblogs.com/muyangxiaodong/p/13209985.html