第六十二天 批量操作、分页、form组件

一、批量操作

1.首先我们用以前的方法尝试创建10万条数据
from django.shortcuts import render,redirect,HttpResponse
from app01 import models
# Create your views here.
def index(request):
    for i in range(100000):
        models.Book.objects.create(name=f'第{i}条数据')
    book_query = models.Book.objects.all()
    return render(request,'book_list.html',locals())
——————————————————————————————————————
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-offset-md-2">
            {% for book_obj in book_query %}
                <p>{{ book_obj.name }}</p>
            {% endfor %}
        </div>
    </div>
</div>
</body>
———————————————————————————————————————
"""
1.首先在老师的版本中sqllite3数据库会锁住
2.我们自己现在的版本中创建10万条数据会卡好一会,但是依旧会创建成功
3.我们这里去使用一种对数据库压力小一点的办法
"""


2.bulk_create()批量创建数据
def index(request):
    book_list = []
    for i in range(100000):
        book_obj = models.Book(name=f'第{i}本书')
        book_list.append(book_obj)
    '''上述四行可以简写为一行>>>:列表生成式(列表表达式)'''
    # book_list = [models.Book(name=f'第{i}本书') for i in range(100000)]
    models.Book.objects.bulk_create(book_list)
    book_query = models.Book.objects.all()
    return render(request, 'book_list.html', locals())
————————————————————————————————————————
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-offset-md-2">
            {% for book_obj in book_query %}
                <p>{{ book_obj.name }}</p>
            {% endfor %}
        </div>
    </div>
</div>
</body>
"""
1.book_obj = models.Book(name=f'第{i}本书'),是先进行对象的创建,不同于上面的直接进行数据库的写入
2.book_list = [models.Book(name=f'第{i}本书') for i in range(100000)] 列表生成式需要进行复习
3.利用models.Book.objects.bulk_create(book_list)中的bulk_create进行批量创建
4.垃圾mac浏览器看不出区别
"""

3.总结
浏览器访问一个django路由 立刻创建10万条数据并展示到前端页面
	create()、all()
涉及到大批量数据的创建 直接使用create可能会造成数据库崩溃
	批量数据创建>>>:bulk_create() 
	批量数据修改>>>:bulk_update()

二、数据分页操作

首先思考分页的本质
1.分页本质上是仅展示部分数据,具体操作阶段在book_query = models.Book.objects.all()
2.因为是一个包含很多queryset的对象,所以可以利用[0,10]进行取数据切片
3.那么就思考将开头和结尾设置在[0,10],数据切片的起始位置和结束位置与用户选择页数,以及每页展示数据个数的关系
4.起始位置=(选择页-1)*每页数据个数      结束位置=选择页*每页数据个数
"""
补充知识:关于字典
dic = {'name':'jojo','pwd':123}
print(dic.get('pwd',1))  # 123
print(dic.get('age',1))  # 1
去取字典中的值的时候如果是原来就有的,那么输出值为原来的值,如果是原来没有的就是后面填写的1
"""
def index(request):
    current_page = request.GET.get('page', 1)
    try:
        current_page = int(current_page)
    except TypeError:
        current_page = 1
    per_page_num = 10
    start_num = (current_page - 1) * 10
    end_num = current_page * 10
    book_query = models.Book.objects.all()[start_num:end_num]
    return render(request, 'bookList.html', locals())
————————————————————————————————————————————————————————
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-offset-md-2">
            {% for book_obj in book_query %}
                <p class="text-center">{{ book_obj.name }}</p>
            {% endfor %}
            <nav aria-label="Page navigation" class="text-center">
              <ul class="pagination">
                <li>
                  <a href="#" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                  </a>
                </li>
                <li><a href="?page=1">1</a></li>
                <li><a href="?page=2">2</a></li>
                <li><a href="?page=3">3</a></li>
                <li><a href="?page=4">4</a></li>
                <li><a href="?page=5">5</a></li>
                <li>
                  <a href="#" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                  </a>
                </li>
              </ul>
            </nav>
        </div>
    </div>
