基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)

项目实战

    • 靓号管理
      • number_list
      • number_add
      • number_edit
      • number_delete
      • number_list(新增搜索)
      • number_list(分页管理)
      • ModelForm和BootStrap
      • 优化项目目录
    • 管理员管理
      • admin_list
      • 分页组件使用说明
      • admin_add
      • admin_edit
      • admin_delete
      • 重置密码
    • Cookie和Session
      • 登录认证
    • 中间件
      • 注销
      • 图片验证码
    • 任务管理
      • task_list
      • task_add

号外号外,基于Pycharm的Django学习,项目实战后续来啦!

前面我们已经学习了最基础的增删改查,现在主要是对前面的补充,以及尝试一些新花样。

靓号管理

首先数据库表结构准备起来!

class PrettyNum(models.Model):
    """靓号管理"""
    mobile = models.CharField(verbose_name="手机号", max_length=11)
    price = models.IntegerField(verbose_name="价格", default=0)
    level_choices = (
        (1, "1级"),
        (2, "2级"),
        (3, "3级"),
        (4, "4级"),
        (5, "5级")
    )
    level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1)
    status_choices = (
        (1, "已占用"),
        (2, "未使用")
    )
    status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第1张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第2张图片
然后自己准备点数据进行测试(按理来说,后续应该都是自己爬取或者使用数据集):

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第3张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第4张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第5张图片
在这里插入图片描述

number_list

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第6张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第7张图片

{% extends 'layout.html' %}

{% block content %}



靓号列表
{% for data in data_list %} {% endfor %}
ID 号码 价格 级别 状态 操作
{{data.id}} {{data.mobile}} {{data.price}} {{data.get_level_display}} {{data.get_status_display}} 编辑 删除
{% endblock %}

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第8张图片

number_add

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第9张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第10张图片

class NumberModelForm(forms.ModelForm):
    # 验证一
    # mobile = forms.CharField(
    #     label="手机号",
    #     # validators必须是数组!
    #     validators=[RegexValidator(r'^\d{11}$', '手机号必须11位')]
    # )

    class Meta:
        model = models.PrettyNum
        fields = "__all__"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}

    # 验证二 (可以使用除了正则以外的验证方法)
    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]
        if len(txt_mobile) != 11:
            # 验证不通过
            raise ValidationError("格式错误")
        # 验证通过 将用户通过的值返回 数据库就保存的是这个数据
        return txt_mobile


def number_add(request):
    if request.method == "GET":
        form = NumberModelForm()
        return render(request, "number_add.html", {"form": form})
    form = NumberModelForm(data=request.POST)
    if form.is_valid():
        # 如果数据合法  保存到数据库
        print(form.cleaned_data)
        form.save()
        return redirect("/number/list/")
    else:
        print(form.errors)
        return render(request, "number_add.html", {"form": form})

其中对字段的有效值验证有两种方法:

# 验证一
    mobile = forms.CharField(
        label="手机号",
        # validators必须是数组!
        validators=[RegexValidator(r'^\d{11}$', '手机号必须11位')]
    )
# 验证二 (可以使用除了正则以外的验证方法)
    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]
        if len(txt_mobile) != 11:
            # 验证不通过
            raise ValidationError("格式错误")
        # 验证通过 将用户通过的值返回 数据库就保存的是这个数据
        return txt_mobile

对于添加页面,和用户添加差不多,前期主要是功能的实现,等到后期就是进行代码优化等了。

{% extends 'layout.html' %}

{% block content %}

新建靓号

{% csrf_token %} {% for field in form %}
{{field}} {{field.errors.0}}
{% endfor %}
{% endblock %}

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第11张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第12张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第13张图片

number_edit

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第14张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第15张图片
编辑可以和新建所使用的ModelForm一样,也可以重新写,比如在编辑的时候,不允许所有字段都编辑。

class NumberEditModelForm(forms.ModelForm):

    class Meta:
        model = models.PrettyNum
        # 不允许编辑手机号
        exclude = ["mobile"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}


def number_edit(request, nid):
    # 根据id去数据库获取编辑的那一行
    row_object = models.PrettyNum.objects.filter(id=nid).first()
    if request.method == "GET":
        form = NumberEditModelForm(instance=row_object)
        return render(request, "number_edit.html", {"form": form})
    # 用Post提交的数据 进行数据校验
    form = NumberEditModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        # 如果数据合法  保存到数据库
        print(form.cleaned_data)
        # 默认保存的是用户输入的所有数据 如果想要再保存用户输入的额外的值
        # form.instance.字段名=值
        form.save()
        return redirect("/number/list/")
    else:
        print(form.errors)
        return render(request, "number_edit.html", {"form": form})

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第16张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第17张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第18张图片
现在假设我们添加或者编辑手机号,并且不允许手机号重复,那怎么办呢?

