Django中的分页操作、form校验工具

批量插入数据

后端:
def fenye(request):
    book_list=[]
    for i in range(100):
        book_list.append(models.Book(title='第%s本书'%i))
    models.Book.objects.bulk_create(book_list)
    book_qurery=models.Book.objects.all()
    return render(request,'fenye.html',locals())
前端:
{% for book_list in book_qurery %}
    <p>{{ book_list.title }}</p>
{% endfor %}

网页分页操作

class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=10, pager_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数

        用法:
        queryset = model.objects.all()
        page_obj = Pagination(current_page,all_count)
        page_data = queryset[page_obj.start:page_obj.end]
        获取数据用page_data而不再使用原始的queryset
        获取前端分页样式用page_obj.page_html
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page < 1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        # 添加前面的nav和ul标签
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
            else:
                temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)

使用自定义的分页,实现分页功能

后端:
from app01.utils.mypage import Pagination
def fenye1(request):
    book_list = []
    for i in range(100):
    book_list.append(models.Book(title='第%s本书' % i))
    models.Book.objects.bulk_create(book_list)
    book_query=models.Book.objects.all()
    current_page=request.GET.get('page',1)
    all_count=book_query.count()
    page_obj=Pagination(current_page=current_page,all_count=all_count)
    page_query=book_query[page_obj.start:page_obj.end]
    return render(request,'fenye1.html',locals())