</div>
</body>
"""
1.首先我们要清楚传输具体页面可以使用get可以携带部分不重要的数据的特性
http://127.0.0.1:8000/login/?aaa=123&bbb=456这就传输了两组数据
print(request.GET)  # <QueryDict: {'aaa': ['123'], 'bbb': ['456']}>
2.也可以利用上面的特性绑定按钮,前面的网址不用写全可以自动绑定<li><a href="?page=1">1</a></li>
3.下面我们就要对导航条的长度进行要求,以及导航数据的个数
"""


2.我们先把全部的页面数字在导航条上面进行展示
def index(request):
    book_data = models.Book.objects.all()
    book_count = book_data.count()
    per_page_num = 10
    page_count, more = divmod(book_count, per_page_num)
    if more:
        page_count += 1
    html_page = ''
    for i in range(1, page_count + 1):
        html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i)
    current_page = request.GET.get('page', 1)
    try:
        current_page = int(current_page)
    except TypeError:
        current_page = 1
    per_page_num = 10
    start_num = (current_page - 1) * 10
    end_num = current_page * 10
    book_query = book_data[start_num:end_num]
    return render(request, 'bookList.html', locals())
——————————————————————————————————————————————————
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-offset-md-2">
            {% for book_obj in book_query %}
                <p class="text-center">{{ book_obj.name }}</p>
            {% endfor %}
            <nav aria-label="Page navigation" class="text-center">
              <ul class="pagination">
                <li>
                  <a href="#" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                  </a>
                </li>
                    {{ html_page|safe }}
                <li>
                  <a href="#" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                  </a>
                </li>
              </ul>
            </nav>
        </div>
    </div>
</div>
</body>
"""
1.第一个难是计算总共有几页divmod(book_count, per_page_num)用以计算除出来的结果和余数
2.如果余数大于一那么计算结果加一即为页数
3.本来想要利用 for 以及 range在前端创建标签,但是前端没法使用range
4.那么可以在后端创建好文字的标签,传输到前段使用|safe进行转换就行了
5.下面我们进行思考页面标签不要全部展示
"""


3.页面标签展示限制
def index(request):
    book_data = models.Book.objects.all()
    book_count = book_data.count()
    per_page_num = 10
    page_count, more = divmod(book_count, per_page_num)
    if more:
        page_count += 1
    current_page = request.GET.get('page', 1)
    try:
        current_page = int(current_page)
    except TypeError:
        current_page = 1
    html_page = ''
    xxx = current_page
    if current_page < 6:
        xxx = 6
    for i in range(xxx - 5, xxx + 6):
        if i == current_page:
            html_page += '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i)
        else:
            html_page += '<li><a href="?page=%s">%s</a></li>' % (i, i)
    start_num = (current_page - 1) * 10
    end_num = current_page * 10
    book_query = book_data[start_num:end_num]
    return render(request, 'bookList.html', locals())
——————————————————————————————————————————————————
html页面同上
"""
1.首先我们本质上只要展示前五页和后五页的标签就行了
2.for i in range(xxx - 5, xxx + 6):其中展示多少自己去设定
3.'<li class="active"><a href="?page=%s">%s</a></li>'中的<li class="active">就是选中给的页面
"""

自定义分页器代码