我们可以去数据库搜是否有和其是一样的,但是有时候编辑不编辑手机号,所以搜的时候还要排除自己啊。

这种验证只能在钩子函数中操作,因为其涉及数据库。

class NumberModelForm(forms.ModelForm):
    # 验证一
    mobile = forms.CharField(
        label="手机号",
        # validators必须是数组!
        validators=[RegexValidator(r'^\d{11}$', '手机号必须11位')]
    )

    class Meta:
        model = models.PrettyNum
        fields = "__all__"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}

    # 验证二 (可以使用除了正则以外的验证方法)
    # def clean_mobile(self):
    #     txt_mobile = self.cleaned_data["mobile"]
    #     if len(txt_mobile) != 11:
    #         # 验证不通过
    #         raise ValidationError("格式错误")
    #     # 验证通过 将用户通过的值返回 数据库就保存的是这个数据
    #     return txt_mobile

    # 验证二 (主要用于设计数据库操作的验证)
    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]
        # 查看数据库中该手机号是否存在
        exists = models.PrettyNum.objects.filter(mobile=txt_mobile).exists()
        if exists:
            # 验证不通过
            raise ValidationError("手机号已存在")
        # 验证通过 将用户通过的值返回 数据库就保存的是这个数据
        return txt_mobile


def number_add(request):
    if request.method == "GET":
        form = NumberModelForm()
        return render(request, "number_add.html", {"form": form})
    form = NumberModelForm(data=request.POST)
    if form.is_valid():
        # 如果数据合法  保存到数据库
        print(form.cleaned_data)
        form.save()
        return redirect("/number/list/")
    else:
        print(form.errors)
        return render(request, "number_add.html", {"form": form})


class NumberEditModelForm(forms.ModelForm):
    mobile = forms.CharField(
        label="手机号",
        # validators必须是数组!
        validators=[RegexValidator(r'^\d{11}$', '手机号必须11位')]
    )

    class Meta:
        model = models.PrettyNum
        fields = "__all__"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        for name, field in self.fields.items():
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}

    # 验证二 (主要用于设计数据库操作的验证)
    def clean_mobile(self):
        # 获取当前编辑的对象的id
        # self.instance.pk
        txt_mobile = self.cleaned_data["mobile"]
        # 查看数据库中除编辑的该手机号之外是否存在
        exists = models.PrettyNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists()
        if exists:
            # 验证不通过
            raise ValidationError("手机号已存在")
        # 验证通过 将用户通过的值返回 数据库就保存的是这个数据
        return txt_mobile


def number_edit(request, nid):
    # 根据id去数据库获取编辑的那一行
    row_object = models.PrettyNum.objects.filter(id=nid).first()
    if request.method == "GET":
        form = NumberEditModelForm(instance=row_object)
        return render(request, "number_edit.html", {"form": form})
    # 用Post提交的数据 进行数据校验
    form = NumberEditModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        # 如果数据合法  保存到数据库
        print(form.cleaned_data)
        # 默认保存的是用户输入的所有数据 如果想要再保存用户输入的额外的值
        # form.instance.字段名=值
        form.save()
        return redirect("/number/list/")
    else:
        print(form.errors)
        return render(request, "number_edit.html", {"form": form})

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第19张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第20张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第21张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第22张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第23张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第24张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第25张图片

number_delete

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第26张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第27张图片

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第28张图片
点击删除就可以了!

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第29张图片

number_list(新增搜索)

现在想实现在页面中添加搜索功能!

常见语法如下:

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第30张图片

def number_list(request):
    # 拿到搜索的值 如果没有设置为空
    data_dict = {}
    search_data = request.GET.get("q", "")
    if search_data:
        data_dict["mobile__contains"] = search_data
    # 按照level降序排列
    data_list = models.PrettyNum.objects.filter(**data_dict).order_by("-level")
    return render(request, "number_list.html", {"data_list": data_list, "search_data": search_data})

{% extends 'layout.html' %}

{% block content %}



