员工管理系统:编辑用户、删除用户、靓号增删改查、分页功能实现、封装分页功能、第三方时间组件的使用

  • 部门管理
  • 用户管理
    • 用户列表
    • 新建用户
- ModelForm:针对数据库中的某个表
- Form。

1. 编辑用户

  • 点击编辑,跳转到编辑页面(将编辑行的ID携带过去)

  • 编辑页面显示默认数据(根据ID获取并设置到页面中)

  • 提交

    • 错误提示
    • 数据校验
    • 在数据库更新
models.UserInfo.filter(id=4).update(...)
def user_edit(request, nid):
    """ 编辑用户 """
    row_object = models.UserInfo.objects.filter(id=nid).first()

    if request.method == "GET":
        # 根据ID去数据库获取编辑的那一行数据
        form = UserModelForm(instance=row_object)
        return render(request, 'user_edit.html', {'form':form})
    
    form = UserModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        # 默认保存的是用户输入的所有数据;如果想要在用户输入的数据以外增加一些参数值
        # form.instance.字段名 = 值
        form.save()
        return redirect('/user/list/')
    return render(request, 'user_edit.html', {'form':form})

2. 删除用户

def user_delete(request, nid):
    models.UserInfo.objects.filter(id=nid).delete()
    return redirect('/user/list/')


3. 靓号管理

3.1 数据库表结构

ID mobile price level(级别 Choices) status(状态:1未占用/2已占用)
  • 根据表结构的需求,在models.py中创建类,由类生成数据库中的表
class PrettyNum(models.Model):
    """ 靓号表 """
    # 想要允许为空: null = True, blank = True

    mobile = models.CharField(verbose_name="手机号", max_length=11)
    price = models.IntegerField(verbose_name="价格", default=100)

    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)
  • 在数据库中手动添加部分数据
INSERT INTO app01_prettynum(mobile,price,level,status) VALUES("11112345678", 1000, 1, 2);

3.2 靓号列表

  • URL
  • 函数
    • 获取所有靓号
    • 结合HTML+render将靓号罗列出来
ID    号码    价格    级别    状态    操作
def pretty_num_list(request):
    """ 靓号列表 """
    pretty_num_list_data = models.PrettyNum.objects.all().order_by('-level')
    return render(request, 'pretty_num_list.html', {'pretty_num_list_data':pretty_num_list_data})

3.3 新建靓号

  • 列表页面点击跳转

  • URL

  • ModelForm类

from django import form
# 需要先导入 form

class PrettyModelForm(forms.ModelForm):
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
class PrettyNumModelForm(forms.ModelForm):
    # name = forms.CharField(min_length=3, label="姓名")

    """ 方式一:验证手机号格式是否正确(字段+正则)"""
    # mobile = forms.CharField(
    #     label="手机号",
    #     # validators=[RegexValidator(r'^1\d{10}$', '格式错误,请输入正确手机号')]
    #     validators=[RegexValidator(r'^1[3-9]\d{9}$', '格式错误,请输入正确手机号')]
    # )
    class Meta:
        model = models.PrettyNum
        fields = ["mobile", "price", "level", "status"]
        # fields = "__all__"  所有字段
        # exclude = ['level']  排除该字段
        # widgets = {
        #     'name': forms.TextInput(attrs={'class':'form-control', 'placeholder':'请输入内容'}),
        #     'password': forms.PasswordInput(attrs={'class':'form-control', 'placeholder':'请输入内容'}),
        # }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name,field in self.fields.items():
            field.widget.attrs = {'class': 'form-control', 'placeholder': '请输入内容'}

    """ 方式二:验证手机号格式是否正确(钩子)"""
    def clean_mobile(self):
        txt_mobile = self.cleaned_data["mobile"]
        if len(txt_mobile) != 11:
            raise ValidationError("手机号长度不正确,请重新输入")

        return txt_mobile
  • 函数
    • 实例化类的对象
    • 通过render将对象传入到HTML中
    • 模板的循环展示所有字段值
  • 点击提交
    • 数据校验
    • 保存到数据库
    • 跳转回靓号列表
"""
添加靓号
"""
def pretty_num_add(request):
    if request.method == "GET":
        form = PrettyNumModelForm()
        return render(request, 'pretty_num_add.html', {'form':form})
    
    form = PrettyNumModelForm(data=request.POST)
    if form.is_valid():
        form.save()
        return redirect('/pretty/num/list/')
    return render(request, 'pretty_num_add.html', {'form':form})