"""
自定义的模组的代码一般放在app目录下的plugins文件夹下面,文件夹需要自己创建
"""
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:  最多显示的页码个数
        """
        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)

三、自定义分页器

def index(request):
    from app01.plugins import mypage
    book_query = models.Book.objects.all()
    page_obj = mypage.Pagination(current_page=request.GET.get('page'),
                                 all_count=book_query.count()
                                 )
    page_query = book_query[page_obj.start:page_obj.end]
    return render(request, 'bookList.html', locals())
——————————————————————————————————————————
<div class="container">
    <div class="row">
        <div class="col-md-8 col-offset-md-2">
            {% for book_obj in page_query %}
                <p class="text-center">{{ book_obj.name}}</p>
            {% endfor %}
            {{ page_obj.page_html|safe }}
        </div>
    </div>
</div>
"""
1.主要注意需要传入自定义汉函数的参数
2.page_query = book_query[page_obj.start:page_obj.end]找到了数据的起始与结束位置
3.page_obj.page_html直接数据导航条标签
"""

四、form组件

前戏:编写用户登录功能并且校验数据返回提示信息(form表单)
# 在这里我们不去使用ajax的异步回调
def ab_form(request):
    data_dic = {'username': '', 'password': ''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'jojo':
            data_dic['username'] = '别和我取一样的名字'
        if password == '123':
            data_dic['password'] = '密码太简单了'
    return render(request, 'ab_form.html', locals())
—————————————————————————————————————————————————
<form action="" method="post">
    <p>username:
        <input type="text" name="username">
        <span style="color: brown">{{ data_dic.username }}</span>
    </p>
    <p>password:
        <input type="text" name="password">
        <span style="color: brown">{{ data_dic.password }}</span>
    </p>
    <input type="submit" value="提交">
</form>
"""
这里创建了一个字典去存储数据并且传输到前端span标签没有值是无法显示的
"""
form组件
1.数据校验
	支持提前设置各种校验规则 之后自动校验
2.渲染页面
	支持直接渲染获取用户数据的各种标签
3.展示信息
	支持针对不同的校验失败展示不同的提示

先创建一个对应的表格
class User(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    email = models.EmailField()

之后再在views文件夹中创建一个类,用来限制
class MyForm(forms.Form):
    name = forms.CharField(max_length=8, min_length=2)
    age = forms.IntegerField(max_value=100, min_value=1)
    email = forms.EmailField()

之后再左下角的Python Console进行试运行
1.数据校验功能
1.1.传递待校验的数据
	form_obj = views.MyForm({'name':'jason','age':18,'email':123})
1.2.判断所有的数据是否符合校验( is_valid() )  # 是否合适valid
	form_obj.is_valid()
1.3.获取符合校验规则的数据( cleaned_data )  # cleaned_data数据清洗
	form_obj.cleaned_data
	{'name': 'jason', 'age': 18}
1.4.查阅不符合校验规则的数据及错误原因( errors )   # 查找错误
	form_obj.errors
	{'email': ['Enter a valid email address.']}

"""
1.form类中编写的字段默认都是必填的 少传则肯定通不过校验 is_valid  !!!!!!!!!!!!!!!!
2.校验如果多传了一些字段 则不参与校验 全程忽略  !!!!!!!!!!!!!!!!!!!
"""

六、form组件的渲染功能

class MyForm(forms.Form):
    name = forms.CharField(max_length=8, min_length=2)
    age = forms.IntegerField(max_value=100, min_value=1)
    email = forms.EmailField()
# 首先利用上面写的类创建一个对象,并且传输到前端页面
def func(request):
    form_obj = MyForm()
    return render(request, 'func.html', locals())
—————————————————————————————————————————————————
渲染标签功能
方式1(封装程度高 扩展性差)
<form action="", method="post">
	{{ form_obj.as_p }}
	{{ form_obj.as_table }}
	{{ form_obj.as_ul }}
	<input type="submit" value="提交">
</form>
"""
这种方式会直接将对象中的字段自动生成一整个页面,但是因为是一整个,所以无法进行自定义样式的修改
"""

方式2(封装程度低 扩展性好 编写困难)
<form action="", method="post">
	{{ form_obj.name.lable }}
	{{ form_obj.name }}
	<input type="submit" value="提交">
</form>
"""
form_obj.name.lable可以理解为生成name的一个lable,这里也就是生成一个name
form_obj.name可以理解为生成一个需要填入的input框
lable的名字可以自己去进行修改
class MyForm(forms.Form):
    name = forms.CharField(max_length=8, min_length=2, lable='用户名')
