8.Djiango-用户管理

Djiango-用户管理

  • 创建一些用户数据

  insert into blog_employee(name,password,age,account,create_time,gender,depart_id) values("李云龙","123456",45,50000,"2020-03-24",1,102);
  insert into blog_employee(name,password,age,account,create_time,gender,depart_id) values("张三丰","123456",45,60000,"2021-03-24",1,103);
  insert into blog_employee(name,password,age,account,create_time,gender,depart_id) values("周杰伦","123456",45,70000,"2022-03-24",1,101);
  • 用户展示 user_list.html

  def users_list(req):
      users_list = Employee.objects.all().order_by("id")
      # for obj in users_list:
          # gender 对应的数据获取
          # gender_choices = (
          #     (1, "男"),
          #     (2, "女"),
          #     )
          # gender = models.SmallIntegerField(choices=gender_choices,verbose_name="性别")
          # print(obj.gender, obj.get_gender_display())
          # # depart 外键值获取
          # # depart = models.ForeignKey(to="Department", to_field="dep_id", on_delete=models.CASCADE, verbose_name="部门")
          # print(obj.depart_id, obj.depart.title)

          # # 循环取所有数据
          # print(obj.id, obj.name, obj.account, obj.age, obj.create_time.strftime(
          #     "%Y-%m-%d"), obj.gender, obj.get_gender_display(), obj.depart_id, obj.depart.title)

          # html 内写法
          # <td>{{ iteam.create_time|date:"Y-m-d" }}</td>
          # <td>{{ iteam.get_gender_display }}</td>
          # <td>{{ iteam.depart.title }}</td>
      return render(req, 'users_list.html', {"users_list": users_list})
  <div class="panel panel-default">
  <div class="panel-heading">
      <strong>
      <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> 用户列表
      </strong>
  </div>
  <table class="table table-bordered">
      <thead>
      <tr>
          <th>ID</th>
          <th>姓名</th>
          <th>密码</th>
          <th>年龄</th>
          <th>余额/元</th>
          <th>入职时间</th>
          <th>性别</th>
          <th>部门</th>
          <th>操作</th>
      </tr>
      </thead>
      <tbody>
      {% for iteam in users_list %}
      <tr>
          <th scope="row">{{ iteam.id }}</th>
          <td>{{ iteam.name }}</td>
          <td>{{ iteam.password }}</td>
          <td>{{ iteam.age }}</td>
          <td>{{ iteam.account }}</td>
          <td>{{ iteam.create_time|date:"Y-m-d" }}</td>
          <td>{{ iteam.get_gender_display }}</td>
          <td>{{ iteam.depart.title }}</td>
          <td>
          <a class="btn btn-primary btn-xs" href="/users/{{ iteam.id }}/edit/"> 编辑 </a>
          <a class="btn btn-danger btn-xs" href="/users/del/?nid={{ iteam.id }}"> 删除 </a>
          </td>
      </tr>
      {% endfor %}
      </tbody>
  </table>
  </div>
  • 用户添加 users_add.html 原始方式

  def users_add(req):
      """部门添加"""
      if req.method == "GET":
          # gender_choices = UserInfo.gender_choices
          # depart_list = Department.objects.all()
          # return render(req, "users_add.html", {"depart_list": depart_list, "gender_choices": gender_choices})
          context = {
              "gender_choices" : Employee.gender_choices,
              "depart_list" : Department.objects.all()
          }

          return render(req, "users_add.html", context)

      try:
          # 获取提交的用户数据
          users_name = req.POST.get("users_name")
          users_password = req.POST.get("users_password")
          users_age = req.POST.get("users_age")
          users_account = req.POST.get("users_account")
          users_create_time = req.POST.get("users_create_time")
          users_gender = req.POST.get("users_gender")
          users_depart_id = req.POST.get("users_depart_id")

          # 保存到数据库
          Employee.objects.create(name=users_name, password=users_password, age=users_age, account=users_account,
                                  create_time=users_create_time, gender=users_gender, depart_id=users_depart_id)

          # 重定向回用户列表
          return redirect("/users/list/")
      except Exception as e:
          # print(e)
          return render(req, "users_add.html", {"error_msg": e})
  <form class="form-group" method="post" action="/users/add/">
      {% csrf_token %}
      <div class="form-group">
      <label for="users_add"><strong>用户名:</strong></label>
      <input type="text" class="form-control" name="users_name" style="width: 300px;" id="users_add" placeholder="用户名">
      </div>
      <div class="form-group" >
      <label for="users_add"><strong>密码:</strong></label>
      <input type="text" class="form-control" name="users_password" style="width: 300px;" id="users_add" placeholder="密码">
      </div>
      <div class="form-group" >
      <label for="users_add"><strong>年龄:</strong></label>
      <input type="text" class="form-control" name="users_age" style="width: 300px;" id="users_add" placeholder="年龄">
      </div>
      <div class="form-group" >
      <label for="users_add"><strong>余额/元:</strong></label>
      <input type="text" class="form-control" name="users_account" style="width: 300px;" id="users_add" placeholder="例: 10000.0">
      </div>
      <div class="form-group" >
      <label for="users_add"><strong>入职时间:</strong></label>
      <input type="text" class="form-control" name="users_create_time" style="width: 300px;" id="users_add" placeholder="例: 2023-10-10">
      </div>
      <div class="form-group" >
      <label for="users_add"><strong>性别:</strong></label>
      <!-- <input type="text" class="form-control" name="users_gender" style="width: 300px;" id="users_add" placeholder="1 或 2 ,1: 男 2: 女"> -->
      <select name="users_gender" id="users_add" class="form-control" style="width: 300px;">
          {% for items in gender_choices %}
          <option value="{{ items.0 }}">{{ items.1 }}</option>
          {% endfor %}
      </select>
      </div>
      <div class="form-group" >
      <label for="users_add"><strong>部门:</strong></label>
      <!-- <input type="text" class="form-control" name="users_depart_id" style="width: 300px;" id="users_add" placeholder="例: 101"> -->
      <select name="users_depart_id" id="users_add" class="form-control" style="width: 300px;">
          {% for items in depart_list %}
          <option value="{{ items.dep_id }}">{{ items.title }}</option>
          {% endfor %}
      </select>
      </div>
      <div class="form-group" >
      <button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span><strong> 提 交</strong></button>
      </div>
      <br>
      <span style="color: red;">  {{ error_msg }}  </span>
  </form>
  • 缺点
    • 数据校验较麻烦
    • 数字输入错误没有错误提示
    • 页面上每一个字段都需要重新写一遍
    • 关联的数据,需要手动获取并循环展示在页面
    • Django组件

  • Form组件(简便)
    • 初识 Form views.py

      class MyForm(Form):
        user = forms.CharField(widget=forms.Input)
        pwd = forms.CharField(widget=forms.Input)
        email = forms.CharField(widget=forms.Input)
      
      def user_add(req):
        if req.method == "GET":
        form = MyForm()
            return render(req, "user_add.html", {"form": form})
    • user_add_form.html {{ form.xxx }} 可以自动生成前端代码
      <form method="post">
      {{ form.user }}
      {{ form.pwd }}
      {{ form.email }}
      </form>
      <!-- 或 -->
      <form method="post">
      {% for field in form %}
      {{ field }}
      {% endfor %}
      </form>
  • ModelForm组件(最简便)
    • models.py
    # 员工管理系统表
    class Department(models.Model):
        """部门表"""
        dep_id = models.IntegerField(verbose_name="部门代码", null=True, blank=True, unique=True)
        title = models.CharField(max_length=32, verbose_name='标题', null=True, blank=True, unique=True)

    class Employee(models.Model):
        """员工表"""
        name = models.CharField(max_length=16, verbose_name="姓名")
        password = models.CharField(max_length=64, verbose_name="密码")
        age = models.IntegerField(verbose_name="年龄")
        account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
        create_time = models.DateField(verbose_name="入职时间")

        # 外键约束
        # to 表示与哪张表关联
        # to_field 表示表中的哪一列
        # 在django中,数据表中的名称自动加上_id,也就是depart_id
        # on_delete=models.CASCADE 表示级联删除(删除部门,部门下的所有员工都会被删除)
        depart = models.ForeignKey(to="Department", to_field="dep_id", on_delete=models.CASCADE, verbose_name="部门")
        # on_delete=models.SET_NULL, null=True, blank=True 表示置空(删除部门,部门下的所有员工的部门字段置为空)
        #depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.SET_NULL, null=True, blank=True)

        gender_choices = (
            (1, "男"),
            (2, "女"),
        )
        gender = models.SmallIntegerField(choices=gender_choices,verbose_name="性别")