3.4 编辑靓号

  • 列表页面:pretty/num/ID(数字)/edit
  • URL
  • 函数
    • 根据ID获取当前编辑的对象
    • ModelForm配合,默认显示编辑前的数据
    • 提交修改

不允许手机号重复

  • 添加【正则表达式】【手机号不能重复】
queryset = models.PrettyNum.objects.filter(mobile="xxx")
queryset = models.PrettyNum.objects.filter(mobile="xxx").first()

# True/False [判断是否已存在]
queryset = models.PrettyNum.objects.filter(mobile="xxx").exists()
  • 编辑【正则表达式】【手机号不能重复】
# 排除自己以外,判断其他的数据中手机号是否重复

# id!=2 and mobile="xxx"
models.PrettyNum.objects.filter(mobile="xxx").exclude(id=2)
class PrettyNumEditModelForm(forms.ModelForm):
    mobile = forms.CharField(
        label="手机号",
        validators=[RegexValidator(r'^1[3-9]\d{9}$', '格式错误,请输入正确手机号')],
        # disabled=True,
    )
    class Meta:
        model = models.PrettyNum
        fields = ["mobile", "price", "level", "status"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name,field in self.fields.items():
            field.widget.attrs = {'class': 'form-control', 'placeholder': '请输入内容'}

    def clean_mobile(self):

        # 当前编辑的那一行的ID
        # self.instance.pk

        txt_mobile = self.cleaned_data["mobile"]
        exists = models.PrettyNum.objects.exclude(id=self.instance.id).filter(mobile=txt_mobile).exists()
        if exists:
            raise ValidationError("手机号已存在,请重新输入")

        return txt_mobile
"""
编辑靓号
"""
def pretty_num_edit(request, nid):
    row_object = models.PrettyNum.objects.filter(id=nid).first()

    if request.method == "GET":
        form = PrettyNumEditModelForm(instance=row_object)
        return render(request, 'pretty_num_edit.html', {'form':form})
    
    form = PrettyNumEditModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        form.save()
        return redirect('/pretty/num/list')
    return render(request, 'pretty_num_edit.html', {'form': form})

3.5 搜索手机号

models.PrettyNum.objects.filter(mobile="xxx", id=16)

# 传入空字典时,相当于获取所有的数据
data_dict = {"mobile":"xxx", "id":15}
models.PrettyNum.objects.filter(**data_dict)
models.PrettyNum.objects.filter(id=16)  # id=xx

models.PrettyNum.objects.filter(id__gt=16)  # id>xx
models.PrettyNum.objects.filter(id__gte=16)  # id>=xx

models.PrettyNum.objects.filter(id__lt=16)  # id<xx
models.PrettyNum.objects.filter(id__lte=16)  # id<=xx
models.PrettyNum.objects.filter(mobile="xxx")  # mobile=xxx

models.PrettyNum.objects.filter(mobile__startswith="xxx")  # mobile以xxx开头
models.PrettyNum.objects.filter(mobile__endswith="xxx")  # mobile以xxx结尾

models.PrettyNum.objects.filter(mobile__contains="xxx")  # mobile包含xxx
# 综合使用
data_dict = {
    "mobile__startswith":"xxx",
    "id__lt":15,
}
models.PrettyNum.objects.filter(**data_dict)
  • 实例
"""
靓号列表
"""
def pretty_num_list(request):

    # 定义一个空字典,如果获取到了q参数值,则传入进行查询;否则传入空字典
    # 传入空字典时表示查询所有数据
    data_dict = {}
    search_data = request.GET.get('q', '')
    if search_data:
        data_dict["mobile__contains"] = search_data

    # 根据level降序排序
    pretty_num_list_data = models.PrettyNum.objects.filter(**data_dict).order_by('-level')

    return render(request, 'pretty_num_list.html', {'pretty_num_list_data':pretty_num_list_data, 'search_data':search_data})
<div style="margin-bottom: 20px" class="clearfix">
    <a href="/pretty/num/add/" class="btn btn-success">
      <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
      新建靓号
    </a>
    <div style="float: right; width: 300px">
      <form method="get">
        <div class="input-group">
          <input
            type="text"
            name="q"
            class="form-control"
            placeholder="请输入手机号..."
            value="{{ search_data }}"
          />
          <span class="input-group-btn">
            <button class="btn btn-default" type="submit">
              <span
                class="glyphicon glyphicon-search"
                aria-hidden="true"
              ></span>
              搜索
            </button>
          </span>
        </div>
      </form>
    </div>
  </div>

3.6 列表分页

models.PrettyNum.objects.all()
# 数据结果切片
models.PrettyNum.objects.filter(id__gt=16)[0:10]
models.PrettyNum.objects.all()[0:10]
def pretty_num_list(request):
    # 定义一个空字典,如果获取到了q参数值,则传入进行查询;否则传入空字典
    # 传入空字典时表示查询所有数据
    data_dict = {}
    search_data = request.GET.get('q', '')
    if search_data:
        data_dict["mobile__contains"] = search_data

    # 1.根据用户想要访问的页码,计算出起止位置
    page = int(request.GET.get('page', 1))
    page_size = 15 # 每页显示的数据条数
    start_page = (page - 1) * page_size
    end_page = page * page_size

    # 数据总条数
    data_count = models.PrettyNum.objects.filter(**data_dict).order_by('-level').count()
    # 计算出总页码
    total_page_count, div = divmod(data_count, page_size)
    if div:
        total_page_count += 1
    # 根据level降序排序;并切片查询出指定数量的数据
    pretty_num_list_data = models.PrettyNum.objects.filter(**data_dict).order_by('-level')[start_page:end_page]
    # 计算出 显示当前页的前5页、后5页
    plus = 5  # 前N页
    if total_page_count <= plus * 2 + 1:
        # 数据库中的数据比较少时,都没有达到plus*2+1页
        start_page_num = 1
        end_page_num = total_page_count
    else:
        # 数据库中的数据比较多时,>plus*2+1页
        # 当前页<5时(小极值)
        if page <= plus:
            start_page_num = 1
            end_page_num = plus * 2 + 1
        else:
            # 当前页>5时(大极值)
            if (page + plus) > total_page_count:
                start_page_num = total_page_count - plus * 2
                end_page_num = total_page_count
            else:
                start_page_num = page - plus
                end_page_num = page + plus


    # 页码
    page_str_list = []

    # 首页
    page_str_list.append('<li><a href="?page={}">首页</a></li>'.format(1))

    # 上一页
    if page > 1 :
        prev = '<li><a href="?page={}">« 上一页</a></li>'.format(page - 1)
    else:
        prev = '<li><a href="?page={}">« 上一页</a></li>'.format(1)
    page_str_list.append(prev)


    for i in range(start_page_num, end_page_num + 1):
        if i == page:
            ele = '<li class="active"><a href="?page={}">{}</a></li>'.format(i,i)
        else:
            ele = '<li><a href="?page={}">{}</a></li>'.format(i,i)
        page_str_list.append(ele)

        
    # 下一页
    if page < total_page_count :
        prev = '<li><a href="?page={}">下一页 »</a></li>'.format(page + 1)
    else:
        prev = '<li><a href="?page={}">下一页 »</a></li>'.format(total_page_count)
    page_str_list.append(prev)

    page_str_list.append('<li><a href="?page={}">尾页</a></li>'.format(total_page_count))

    # 告诉浏览器该字符串可信/安全 mark_safe
    page_string = mark_safe("".join(page_str_list))

    return render(request, 'pretty_num_list.html',
                  {'pretty_num_list_data': pretty_num_list_data, 'search_data': search_data, "page_string": page_string})

4. 分页组件的封装

Pagination —> 公共组件

"""
自定义的分页组件,以后如果想要使用这个分页组件,需要做如下几件事:

(1) 在视图函数中:
def pretty_num_list(request):

    # 1. 根据自己的情况去筛选自己想要的数据
    pretty_num_list_data = models.PrettyNum.objects.all()

    # 2. 实例化分页对象
    page_object = Pagination(request, pretty_num_list_data)

    # 3. 创建字典,获取数据
    context = {
        'search_data': search_data,
        'pretty_num_list_data': page_object.page_queryset,  # 分完页的数据
        "page_string": page_object.html(),                  # 生成页码
    }
    # 4. 返回相关内容
    return render(request, 'pretty_num_list.html', context)

(2) 在HTML中:

{% for obj in pretty_num_list_data %}
    {{obj.xxx}}
{% endfor %}

<ul class="pagination">
      {{ page_string }}
</ul>
"""


from django.utils.safestring import mark_safe


class Pagination(object):
    
    def __init__(self, request, queryset, page_size=10, page_param="page", plus = 5):
        """
        :param request: 请求的对象
        :param queryset: 查询的符合条件的数据(根据这个数据给他进行分页处理)
        :param page_size: 每页显示多少条数据
        :param page_param: 在URL中传递的获取分页的参数, 例如: /pretty/list/?page=12
        :param plus: 显示当前页的 前或后n页 (页码)
        """

        from django.http.request import QueryDict
        import copy

        query_dict = copy.deepcopy(request.GET)
        query_dict._mutable = True
        self.query_dict = query_dict   
        self.page_param = page_param     


        page = request.GET.get(page_param, "1")


        # 如果输入的不是数字,则赋值为1,即初始页面第1页
        if page.isdecimal():
            page = int(page)
        else:
            page = 1
        self.page = page
        self.page_size = page_size

        self.start = (page - 1) * page_size
        self.end = page * page_size

        self.page_queryset = queryset[self.start:self.end]


        # 数据总条数
        total_count = queryset.count()

        # 计算出总页码
        total_page_count, div = divmod(total_count, page_size)
        if div:
            total_page_count += 1

        self.total_page_count = total_page_count
        self.plus = plus


    def html(self):
        # 计算出 显示当前页的前5页、后5页
          # 前N页
        if self.total_page_count <= self.plus * 2 + 1:
            # 数据库中的数据比较少时,都没有达到plus*2+1页
            start_page_num = 1
            end_page_num = self.total_page_count
        else:
            # 数据库中的数据比较多时,>plus*2+1页
            # 当前页<5时(小极值)
            if self.page <= self.plus:
                start_page_num = 1
                end_page_num = self.plus * 2 + 1
            else:
                # 当前页>5时(大极值)
                if (self.page + self.plus) > self.total_page_count:
                    start_page_num = self.total_page_count - self.plus * 2
                    end_page_num = self.total_page_count
                else:
                    start_page_num = self.page - self.plus
                    end_page_num = self.page + self.plus


        # 页码
        page_str_list = []

        
        # 首页
        self.query_dict.setlist(self.page_param, [1])
        page_str_list.append('<li><a href="?{}">首页</a></li>'.format(self.query_dict.urlencode()))

        # 上一页
        if self.page > 1 :
            self.query_dict.setlist(self.page_param, [self.page - 1])
            prev = '<li><a href="?{}">« 上一页</a></li>'.format(self.query_dict.urlencode())
        else:
            self.query_dict.setlist(self.page_param, [1])
            prev = '<li><a href="?{}">« 上一页</a></li>'.format(self.query_dict.urlencode())
        page_str_list.append(prev)


        # 页面
        for i in range(start_page_num, end_page_num + 1):
            self.query_dict.setlist(self.page_param, [i])
            if i == self.page:
                ele = '<li class="active"><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(),i)
            else:
                ele = '<li><a href="?{}">{}</a></li>'.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])
            prev = '<li><a href="?{}">下一页 »</a></li>'.format(self.query_dict.urlencode())
        else:
            self.query_dict.setlist(self.page_param, [self.total_page_count])
            prev = '<li><a href="?{}">下一页 »</a></li>'.format(self.query_dict.urlencode())
        page_str_list.append(prev)

        # 尾页
        self.query_dict.setlist(self.page_param, [self.total_page_count])
        page_str_list.append('<li><a href="?{}">尾页</a></li>'.format(self.query_dict.urlencode()))

        search_string = """
        <li>
            <form method="get" style="float: left; margin-left: 20px">
            <input
                name="page"
                style="
                position: relative;
                float: left;
                display: inline-block;
                width: 160px;
                border-radius: 0;
                "
                type="text"
                class="form-control"
                placeholder="页码"
            />
            <button
                style="border-radius: 0"
                class="btn btn-default"
                type="submit"
            >
                跳转
            </button>
            </form>
        </li>
        """

        page_str_list.append(search_string)

        # 告诉浏览器该字符串可信/安全 mark_safe
        page_string = mark_safe("".join(page_str_list))
        return page_string

5. 时间选择组件

  • 引入css
  • 引入js

详情见employeeManage –> user_add.html、user_model_form_add.html

评论