"""

方式3(推荐使用)
<form action="", method="post">
	{% for form in form_obj %}
	<p>{{ form.label }}{{ form }}</p>
	{% endfor %}
	<input type="submit" value="提交">
</form>
"""
这里的意思其实和上面的一样循环中的form约等于form_obj.name
这个因为for循环所以不用再自己一个个的去写了
"""

六、form的数据校验以及展示功能

from django import forms
class MyForm(forms.Form):
    name = forms.CharField(max_length=8, min_length=2)
    age = forms.IntegerField(max_value=100, min_value=1)
    email = forms.EmailField()
def func(request):
    form_obj = MyForm()
    if request.method == 'POST':
        form_obj = MyForm(request.POST)
        if form_obj.is_valid():
            print(form_obj.cleaned_data)
    return render(request, 'func.html', locals())
————————————————————————————————————————————————
<form action="" method="post" novalidate>
    {% for form in form_obj %}
        <p>{{ form.label }}{{ form }}
        <span style="color: red">{{ form.errors.0 }}</span>
        </p>
    {% endfor %}
    <input type="submit" value="提交">
</form>
"""
1.首先要注意前端的校验很不靠谱所以在form上面用novalidate关闭校验
2.form_obj = MyForm(request.POST)因为POST获取到的本来就是字典直接传入就行了没必要一个个request.POST.get   !!!!!!!!!!!!!!!!!!!!!!!!
3.检验成功就可以传入数据库了可以用 create(**form_obj.cleaned_data),因为后面出来的是清洗好的字典,拿来用就行了 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4.这里最传神的一点就是,因为post以后form_obj从原来的没有值,被赋值了,就可以传输到前端去调用errors等方法,form.errors.0就可以显示出来值了
"""


# 可以自己指定错误的报错信息
class MyForm(forms.Form):
    name = forms.CharField(max_length=8, min_length=2, label='用户名',
                           error_messages={
                               'max_length': '用户名最多8个字符',
                               'min_length': '用户名最少2个字符',
                               'required': '用户名不为空'
                           }
                           )
    age = forms.IntegerField(max_value=100, min_value=1)
    email = forms.EmailField(error_messages={'invalid':'格式错误'})


# 或者可以在settings文件中将Django中的报错指定为中文!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
from django.conf import global_settings  # 在其中可以找到该设置什么方法
LANGUAGE_CODE = 'zh-hans'

七、form的数据校验以及展示功能(注意前面import的东西)

from django import forms
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator

重要的字段参数
max_length、min_length
max_value、min_value
label				字段注释
error_messages		  错误提示  # 前文有提到,类似于超出范围的时候的报错
required			  是否为空  # required=True就是不能为空的意思
widget				 标签类型、标签属性
initial				  默认值  # 标签的默认值比如说摸认选择为男性
validators			  正则校验

widget详解
class MyForm(forms.Form):
    name = forms.CharField(max_length=8, min_length=2,widget=forms.widgets.TextInput(attrs={'class': 'form-control'}))  # class和id都可以去控制

validators详解
phone = forms.CharField(validators=[
                                    RegexValidator(r'^[0-9]+$','请输入数字'),
                                    RegexValidator(r'^159[0-9]+$', '请输入以159为开头数字')
    ])

以后要用到widget可以查询下面的方法

参考https://blog.csdn.net/ak739105231/article/details/103852585

TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget

单radio,值为字符串
user = fields.CharField(
    initial=2,
    widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
)

单radio,值为字符串
user = fields.ChoiceField(
    choices=((1, '上海'), (2, '北京'),),
    initial=2,
    widget=widgets.RadioSelect
)

单select,值为字符串
user = fields.CharField(
    initial=2,
    widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
)

单select,值为字符串
user = fields.ChoiceField(
    choices=((1, '上海'), (2, '北京'),),
    initial=2,
    widget=widgets.Select
)

多选select,值为列表
user = fields.MultipleChoiceField(
    choices=((1,'上海'),(2,'北京'),),
    initial=[1,],
    widget=widgets.SelectMultiple
)


单checkbox
user = fields.CharField(
    widget=widgets.CheckboxInput()
)


多选checkbox,值为列表
user = fields.MultipleChoiceField(
    initial=[2, ],
    choices=((1, '上海'), (2, '北京'),),
    widget=widgets.CheckboxSelectMultiple
)

八、form组件之对钩函数

class MyForm(forms.Form):
    '''第一层校验'''
    name = forms.CharField(max_length=8, min_length=3, label='用户名')
    pwd = forms.IntegerField(label='密码')
    confirm_pwd = forms.IntegerField(label='确认密码')
    age = forms.IntegerField()
    email = forms.EmailField()
    '''第二层校验'''
    # 1.校验用户名是否已存在
    def clean_name(self):
        name = self.cleaned_data.get('name')
        res = models.User.objects.filter(name=name)
        if res:
            return self.add_error('name', '用户名已经存在')
        return name
    # 2.校验两次密码是否一致
    def clean(self):
        pwd = self.cleaned_data.get('pwd')
        confirm_pwd = self.cleaned_data.get('confirm_pwd')
        if confirm_pwd != pwd:
            return self.add_error('confirm_pwd', '两次密码不一致')
        return self.cleaned_data


def func(request):
    form_obj = MyForm()
    if request.method == 'POST':
        form_obj = MyForm(request.POST)
        if form_obj.is_valid():
            res = form_obj.cleaned_data
            res.pop('confirm_pwd')
            print(res)
            models.User.objects.create(**res)
    return render(request, 'func.html', locals())
—————————————————————————————————————————————————
<body>
<form action="" method="post" novalidate>
    {% for form in form_obj %}
        <p>{{ form.label }}{{ form }}
        <span style="color: red">{{ form.errors.0 }}</span>
        </p>
    {% endfor %}
    <input type="submit" value="提交">
</form>
</body>

"""
1.钩子函数clean只有在form上面的检验完成之后才运行
2.clean_name是针对name字段进行校验,clean可以对所有字段进行校验
3.return self.add_error('name', '用户名已经存在')是专门针对name进行报错的方法!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4.钩子函数必须要把勾的东西还回去,clean_name就return name,clean就把所有东西还回去return self.cleaned_data!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
"""

九、modelform组件(modelform是form的优化版本 使用更简单 功能更强大)

"""modelform最厉害的地方在于不用自己一个个的去创建字段了,它可以自动给你批量生成"""
class MyModelForm(forms.ModelForm):
    class Meta:
        model = models.User
        fields = '__all__'

    def clean_name(self):
        name = self.cleaned_data.get('name')
        res = models.User.objects.filter(name=name)
        if res:
            self.add_error('name', '用户名已存在')
        return name

def md(request):
    modelform_obj = MyModelForm()
    if request.method == 'POST':
        modelform_obj = MyModelForm(request.POST)
        # edit_obj = models.User.objects.filter(name='jason').first()
        # modelform_obj = MyModelForm(request.POST,instance=edit_obj)
        if modelform_obj.is_valid():
            modelform_obj.save()  # 保存数据
    return render(request,'md.html',locals())
—————————————————————————————————————————————————
<body>
<form action="" method="post" novalidate>
    {% for modelform in modelform_obj %}
        <p>
        {{ modelform.label }}{{ modelform }}
        <span>{{ modelform.errors.0 }}</span>
        </p>
    {% endfor %}
    <input type="submit" value="提交">
</form>
</body>

"""
1.首先创建一个MyModelForm继承了我们要用的forms.modelform
2.在里面说明要对应的表以及其中的字段class Meta:   model = models.User  fields = '__all__'
3.接下来的第二步的数据校验就和form的一样了,clean的功能使用方法是一样的
4.modelform_obj.save()  是保存数据的方法!!!!!!!!!!!!!!!!
5.如果想修改数据就得使用
# edit_obj = models.User.objects.filter(name='jason').first()
# modelform_obj = MyModelForm(request.POST,instance=edit_obj)
"""


# 自己试试增删改查的方法
posted @   暧昧的花蝴蝶  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示