【Django】教程-5-ModelForm增删改查+规则校验【正则+钩子函数】

【Django】教程-1-安装+创建项目+目录结构介绍
【Django】教程-2-前端-目录结构介绍
【Django】教程-3-数据库相关介绍
【Django】教程-4-一个增删改查的Demo

11. ModelForm

11.1 models.py

python 复制代码
from django.utils import timezone
from django.db import models

# 数据库,表对应关系
class Department(models.Model):
    """部门表"""
    title = models.CharField(verbose_name="部门名称", max_length=32)
    create_time = models.DateTimeField(verbose_name="创建时间", default=timezone.now)
    # 数据状态
    status_choices = (
        (1, "已删除"),
        (0, "可用"),
    )
    status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=0)

    # 重写toString方法
    def __str__(self):
        return self.title


class UserInfo(models.Model):
    '''用户表'''

    name = models.CharField(verbose_name="姓名", max_length=32)
    password = models.CharField(verbose_name="密码", max_length=64)
    age = models.IntegerField(verbose_name="年龄")
    create_time = models.DateTimeField(verbose_name="创建时间", default=timezone.now)
    depart = models.ForeignKey(verbose_name="部门", to="Department", to_field="id", null=True, blank=True,
                               on_delete=models.SET_NULL)

    # 在django中做的约束
    gender_choices = (
        (1, "男"),
        (2, "女"),
    )
    gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices, default=1)
    # 数据状态
    status_choices = (
        (1, "已删除"),
        (0, "可用"),
    )
    status = models.SmallIntegerField(verbose_name="数据状态", choices=status_choices, default=0)

11.1 forms.py

python 复制代码
from django import forms
from .models import Department, UserInfo


class DepartmentForm(forms.ModelForm):
    class Meta:
        model = Department
        fields = "__all__"


class UserModelForm(forms.ModelForm):
    name = forms.CharField(min_length=8, label="用户名")
    class Meta:
        model = UserInfo
        # fields = ['name', 'password', 'age', 'gender']
        fields = "__all__"
        # 排除哪个字段
        # exclude = ['status']
        
	# 样式重写 
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

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

11.1 urls.py

python 复制代码
from django.urls import path
from appTang import views

# 映射关系,视图--->函数
urlpatterns = [
    path('user/modelform/add', views.user_modelform_add),
    path('user/modelform/edit/<int:nid>/', views.user_modelform_edit),

]

11.1 user_model_add.html

html 复制代码
{% extends 'layout.html' %}

{% block title %}
    添加用户
{% endblock %}