靓号列表
{% for data in data_list %} {% endfor %}
ID 号码 价格 级别 状态 操作
{{data.id}} {{data.mobile}} {{data.price}} {{data.get_level_display}} {{data.get_status_display}} 编辑 删除
{% endblock %}

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第31张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第32张图片

number_list(分页管理)

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第33张图片
首先要根据数据计算每一页的下标!

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第34张图片
基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第35张图片
但是这样的话,不懂我们页面设计的人可能就不会了,所以要设置分页工具。

我就简单的生成了15条数据,所以这里每一页我设置的3条奥!

先来测试一下:

def number_list(request):
    # 拿到搜索的值 如果没有设置为空
    data_dict = {}
    search_data = request.GET.get("q", "")
    if search_data:
        data_dict["mobile__contains"] = search_data

    page = int(request.GET.get("page", 1))
    pagesize = 3
    start = (page-1)*pagesize
    end = page*pagesize

    # 按照level降序排列
    # data_list = models.PrettyNum.objects.filter(**data_dict).order_by("-level")[start:end]
    data_list = models.PrettyNum.objects.filter(**data_dict)[start:end]
    return render(request, "number_list.html", {"data_list": data_list, "search_data": search_data})
{% extends 'layout.html' %}

{% block content %}



靓号列表
{% for data in data_list %} {% endfor %}
ID 号码 价格 级别 状态 操作
{{data.id}} {{data.mobile}} {{data.price}} {{data.get_level_display}} {{data.get_status_display}} 编辑 删除
{% endblock %}

基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第36张图片
但是这里的分页我们设置死了,但是如果其数据比较多,我们需要其自己自动跳转,所以还需要处理。