前端:
{% for book_obj in  page_query %}
    <p>{{ book_obj.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}

创建多对多表关系的三种方式

1、全自动

好处在于django orm会自动帮你创建第三张关系表,但是它只会帮你创建两个表的关系字段,不会再额外添加字段,虽然方便,但是第三张表的扩展性较差,无法随意的添加额外的字段

2、纯手动

好处在于第三张表可以任意的添加额外的字段,不足之处在于orm查询的时候,很多方法都不支持,查询的时候非常麻烦

3、半自动

手动建表,但是你会告诉orm第三张表是你自己建的,orm只需要给我提供方便的查询方法,第三种虽然可以使用orm查询方法,但不支持使用:add(),set(),remove(),clear()

class Book(models.Model):
  authors=models.ManyToManyField(to='Author',through='Book_Author',through_fields('book','author'))

class Author(models.Model):
    books=models.ManyToManyField(to='Book',through='Book_Author',though_fields=('author','book'))
    
class Book_Author(models.Model):
	book=models.ForeignKey(to='Book')
    author=models.ForeignKey(to='Author')
    create_time=models.DateField(auto_now_add=True)
    
    半自动,一定要加两个额外的参数
    through='Book_Author',through_fields=('book','author')
    后面字段的顺序
    由第三张表通过哪个字段查询单表,就把哪个字段放前面

form校验组件

注册功能:

  • 用户输入的用户名中不能包含“不好”,如果包含了就提示用户,输入的内容充满消极意义
  • 用户输入的密码,不能小于三位,如果少于三位,提示用户密码太短了

校验数据通常是前后端都有校验.前端可有不校验,后端必须校验

如何取消前端帮我们做的校验 form表单中添加一个参数即可,</form action="" method="post" novalidate>

form组件能够自动帮你完成:

  • 渲染页面

  • 
    
  • 校验数据

  • 展示错误信息

        展示错误信息   用对象点errors.0
            <form action="" method="post" novalidate>
                {% for foo in form_obj %}
              <p>
                    {{ foo.label }}:{{ foo }}
                    <span style="color: red">{{ foo.errors.0 }}</span>
                </p>
                {% endfor %}
                <input type="submit">
            </form>
    

Form常用字段与插件

  • initial:初始值,input框里面的初始值 initial=‘初始值’

  • error_messages:重写错误信息 error_messages={"required":'不能为空 ',“invalid”:'格式错误'}

  • min_length/max_length: 最短长度/最大长度

  • label :显示文本内容

  • required=True 是否允许为空

  • widget=None html插件

  • help_text=' ' 初始值

  • validatores=[] 自定义验证规则

  • lacalize=False 是否支持本地化

  • disabled·=False 是否可编辑

  • label_suffix=None label内容后缀

  • strip=True 是否移除用户输入空白

  • decimal_places=None 小数位长度

  • input_formats=None 时间格式化

  • DateField(BaseTemporaField) 格式:2019-10-23

  • TimeField(BaseTemporalField) 格式:11:12

  • DateTimeField(BaseTemporalField) 格式:2015-09-11 11:12

  • regex :自定制正则表达式

  • allow_empty_file=False 是否允许空文件

  • required=True 是否必填

  • widget=None 插件,默认select插件

  • radioSelect:单radio值为字符串

  • class LoginForm(forms.Form):
        username = forms.CharField(
            min_length=8,
            label="用户名",
            initial="张三",
            error_messages={
                "required": "不能为空",
                "invalid": "格式错误",
                "min_length": "用户名最短8位"
            }
        )
        pwd = forms.CharField(min_length=6, label="密码")
        gender = forms.fields.ChoiceField(
            choices=((1, "男"), (2, "女"), (3, "保密")),
            label="性别",
            initial=3,
            widget=forms.widgets.RadioSelect()
        )
    
  • 单选Select

  • class LoginForm(forms.Form):
        ...
        hobby = forms.ChoiceField(
            choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
            label="爱好",
            initial=3,
            widget=forms.widgets.Select()
        )
    
  • 多选Select

  • class LoginForm(forms.Form):
        ...
        hobby = forms.MultipleChoiceField(
            choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
            label="爱好",
            initial=[1, 3],
            widget=forms.widgets.SelectMultiple() )
    
  • 单选checkbox

  • class LoginForm(forms.Form):
        ...
        keep = forms.ChoiceField(
            label="是否记住密码",
            initial="checked",
            widget=forms.widgets.CheckboxInput() )
    
  • 多选checkbox

  • class LoginForm(forms.Form):
        ...
        hobby = forms.MultipleChoiceField(
            choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
            label="爱好",
            initial=[1, 3],
            widget=forms.widgets.CheckboxSelectMultiple()
        )
    
    choice字段注意事项

    在使用选择标签时,需要注意choices的选项可以配置从数据库中获取,但是由于是静态字段获取的值无法实时更新,需要重写构造方法从而实现choice实时更新

    方式一:
    from django.forms import Form
    from django.forms import widgets
    from django.forms import fields
    class MyForm(Form):
        user = fields.ChoiceField(
            # choices=((1, '上海'), (2, '北京'),),
            initial=2,
            widget=widgets.Select
        )
     
        def __init__(self, *args, **kwargs):
            super(MyForm,self).__init__(*args, **kwargs)
            # self.fields['user'].choices = ((1, '上海'), (2, '北京'),)
            # 或
            self.fields['user'].choices = models.Classes.objects.all().values_list('id','caption')
    方式二:
    from django import forms
    from django.forms import fields
    from django.forms import models as form_model
    class FInfo(forms.Form):
        authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())  # 多选
        # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())  # 单选
    
    • RegexValidator验证器

    • from django.forms import Form
      from django.forms import widgets
      from django.forms import fields
      from django.core.validators import RegexValidator
       
      class MyForm(Form):
          user = fields.CharField(
              validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
      

      局部钩子

      在form类中定义clean_字段名()方法,就能够实现对特定字段进行校验

      class LoginForm(forms.Form):
          username = forms.CharField(
              min_length=8,
              label="用户名",
              initial="张三",
              error_messages={
                  "required": "不能为空",
                  "invalid": "格式错误",
                  "min_length": "用户名最短8位"
              },
              widget=forms.widgets.TextInput(attrs={"class": "form-control"})
          )
          ...
          # 定义局部钩子,用来校验username字段
          def clean_username(self):
              value = self.cleaned_data.get("username")
              if "666" in value:
                  raise ValidationError("光喊666是不行的")
              else:
                  return value
      
      全局钩子

      在form类中定义clean()方法,就能够实现对字段进行全局校验

      class LoginForm(forms.Form):
          ...
          password = forms.CharField(
              min_length=6,
              label="密码",
              widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
          )
          re_password = forms.CharField(
              min_length=6,
              label="确认密码",
              widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
          )
          ...
          # 定义全局的钩子,用来校验密码和确认密码字段是否相同
          def clean(self):
              password_value = self.cleaned_data.get('password')
              re_password_value = self.cleaned_data.get('re_password')
              if password_value == re_password_value:
                  return self.cleaned_data
              else:
                  self.add_error('re_password', '两次密码不一致')
                  raise ValidationError('两次密码不一致')
      
      如何改变input框的type属性值
      如何改变input框的type属性值
          widget= widgets.TextInput()
          widget=widgets.PasswordInput()
      如何让forms组件渲染出来的input框有form-control类属性
          widget= widgets.TextInput(attrs={'class':'form-control others'})  # 如果有多个类属性 空格隔开
          widget=widgets.PasswordInput(attrs={'class':'form-control others'})
      
原文地址:https://www.cnblogs.com/lzss/p/11772127.html