{% block content %}
    <div>
        <div class="container">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">
                        <span class="glyphicon glyphicon-tag" aria-hidden="true">
                            添加用户</span>
                    </h3>
                </div>

                <div class="panel panel-body">
{#                novalidate, 关掉浏览器的校验#}
                    <form method="post" novalidate>

                        {% csrf_token %}

                        {% for u in user %}
                            <div class="form-group">
                                <label>{{ u.label }}:</label>
                                {{ u }}
                                <span style="color: red">{{ u.errors.0 }}</span>
                            </div>
                        {% endfor %}


                        <button type="submit" class="btn btn-primary">保存</button>
                    </form>
                </div>
            </div>
        </div>
    </div>

{% endblock %}

11.1 user_model_edit.html

html 复制代码
{% extends 'layout.html' %}
{% block title %}
    编辑用户
{% endblock %}

{% block content %}
    <div class="container">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">
                        <span class="glyphicon glyphicon-tag" aria-hidden="true">
                            编辑用户</span>
                    </h3>
                </div>

                <div class="panel panel-body">
                    <form method="post" novalidate>
                        {% csrf_token %}
                        {% for u in user %}
                            <div class="form-group">
                                <label>{{ u.label }}:</label>
                                {{ u }}
                                <span style="color: red">{{ u.errors.0 }}</span>
                            </div>
                        {% endfor %}


                        <button type="submit" class="btn btn-primary">保存</button>
                    </form>
                </div>
            </div>
        </div>
{% endblock %}

11.1 views.py

python 复制代码
from django.shortcuts import render, HttpResponse, redirect

from appTang import models
from appTang.forms import DepartmentForm, UserModelForm
from appTang.models import UserInfo, Department


# 用户列表
def user_list(req):
    """用户展示"""
    # select * from userinfo order by name asc; -name 倒序
    user_list = UserInfo.objects.all().order_by("-name")
    return render(req, 'user/user_list.html', {"user_list": user_list})

def user_modelform_add(req):
    """ modelform 方式添加用户"""
    if req.method == 'GET':
        user = UserModelForm()
        return render(req, 'user/user_model_add.html', {"user": user})
    # 用户post提交,数据校验
    user = UserModelForm(data=req.POST)
    if user.is_valid():
        print(user.changed_data)
        user.save()
        return redirect("/user/list")
    # 校验失败, 在页面上展示错误信息
    return render(req, 'user/user_model_add.html', {"user": user})


def user_modelform_edit(req, nid):
    """ 用户编辑"""
    if req.method == 'GET':
        # 根据id获取要编辑,那行数据
        row_obj = models.UserInfo.objects.filter(id=nid).first()
        user = UserModelForm(instance=row_obj)
        return render(req, "user/user_model_edit.html", {"user": user})
    # 表单提交, 需要先查询到,然后实例化进去
    row_obj = models.UserInfo.objects.filter(id=nid).first()
    user = UserModelForm(data=req.POST, instance=row_obj)
    # 数据校验通过
    if user.is_valid():
        # 默认用户保存,用户输入的所有数据
        # 给数据增加其他的, 需要保存的值, user.instance.字段名 = 值
        user.save()
        return redirect("/user/list")
    # 否则
    return render(req, "user/user_model_edit.html", {"user": user})

11.1 user_list.html

html 复制代码
{% extends 'layout.html' %}

{% block content %}

    <div>
        <div class="container">
            <div style="margin-bottom: 10px">
                <a class="btn btn-success" href="/user/modelform/add">
                    <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
                    ModelForm新建用户</a>
            </div>
            <div class="panel panel-default">
                <div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>用户列表
                </div>

                <table class="table table-bordered">
                    <thead>
                    <tr>
                        <th>#</th>
                        <th>姓名</th>
                        <th>密码</th>
                        <th>年龄</th>
                        <th>部门</th>
                        <th>创建时间</th>
                        <th>性别</th>
                        <th>状态</th>
                        <th>操作</th>
                    </tr>
                    </thead>
                    <tbody>
                    {% for i in user_list %}
                        <tr>
                            <td>{{ i.id }}</td>
                            <td>{{ i.name }}</td>
                            <td>{{ i.password }}</td>
                            <td>{{ i.age }}</td>
                            <td>
                                {% if i.depart %}
                                    {{ i.depart.title }}
                                {% endif %}

                            </td>
                            <td>{{ i.create_time|date:"Y-m-d H:i:s" }}</td>
                            <td>{{ i.get_gender_display }}</td>
                            <td>{{ i.get_status_display }}</td>
                            <td>
                                <a class="btn btn-primary btn-xs" href="/user/modelform/edit/{{ i.id }}">MF编辑</a>
                                <a class="btn btn-danger  btn-xs" href="/user/del?nid={{ i.id }}">删除</a>
                            </td>
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
            </div>

        </div>


    </div>


{% endblock %}

11.1 layout.html

html 复制代码
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}"/>
    <style>
        .navbar {
            border-radius: 0;
        }
    </style>

</head>
<body>

<nav class="navbar navbar-default">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">系统</a>
        </div>


        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li><a href="/user/list">用户管理</a></li>
                <li><a href="/department/list">部门管理</a></li>
            </ul>
            <form class="navbar-form navbar-left">
                <div class="form-group">
                    <input type="text" class="form-control" placeholder="查询">
                </div>
                <button type="submit" class="btn btn-default">查询</button>
            </form>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="#">Link</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">Dropdown <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">Separated link</a></li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</nav>


<div>
    {% block content %}{% endblock %}
</div>

<script src="{ % static 'js/jquery-3.7.1.js %'}"></script>
<script src="{ % static 'plugins/bootstrap-3.4.1/js/bootstrap.js %'}"></script>

</body>
</html>

11.2 forms.py格式校验

python 复制代码
from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from .models import Department, UserInfo

class DepartmentForm(forms.ModelForm):
    class Meta:
        model = Department
        fields = "__all__"