def number_list(request):
    # 拿到搜索的值 如果没有设置为空
    data_dict = {}
    search_data = request.GET.get("q", "")
    if search_data:
        data_dict["mobile__contains"] = search_data

    page = int(request.GET.get("page", 1))
    pagesize = 3
    start = (page-1)*pagesize
    end = page*pagesize

    # 按照level降序排列
    # data_list = models.PrettyNum.objects.filter(**data_dict).order_by("-level")[start:end]
    data_list = models.PrettyNum.objects.filter(**data_dict)[start:end]

    # 数据总条数
    total_count = models.PrettyNum.objects.filter(**data_dict).count()

    # 总页码计算 c(商),d(余数) = divmod(a,b)
    total_page_count, div = divmod(total_count, pagesize)
    if div:
        total_page_count += 1

    

    # 页码
    page_str_list = []

    # range()前取后不取
    for i in range(1, total_page_count+1):
        ele = '
  • {}
  • '.format(i, i) page_str_list.append(ele) # 将字符串使用mark_safe()后才能变成html page_string = mark_safe("".join(page_str_list)) return render(request, "number_list.html", {"data_list": data_list, "search_data": search_data, "page_string":page_string})

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第37张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第38张图片

    这里的页面网址跳转是解决了,但是还是有一个问题,如果页面太多了咋整?所以还是要解决一下!

    def number_list(request):
        # 拿到搜索的值 如果没有设置为空
        data_dict = {}
        search_data = request.GET.get("q", "")
        if search_data:
            data_dict["mobile__contains"] = search_data
    
        page = int(request.GET.get("page", 1))
        pagesize = 2
        start = (page-1)*pagesize
        end = page*pagesize
    
        # 按照level降序排列
        # data_list = models.PrettyNum.objects.filter(**data_dict).order_by("-level")[start:end]
        data_list = models.PrettyNum.objects.filter(**data_dict)[start:end]
    
        # 数据总条数
        total_count = models.PrettyNum.objects.filter(**data_dict).count()
    
        # 总页码计算 c(商),d(余数) = divmod(a,b)
        total_page_count, div = divmod(total_count, pagesize)
        if div:
            total_page_count += 1
    
        # 计算出显示当前页的前2页 后2页
        plus = 2
    
        # 设置不要出现负值
        if total_page_count <= 2 * plus + 1:
            # 数据库数据较少 都没有11页
            start_page = 1
            end_page = total_page_count
        else:
            # 数据库数据较多
            if page <= plus:
                # 不能有负值
                start_page = 1
                end_page = 2 * plus + 1
            else:
                # 设置不要超出总页码
                if (page + plus) >= total_page_count:
                    start_page = total_page_count - 2 * plus
                    end_page = total_page_count
                else:
                    start_page = page - plus
                    end_page = page + plus
    
        # 页码
        page_str_list = []
    
        # 首页
        first_page = '
  • 首页
  • '.format(1) page_str_list.append(first_page) # 上一页 if page>1: prev = '
  • 上一页
  • '.format(page-1) else: prev = '
  • 上一页
  • '.format(1) page_str_list.append(prev) # range()前取后不取 加不加一一定要注意啊! for i in range(start_page, end_page+1): if i == page: ele = '
  • {}
  • '.format(i, i) else: ele = '
  • {}
  • '.format(i, i) page_str_list.append(ele) # 下一页 if page

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第39张图片
    要是页面很多,能不能搞一个输入框,是输入一个页码数就跳到输入的页码呢?将page传到前端来,这样只用改前端即可奥!

    {% extends 'layout.html' %}
    
    {% block content %}
    
    
    
    
    靓号列表
    {% for data in data_list %} {% endfor %}
    ID 号码 价格 级别 状态 操作
    {{data.id}} {{data.mobile}} {{data.price}} {{data.get_level_display}} {{data.get_status_display}} 编辑 删除
      {{page_string}}
    {% endblock %}

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第40张图片

    现在是实现了,但是有没有发现代码很冗余,所以我们想要把这个代码进行封装一下,相当于一个公共组件,那里想用哪里用!

    # -*- coding:utf-8 -*-
    # @Author  : 雾里看花花里看雾(王晓曼)
    # @Time    : 2022/2/19 23:26
    # @FileName: Pagination.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/qq_43779149
    
    from django.utils.safestring import mark_safe
    import copy
    
    """
    分页组件使用说明:
        1、筛选数据
        # 拿到搜索的值 如果没有设置为空
        data_dict = {}
        search_data = request.GET.get("q", "")
        if search_data:
            data_dict["mobile__contains"] = search_data
        data_list = models.PrettyNum.objects.filter(**data_dict)
        2、实例化对象
        page_object = Pagination(request, data_list)
        page_list = page_object.data_list
        page_string = page_object.html()
        3、处理传参数据
        content = {
            "page_list": page_list,
            "search_data": search_data,
            "page_string": page_string,
        }
        return render(request, "number_list.html", content)
        4、前端分页数据
        
      {{page_string}}
    """ class Pagination(object): def __init__(self, request, data_list, pagesize=2, page_param="page", plus=2): """ :param request是请求请求的对象 :param data_list是符合条件的数据 :param pagesize是每页显示多少条数据 :param page_param是url中获取的分页参数 :param plus是显示当前的前后多少页 :return page_string是html分页栏 """ # 拿到url里面的参数 print(request.GET.urlencode()) query_dict = copy.deepcopy(request.GET) query_dict.mutable = True self.query_dict = query_dict self.page_param = page_param # query_dict.setlist('page', [3]) # print(query_dict.urlencode()) page = request.GET.get(page_param, "1") # 如果page是十进制的数 if page.isdecimal(): page = int(page) else: page = 1 self.page = page self.pagesize = pagesize self.plus = plus self.start = (page - 1) * pagesize self.end = page * pagesize # 注意有的是,有的是:不要搞错了! self.data_list = data_list[self.start:self.end] # 数据总条数 total_count = data_list.count() # 总页码计算 c(商),d(余数) = divmod(a,b) total_page_count, div = divmod(total_count, pagesize) if div: total_page_count += 1 self.total_page_count = total_page_count def html(self): # 设置不要出现负值 if self.total_page_count <= 2 * self.plus + 1: # 数据库数据较少 都没有11页 start_page = 1 end_page = self.total_page_count else: # 数据库数据较多 if self.page <= self.plus: # 不能有负值 start_page = 1 end_page = 2 * self.plus + 1 else: # 设置不要超出总页码 if (self.page + self.plus) >= self.total_page_count: start_page = self.total_page_count - 2 * self.plus end_page = self.total_page_count else: start_page = self.page - self.plus end_page = self.page + self.plus # 页码 page_str_list = [] # 首页 self.query_dict.setlist(self.page_param, [1]) first_page = '
  • 首页
  • '.format(self.query_dict.urlencode()) page_str_list.append(first_page) # 上一页 if self.page > 1: self.query_dict.setlist(self.page_param, [self.page - 1]) prev = '
  • 上一页
  • '.format(self.query_dict.urlencode()) else: self.query_dict.setlist(self.page_param, [1]) prev = '
  • 上一页
  • '.format(self.query_dict.urlencode()) page_str_list.append(prev) # range()前取后不取 加不加一一定要注意啊! for i in range(start_page, end_page + 1): if i == self.page: self.query_dict.setlist(self.page_param, [i]) ele = '
  • {}
  • '.format(self.query_dict.urlencode(), i) else: self.query_dict.setlist(self.page_param, [i]) ele = '
  • {}
  • '.format(self.query_dict.urlencode(), i) page_str_list.append(ele) # 下一页 if self.page < self.total_page_count: self.query_dict.setlist(self.page_param, [self.page + 1]) next = '
  • 下一页
  • '.format(self.query_dict.urlencode()) else: self.query_dict.setlist(self.page_param, [self.total_page_count]) next = '
  • 下一页
  • '.format(self.query_dict.urlencode()) page_str_list.append(next) # 尾页 self.query_dict.setlist(self.page_param, [self.total_page_count]) last_page = '
  • 尾页
  • '.format(self.query_dict.urlencode()) page_str_list.append(last_page) # 将字符串使用mark_safe()后才能变成html page_string = mark_safe("".join(page_str_list)) return page_string

    怎么使用呢?

    def number_list(request):
        # 拿到搜索的值 如果没有设置为空
        data_dict = {}
        search_data = request.GET.get("q", "")
        if search_data:
            data_dict["mobile__contains"] = search_data
    
        data_list = models.PrettyNum.objects.filter(**data_dict)
    
        page_object = Pagination(request, data_list)
        page_list = page_object.data_list
        page_string = page_object.html()
    
        content = {
            "page_list": page_list,
            "search_data": search_data,
            "page_string": page_string,
        }
        return render(request, "number_list.html", content)
    
    {% extends 'layout.html' %}
    
    {% block content %}
    
    
    
    
    靓号列表
    {% for data in page_list %} {% endfor %}
    ID 号码 价格 级别 状态 操作
    {{data.id}} {{data.mobile}} {{data.price}} {{data.get_level_display}} {{data.get_status_display}} 编辑 删除
      {{page_string}}
    {% endblock %}

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第41张图片

    ModelForm和BootStrap

    我们每写一个类就要写一个bootstrap样式,这样太累了吧,想想继承?

    # -*- coding:utf-8 -*-
    # @Author  : 雾里看花花里看雾(王晓曼)
    # @Time    : 2022/2/20 1:02
    # @FileName: BootStrap.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/qq_43779149
    
    """BootStrap样式类"""
    
    
    class BootStrapModelForm(object):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
    
            # 循环ModelForm中所有字段 给每个字段的插件设置
            for name, field in self.fields.items():
                # 字段中有属性保留原来的属性 没有属性才添加
                if field.widget.attrs:
                    field.widget.attrs["class"] = "form-control"
                    field.widget.attrs["placeholder"] = field.label
                else:
                    field.widget.attrs = {"class": "form-control", "placeholder": field.label}
    

    这样就将一个BootStrap样式类写好了,需要加样式就继承该类就好啦。

    优化项目目录

    由于当业务需求越来越广时,我们要编辑的视图函数和类会越来越多,如果都放在一个py文件中,就会看着很缭乱,所以我们需要想办法优化一下项目目录。

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第42张图片

    管理员管理

    表结构如下:

    class Admin(models.Model):
        """管理员"""
        username = models.CharField(verbose_name="用户名", max_length=32)
        password = models.CharField(verbose_name="密码", max_length=64)
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第43张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第44张图片
    写一个函数准备点数据:

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第45张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第46张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第47张图片

    admin_list

    为了安全起见,一般管理员的密码是不让显示出来的。

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第48张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第49张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第50张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第51张图片

    现在我们尝试一下,上面写的分页管理,进行简单的应用一下。

    我们先来回顾一下分页组件使用说明:

    分页组件使用说明

    分页组件使用说明:
        1、筛选数据
        # 拿到搜索的值 如果没有设置为空
        data_dict = {}
        search_data = request.GET.get("q", "")
        if search_data:
            data_dict["mobile__contains"] = search_data
        data_list = models.PrettyNum.objects.filter(**data_dict)
        2、实例化对象
        page_object = Pagination(request, data_list)
        page_list = page_object.data_list
        page_string = page_object.html()
        3、处理传参数据
        content = {
            "page_list": page_list,
            "search_data": search_data,
            "page_string": page_string,
        }
        return render(request, "number_list.html", content)
        4、前端分页数据
        
      {{page_string}}

    具体可以根据使用更改:

    def admin_list(request):
        data_list = models.Admin.objects.all()
        page_object = Pagination(request, data_list)
        content = {
            "data_list": page_object.data_list,
            "page_string": page_object.html()
        }
        return render(request, "admin_list.html", content)
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第52张图片

    admin_add

    由于我们前面写的添加页面,基本上都是差不多的,所以这里写一个公共的添加页面。

    {% extends 'layout.html' %}
    
    {% block content %}
    
    

    {{title}}

    {% csrf_token %} {% for field in form %}
    {{field}} {{field.errors.0}}
    {% endfor %}
    {% endblock %}

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第53张图片
    继承样式类后,就只用写这么一点咯。

    class AdminModelForm(BootStrapModelForm, forms.ModelForm):
        class Meta:
            model = models.Admin
            fields = "__all__"
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第54张图片
    先只写了get测试:

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第55张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第56张图片
    对于密码,我们希望确认密码,并且显示的是密码框。

    class AdminModelForm(BootStrapModelForm, forms.ModelForm):
        # 增加一个确认密码输入框 并且设置密码不可见
        confirm_password = forms.CharField(
            label="确认密码",
            # render_value=True用于密码不一致的时候 不清空密码
            widget=forms.PasswordInput(render_value=True)
        )
    
        class Meta:
            model = models.Admin
            fields = ["username", "password", "confirm_password"]
            widgets = {
                # 对于密码也设置不可见
                "password": forms.PasswordInput(render_value=True)
            }
    
        # 密码md5加密
        def clean_password(self):
            pwd = self.cleaned_data.get("password")
            # 存储的是密码密文
            return md5(pwd)
    
        def clean_confirm_password(self):
            # 第一次输入的密码
            pwd = self.cleaned_data.get("password")
            # 确认输入的密码
            confirm = md5(self.cleaned_data.get("confirm_password"))
            if pwd != confirm:
                raise ValidationError("密码不一致")
            # 放在cleaned_data 到时候save到数据库
            return confirm
    
    

    同时还希望进行md5加密。

    import hashlib
    from employeesystem.settings import SECRET_KEY
    
    
    def md5(data_string):
        """md5加密"""
        obj = hashlib.md5(SECRET_KEY.encode("utf-8"))
        obj.update(data_string.encode("utf-8"))
        return obj.hexdigest()
    

    可以看出:

    def admin_add(request):
        if request.method == "GET":
            form = AdminModelForm()
            content = {
                "form": form,
                "title": "新建管理员"
            }
            return render(request, "common.html", content)
        form = AdminModelForm(data=request.POST)
        if form.is_valid():
            # 如果数据合法  保存到数据库
            print(form.cleaned_data)
            form.save()
            return redirect("/admin/list/")
        else:
            print(form.errors)
            return render(request, "common.html", {"form": form, "title": "新建管理员"})
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第57张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第58张图片

    admin_edit

    这里我们设置只允许修改用户名。

    class AdminEditModelForm(BootStrapModelForm, forms.ModelForm):
        class Meta:
            model = models.Admin
            fields = ["username"]
    
    def admin_edit(request, nid):
        # 对象/None
        row_object = models.Admin.objects.filter(id=nid).first()
        if not row_object:
            return render(request, "error.html", {"msg": "数据不存在"})
        if request.method == "GET":
            form = AdminEditModelForm(instance=row_object)
            return render(request, "common.html", {"form": form, "title": "编辑管理员"})
        # 用Post提交的数据 进行数据校验
        form = AdminEditModelForm(data=request.POST, instance=row_object)
        if form.is_valid():
            # 如果数据合法  保存到数据库
            print(form.cleaned_data)
            # 默认保存的是用户输入的所有数据 如果想要再保存用户输入的额外的值
            # form.instance.字段名=值
            form.save()
            return redirect("/admin/list/")
        else:
            print(form.errors)
            return render(request, "common.html", {"form": form, "title": "编辑管理员"})
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第59张图片

    admin_delete

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第60张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第61张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第62张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第63张图片

    重置密码

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第64张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第65张图片

    class AdminResetModelForm(BootStrapModelForm, forms.ModelForm):
        # 增加一个确认密码输入框 并且设置密码不可见
        confirm_password = forms.CharField(
            label="确认密码",
            widget=forms.PasswordInput()
        )
    
        class Meta:
            model = models.Admin
            fields = ["password", "confirm_password"]
            widgets = {
                # 对于密码也设置不可见
                "password": forms.PasswordInput()
            }
    
        # 密码md5加密
        def clean_password(self):
            pwd = self.cleaned_data.get("password")
            # 存储的是密码密文
            return md5(pwd)
    
        def clean_confirm_password(self):
            # 第一次输入的密码
            pwd = self.cleaned_data.get("password")
            # 确认输入的密码
            confirm = md5(self.cleaned_data.get("confirm_password"))
            if pwd != confirm:
                raise ValidationError("密码不一致")
            # 放在cleaned_data 到时候save到数据库
            return confirm
    
    def admin_reset(request, nid):
        # 对象/None
        row_object = models.Admin.objects.filter(id=nid).first()
        if not row_object:
            return render(request, "error.html", {"msg": "数据不存在"})
        if request.method == "GET":
            form = AdminResetModelForm(instance=row_object)
            return render(request, "common.html", {"form": form, "title": "重置密码——{}".format(row_object.username)})
        # 用Post提交的数据 进行数据校验
        form = AdminResetModelForm(data=request.POST, instance=row_object)
        if form.is_valid():
            # 如果数据合法  保存到数据库
            print(form.cleaned_data)
            # 默认保存的是用户输入的所有数据 如果想要再保存用户输入的额外的值
            # form.instance.字段名=值
            form.save()
            return redirect("/admin/list/")
        else:
            print(form.errors)
            return render(request, "common.html", {"form": form, "title": "重置密码——{}".format(row_object.username)})
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第66张图片
    但是需要注意,重置密码的时候不能和之前的一样!

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第67张图片

    Cookie和Session

    cookie:
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第68张图片
    session:
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第69张图片

    登录认证

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第70张图片
    这个是使用form来做的:

    # 使用form来做的
    class LoginForm(BootStrapModelForm, forms.Form):
        username = forms.CharField(label="用户名", widget=forms.TextInput)
        password = forms.CharField(label="密码", widget=forms.PasswordInput)
    
        # 密码md5加密
        def clean_password(self):
            pwd = self.cleaned_data.get("password")
            return md5(pwd)
    
    def login(request):
        if request.method == "GET":
            form = LoginForm()
            return render(request, "login.html", {"form": form})
        form = LoginForm(data=request.POST)
        if form.is_valid():
            # form.cleaned_data拿到的数据 是一个字典
            admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
            if not admin_object:
                # 显示错误信息
                form.add_error("username", "用户名或者密码错误")
                return render(request, "login.html", {"form": form})
    
            # 网站生成随机字符串:写到用户浏览器的cookie中 再写入到session中
            request.session["info"] = admin_object.username
            # 用户名 密码正确 登录成功
            return redirect("/admin/list/")
        # 空的时候的处理
        return render(request, "login.html", {"form": form})
    

    相应的页面和样式如下:

    {% load static %}
    
    
    
    
        
        Login
        
        
    
    
    

    Login

    {% csrf_token %} {% for field in form %}
    {{field}} {{field.errors.0}}
    {% endfor %}
    body{
        /* 设置背景图片url */
        background-image:url(../img/flowers.jpg);
        /* 设置背景图片全屏 */
        background-size:100%;
    }
    .container{
        width:300px;
        margin-top:150px;
    }
    .panel-title{
        text-align:center;
    }
    .btn-primary{
        width:70px;
        height:35px;
        margin-left:80px;
        margin-top:10px;
    }
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第71张图片
    同样有一点,那就是如果你不登陆,那么相应的什么列表是不会让访问的,所以对于每一个函数,都要加上这样的验证信息。

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第72张图片
    所以就有了中间件!

    中间件

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第73张图片
    注意是在app对应目录下新建一个文件夹middleware,然后再创建一个文件,编辑一个中间件(也就是一个类)后,然后要去settings.py下注册该中间件!

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第74张图片

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第75张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第76张图片
    如果在process_request中,没有返回值,那就是可以继续走下去,如果有返回值,那就回来了。

    中间件执行的顺序,是按照注册的顺序来的。

    那么问题来啦,怎么使用中间件实现登录验证呢?

    class AuthMiddleware(MiddlewareMixin):
        def process_request(self, request):
    
            # 排除那些不需要登录就可以访问的页面
            # request.path_info表示获取当前用户请求的url
            if request.path_info == "/login/":
                return
    
            # 1、读取当前访问的用户的session值
            info_dict = request.session.get("info")
            if info_dict:
                return
    
            # 2、没有登录过
            return redirect("/login/")
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第77张图片

    注销

    那么怎么清除session呢?

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第78张图片

    def logout(request):
        request.session.clear()
        return redirect("/login/")
    

    这样当我们没有登录的时候,是不能访问任何其他页面的。

    于是我又把页面大致逻辑改的相对合理一点了!

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第79张图片
    当登录进去后,这里就是当前登录用户的用户名,当点击注销后就又变成了登录页面。

    图片验证码

    只有用户名和密码,存在暴力破解!

     

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第80张图片

    def login(request):
        if request.method == "GET":
            form = LoginForm()
            return render(request, "login.html", {"form": form})
        form = LoginForm(data=request.POST)
        if form.is_valid():
            # 验证码校验
    
            # 因为要在数据库中搜索 需要使用pop 注意区分session中的字段和cleaned_data中的字段!
            user_input_code = form.cleaned_data.pop("code")
            # 60秒超时就可能为空 故为了比较设置一个默认空值
            code = request.session.get("image_code", "")
            if code.upper() != user_input_code.upper():
                form.add_error("code", "验证码错误")
                return render(request, "login.html", {"form": form})
    
            # 用户名和密码校验
    
            # form.cleaned_data拿到的数据 是一个字典
            admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
            if not admin_object:
                # 显示错误信息
                form.add_error("username", "用户名或者密码错误")
                return render(request, "login.html", {"form": form})
    
            # 网站生成随机字符串:写到用户浏览器的cookie中 再写入到session中
            request.session["info"] = admin_object.username
    
            # 7天免登录 否则60秒失效
            request.session.set_expiry(60*60*24*7)
    
            # 用户名 密码正确 登录成功
            return redirect("/admin/list/")
        # 空的时候的处理
        return render(request, "login.html", {"form": form})
    

    生成随机码图片:

    def image_code(request):
        # 调用pillow函数 生成图片
        img, code_string = check_code()
        print(code_string)
    
        # 写入到自己的session中 以便于后续获取验证码再进行校验
        request.session["image_code"] = code_string
        # 给session设置60s超时
        request.session.set_expiry(60)
    
        stream = BytesIO()
        img.save(stream, 'png')
    
        return HttpResponse(stream.getvalue())
    

    任务管理

    Jquery的Ajax请求!

    class Task(models.Model):
        """任务"""
        level_choices = (
            (1, "紧急"),
            (2, "重要"),
            (3, "临时"),
        )
        level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1)
        title = models.CharField(verbose_name="标题", max_length=64)
        detail = models.TextField(verbose_name="详细信息")
    
        user = models.ForeignKey(verbose_name="负责人", to="Admin", on_delete=models.CASCADE)
    
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第81张图片

    task_list

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第82张图片

    def task_list(request):
        """任务列表"""
        form = TaskModelForm()
        return render(request, "task_list.html", {"form": form})
    
    # ajax使用post请求时需要加上csrf免除
    @csrf_exempt
    def task_add(request):
        data_dict = {"status": True}
        return HttpResponse(json.dumps(data_dict))
    
    
    {% extends 'layout.html' %}
    
    {% block content %}
    

    任务表单

    {% for field in form %}
    {{field}} {{field.errors.0}}
    {% endfor %}
    {% endblock %} {% block js %} {% endblock %}

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第83张图片

    task_add

    $.ajax({
                        url: 请求的url,
                        type: 请求的方式,
                        data: 请求的数据,
                        dataType: 请求的数据格式,
                        success: function(成功时返回的结果){
                            console.log(res);
                            console.log(res.status);
                            console.log(res.data);
                        }
                    })
    

    如下:

    # ajax使用post请求时需要加上csrf免除
    @csrf_exempt
    def task_add(request):
        # 用户发送过来的数据进行校验(ModelForm进行校验)
        form = TaskModelForm(data=request.POST)
        if form.is_valid():
            form.save()
            data_dict = {"status": True}
            return HttpResponse(json.dumps(data_dict))
        data_dict = {"status": False, "error": form.errors}
        return HttpResponse(json.dumps(data_dict))
    

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第84张图片
    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第85张图片

    基于Pycharm的Django学习 —— 项目实战(分页、cookie、session、中间件、Ajax)_第86张图片
    下一篇文章见!!!

    你可能感兴趣的:(Python,django,pycharm,学习)