* views.py
      from django import forms
      class MyForm(forms.ModelForm):
        xxx = forms.CharField("...")  # 自定义字段
        class Meta:
          field = ["name", "password", "age", "xxx"]   # 按需引用models字段和自定义字段

      def user_add(req):
        if req.method == "GET":
        form = MyForm()
          return render(req, "user_add.html", {"form": form})
  * user_add_modelform.html    `{{ form.xxx }} 可以自动生成前端代码`
        <form method="post">
          {{ form.user }}
          {{ form.pwd }}
          {{ form.email }}
        </form>
        <!-- 或 -->
        <form method="post">
          {% for field in form %}
          {{ field }}
          {% endfor %}
        </form>

Django之ModelForm组件

ModelForm
    a.  class Meta:
            model,                           # 对应Model的
            fields=None,                     # 字段
            exclude=None,                    # 排除字段
            labels=None,                     # 提示信息
            help_texts=None,                 # 帮助提示信息
            widgets=None,                    # 自定义插件
            error_messages=None,             # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
            field_classes=None               # 自定义字段类 (也可以自定义字段)
            localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据
            如:
                数据库中
                    2016-12-27 04:10:57
                setting中的配置
                    TIME_ZONE = 'Asia/Shanghai'
                    USE_TZ = True
                则显示:
                    2016-12-27 12:10:57
    b. 验证执行过程
        is_valid -> full_clean -> 钩子 -> 整体错误

    c. 字典字段验证
        def clean_字段名(self):
            # 可以抛出异常
            # from django.core.exceptions import ValidationError
            return "新值"
    d. 用于验证
        model_form_obj = XXOOModelForm()
        model_form_obj.is_valid()
        model_form_obj.errors.as_json()
        model_form_obj.clean()
        model_form_obj.cleaned_data
    e. 用于创建
        model_form_obj = XXOOModelForm(request.POST)
        #### 页面显示,并提交 #####
        # 默认保存多对多
            obj = form.save(commit=True)
        # 不做任何操作,内部定义 save_m2m(用于保存多对多)
            obj = form.save(commit=False)
            obj.save()      # 保存单表信息
            obj.save_m2m()  # 保存关联多对多信息

    f. 用于更新和初始化
        obj = model.tb.objects.get(id=1)
        model_form_obj = XXOOModelForm(request.POST,instance=obj)
        ...

        PS: 单纯初始化
            model_form_obj = XXOOModelForm(initial={...})
  • 用户添加 users_add_modelform.html modelform方式优化

    • models.py
    class Department(models.Model):
        """部门表"""
        dep_id = models.IntegerField(
            verbose_name="部门代码", null=True, blank=True, unique=True)
        title = models.CharField(
            max_length=32, verbose_name='标题', null=True, blank=True, unique=True)

        def __str__(self):
            # haha = str(self.dep_id) + ":" + str(self.title)
            return str(self.dep_id) + ":" + str(self.title)
  • views.py
    class UserModelForm(forms.ModelForm):
        # xxx = forms.CharField("...")  # 自定义字段
        # 自定义数据校验
        # 例如: 用户名最小三个字符
        # name = forms.CharField(min_length=3, label="用户名")

        class Meta:
            model = Employee
            fields = ["name", "password", "age", "account",
                    "create_time", "gender", "depart"]  # 按需引用models字段和自定义字段
            # 逐一控制标签的样式
            # widgets = {
            #     "name": forms.TextInput(attrs={"class": "form-control"}),
            #     "password": forms.PasswordInput(attrs={"class": "form-control"}),
            #     "age": forms.TextInput(attrs={"class": "form-control"}),
            # }

            # 这里让日期可以手动点击鼠标选择,所以单独拎出来,加上日期插件
            # widgets = {
            #     "create_time": forms.DateInput(attrs={'class': 'form-control', 'id': 'myDate', "style": "width: 300px;"}),
            # }
            widgets = {
                "create_time": forms.DateTimeInput(attrs={'class': 'form-control', 'id': 'myDate', "value": "2023-01-01", "style": "width: 300px;", "placeholder": "入职日期"}),
            }

        # 循环找到所有的插件,添加 "class": "form-control"
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)

            for name, field in self.fields.items():
                # 可以排除指定的字段
                if name == "create_time":
                    # global widgets
                    # widgets = {
                    #     "create_time": forms.DateInput(attrs={'class': 'form-control', 'id': 'myDate', "value": "2020-01-01 00:00", "style": "width: 300px;"}),
                    # }
                    continue
                # print(name, field)
                field.widget.attrs = {
                    "class": "form-control", "style": "width: 300px;", "placeholder": field.label}

    def users_add_modelform(req):
        """添加用户(ModelForm版本)"""
        if req.method == "GET":
            form = UserModelForm()
            return render(req, "users_add_modelform.html", {"form": form})

        try:
            # 用户POST请求提交数据,需要进行数据校验
            form = UserModelForm(data=req.POST)
            if form.is_valid():
                print(form.cleaned_data)
                # {'name': 'test', 'password': 'test', 'age': 22, 'account': Decimal('2222'), 'create_time': datetime.date(2023, 1, 5), 'gender': 1, 'depart': <Department: 101:销售>}
                # 如果数据合法保存至数据库
                # Employee.objects.create(...)
                # 插入用户输入以外字段的值
                # form.instance.字段名 = 值
                form.save()
                return redirect("/users/list/")

            # 校验失败(在页面上显示错误信息)
            return render(req, "users_add_modelform.html", {"form": form})

        except Exception as e:
            # print(e)
            return render(req, "users_add_modelform.html", {"error_msg": e})
  • users_add_modelform.html
    {% extends 'layout.html' %}

    {% block css %}
    {% load static %}
    <link rel="stylesheet" href="{% static 'plugins/font-awesome-4.7.0/css/font-awesome.css' %}">
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css' %}">
    {% endblock %}

    {% block content %}
    <div class="container">
        <hr>
        </hr>
        <div class="panel panel-default">
        <div class="panel-heading">
            <h3 class="panel-title"><strong>新建用户-ModelForm</strong></h3>
        </div>
        <div class="panel-body">
            <form class="form-group" method="post" action="/users/add_modelform/">
            {% csrf_token %}

            {% for field in form %}
                <div class="form-group">
                    <label>{{ field.label }}: </label>
                    {{ field }}
                    <!-- 数据校验,显示错误信息 -->
                    <span style="color: red;">{{ field.errors.0 }}</span>
                </div>
            {% endfor %}
            <div class="form-group" >
                <button type="submit" class="btn btn-success"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span><strong> 提 交</strong></button>
            </div>
            <br>
            <span style="color: red;">  {{ error_msg }}  </span>
            </form>
        </div>
        </div>
    </div>
    {% endblock %}

    {% block js %}
    {% load static %}
    <script src="{% static 'plugins/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js' %}"></script>
    <script src="{% static 'plugins/bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js' %}"></script>

    <script>
        $(function () {
            $('#myDate').datetimepicker({
                // format: 'yyyy-mm-dd hh:ii',
                minView: "month", //选择日期后,不会再跳转去选择时分秒
                format: 'yyyy-mm-dd',
                startDate: '0',
                language: 'zh-CN',
                autoclose: true
            })
        })
    </script>
    {% endblock %}
  • 注意
    <!-- 关闭浏览器数据验证 -->
    <form class="form-group" method="post" action="/users/add_modelform/" novalidate>
    <!-- ModelForm 数据验证错误信息 -->
    <span style="color: red;">{{ field.errors.0 }}</span>