# 添加的 ModelForm
class UserModelForm(forms.ModelForm):
    name = forms.CharField(min_length=2, label="用户名")
    # 定义密码正则表达式和验证器
    password_regex = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$'
    password_validator = RegexValidator( regex=password_regex,
        message="密码必须包含至少一个小写字母、一个大写字母、一个数字和一个特殊字符。")
    password = forms.CharField(label="密码", validators=[password_validator],widget=forms.PasswordInput)

    class Meta:
        model = UserInfo
        # fields = ['name', 'password', 'age', 'gender']
        fields = "__all__"
        # 排除哪个字段
        # exclude = ['status']
        # widgets = {"name": forms.TextInput(attrs={"class": "form-control"}),
        #            "password": forms.PasswordInput(attrs={"class": "form-control"})}

    # 验证方式2 : 定义钩子方法, clean_字段名(self)
    def clean_age(self):

        txt_age = self.cleaned_data["age"]
        if txt_age<=18:
            #验证不通过
            raise ValidationError("未成年不允许!")
        # 验证通过
        return txt_age
    # 不允许重名,去数据库查询,校验!
    def clean_name(self):
        txt_name = self.cleaned_data["name"]
        if models.UserInfo.objects.filter(name=txt_name).exists():
            raise ValidationError("---重名了!---")
        # 验证通过
        return txt_name

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

        for name, field in self.fields.items():
            if name == "status":
                continue
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}
# 编辑的 ModelForm
class UserModelEditForm(forms.ModelForm):
    # 定义name不可编辑 
    name = forms.CharField(disabled=True, label="用户名")
    password_regex = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$'
    password_validator = RegexValidator(regex=password_regex,
        message="密码必须包含至少一个小写字母、一个大写字母、一个数字和一个特殊字符。")
    password = forms.CharField(label="密码",validators=[password_validator],widget=forms.PasswordInput)

    class Meta:
        model = UserInfo
        fields = "__all__"
        # 排除哪个字段
        exclude = ['status']

    def clean_age(self):
        txt_age = self.cleaned_data["age"]
        if txt_age<=18:
            raise ValidationError("未成年不允许!")
        return txt_age

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

        for name, field in self.fields.items():
            if name == "status":
                continue
            if name == "depart":
                # 过滤掉 status=1 的数据
                self.fields['depart'].queryset = Department.objects.exclude(status=1)
                # 设置默认选项的标签为 请选择
                self.fields['depart'].empty_label = "请选择"
            field.widget.attrs = {"class": "form-control", "placeholder": field.label}
python 复制代码
# 添加  - 不允许重名,去数据库查询,校验!
    def clean_name(self):
        txt_name = self.cleaned_data["name"]
        if models.UserInfo.objects.filter(name=txt_name).exists():
            raise ValidationError("---重名了!---")
        # 验证通过
        return txt_name
# 编辑 - 排除自己,校验    
    def clean_name(self):
       # print(self.instance.pk)
        txt_name = self.cleaned_data["name"]
        if models.UserInfo.objects.exclude(id=self.instance.pk).filter(name=txt_name).exists():
            raise ValidationError("---重名了!---")
        # 验证通过
        return txt_name
相关推荐
hanxiuchao1 天前
告别客户端臃肿!网页端 M3U8 播放调试方案,适配全办公场景
运维·python·django·m3u8·m3u8播放
程序员羽痕1 天前
基于深度学习的眼疾识别系统
人工智能·pytorch·深度学习·分类·django
流云鹤2 天前
1. 配置环境、创建导航栏
python·django
流云鹤2 天前
2.登录模块
python·django
流云鹤2 天前
专题栏说明
python·django
XGeFei3 天前
【Django学习笔记】—— Django 高并发通俗讲解
笔记·学习·django
weixin_BYSJ19873 天前
springboot美食食谱小程序---附源码24044
java·spring boot·python·spring cloud·django·tomcat·php
许彰午4 天前
87_Python Django模型与数据库
数据库·python·django
weixin_BYSJ19874 天前
SpringBoot + MySQL 乒乓球运动员信息管理系统项目实战--附源码04954
java·javascript·spring boot·python·django·flask·php
Database_Cool_6 天前
RAG 混合检索首选:阿里云 Lindorm 向量+全文一体化检索
数据库·阿里云·django·云计算