文章目录
前言
前一篇博客介绍了Django的一些基本操作,本篇博客将结合部门用户管理案例介绍更多的操作,包括表结构的级联、约束,重定向,模板继承,ModelForm各种操作,校验错误提示等等。
一、新建项目
- 新建
- 删除不需要的部分
二、新建app
- 新建:
python manage.py startapp app01
- 如果打开了
Run manage...
窗口,则只需要输入后半部分:startapp app01
- 注册app
三、设计表结构
- 理清关系
- 代码:注意表结构的级联和约束
py
from django.db import models
class Department(models.Model):
""" 部门表 """
title = models.CharField(verbose_name='标题', max_length=32)
class UserInfo(models.Model):
""" 员工表 """
name = models.CharField(verbose_name="姓名", max_length=16)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄")
account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
create_time = models.DateTimeField(verbose_name="入职时间")
# 无约束
# depart_id = models.BigIntegerField(verbose_name="部门ID")
# 1.有约束
# - to,与那张表关联
# - to_field,表中的那一列关联
# 2.django自动
# - 写的depart
# - 生成数据列 depart_id
# 3.部门表被删除
# ### 3.1 级联删除
depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE)
# ### 3.2 置空
# depart = models.ForeignKey(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)
四、新建数据库
- MySQL生成数据库
sql
create database gx_day16 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
- 修改配置文件,连接MySQL
py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'gx_day16', # 数据库名字
'USER': 'root',
'PASSWORD': 'root123',
'HOST': '127.0.0.1', # 那台机器安装了MySQL
'PORT': 3306,
}
}
- django命令生成数据库表
py
python manage.py makemigrations
python manage.py migrate
- 如果打开了
Run manage...
窗口则只需要输入后一部分
- 验证表结构创建成功
五、新建静态文件
- static目录
六、部门管理
1.部门展示
- url
py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('depart/list/', views.depart_list),
]
- view
py
from turtle import title
from django.shortcuts import render, redirect
from qpp01.models import Department,UserInfo
# Create your views here.
def depart_list(request):
"""部门列表"""
depart_list = Department.objects.all()
return render(request, "depart_list.html", {"depart_list": depart_list})
- depart_list.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1/css/bootstrap.css">
<link rel="stylesheet" href="/static/plugins/font-awesome-4.7.0/css/font-awesome.css">
<style>
.navbar {
border-radius: 0;
}
</style>
</head>
<body>
<!-- 导航条, https://v3.bootcss.com/components/#navbar -->
<nav class="navbar navbar-default">
<div class="container">
<!-- 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>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="#">部门管理</a></li>
<li><a href="#">部门管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">注册</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">poker <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><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<!-- 新建区域 -->
<div>
<div class="container">
<div style="margin-bottom: 10px">
<a class="btn btn-primary" href="/depart/add/" target="_blank">新建部门</a>
</div>
<div>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">
<span class="glyphicon glyphicon-th-list" aria-hidden="true" style="margin-right: 5px;"></span>
<span>部门列表</span>
</div>
<!-- Table -->
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for obj in depart_list %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.title }}</td>
<td>
<button type="button" class="btn btn-primary btn-xs">编辑</button>
<button type="button" class="btn btn-danger btn-xs">删除</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="/static/js/jquery-3.6.1.min.js"></script>
<script src="/static/plugins/bootstrap-3.4.1/js/bootstrap.min.js"></script>
</body>
</html>
- 效果
2.部门添加
- url
py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('depart/list/', views.depart_list),
path('depart/add/', views.depart_add),
]
- views
py
def depart_add(request):
"""部门添加"""
if request.method == "GET":
return render(request, "depart_add.html")
# 获取用户提交的部门数据
depart_title = request.POST.get("depart_title")
# 保存到数据库
Department.objects.create(title=depart_title)
return redirect("/depart/list/")
- depart_list.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1/css/bootstrap.css">
<link rel="stylesheet" href="/static/plugins/font-awesome-4.7.0/css/font-awesome.css">
<style>
.navbar {
border-radius: 0;
}
</style>
</head>
<body>
<div>
<!-- 导航条, https://v3.bootcss.com/components/#navbar -->
<nav class="navbar navbar-default">
<div class="container">
<!-- 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>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="#">部门管理</a></li>
<li><a href="#">部门管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">注册</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true" aria-expanded="false">poker <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><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
</div>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">新建部门</h3>
</div>
<div class="panel-body">
<form action="/depart/add/" method="post">
{% csrf_token %}
<div class="form-group">
<label>部门名称</label>
<input type="text" class="form-control" placeholder="部门名称" name="depart_title">
</div>
<button type="submit" class="btn btn-primary">保存</button>
</form>
</div>
</div>
</div>
</body>
</html>
- 效果
3.部门删除
- url
py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('depart/list/', views.depart_list),
path('depart/add/', views.depart_add),
path('depart/delete/', views.depart_delete),
]
- views(注意redirect重定向)
py
def depart_delete(request):
"""部门删除"""
nid = request.GET.get('nid')
Department.objects.filter(id=nid).delete()
# 重定向回部门列表
return redirect("/depart/list/")
- depart_list.html
html
<tbody>
{% for obj in depart_list %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.title }}</td>
<td>
<a class="btn btn-primary btn-xs">编辑</a>
<a class="btn btn-danger btn-xs" href="/depart/delete/?nid={{ obj.id }}">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
4.部门编辑
- url
py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('depart/list/', views.depart_list),
path('depart/add/', views.depart_add),
path('depart/delete/', views.depart_delete),
path('depart/<int:nid>/edit/', views.depart_edit),
]
- views
py
def depart_edit(request, nid):
"""部门编辑"""
if request.method == "GET":
# 根据nid,获取数据
row_object = Department.objects.filter(id=nid).first()
return render(request, 'depart_edit.html', {"row_object": row_object})
# 如果是POST请求,保存修改
depart_title = request.POST.get('depart_title')
Department.objects.filter(id=nid).update(title=depart_title)
# 重定向回部门列表
return redirect('/depart/list/')
- depart_edit.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1/css/bootstrap.css">
<link rel="stylesheet" href="/static/plugins/font-awesome-4.7.0/css/font-awesome.css">
<style>
.navbar {
border-radius: 0;
}
</style>
</head>
<body>
<div>
<!-- 导航条, https://v3.bootcss.com/components/#navbar -->
<nav class="navbar navbar-default">
<div class="container">
<!-- 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>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="#">部门管理</a></li>
<li><a href="#">部门管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">注册</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true" aria-expanded="false">poker <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><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
</div>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">编辑部门</h3>
</div>
<div class="panel-body">
<form action="/depart/{{ row_object.id }}/edit/" method="post">
{% csrf_token %}
<div class="form-group">
<label>部门名称</label>
<input type="text" class="form-control" placeholder="部门名称" name="depart_title" value="{{ row_object.title }}">
</div>
<button type="submit" class="btn btn-primary">保存</button>
</form>
</div>
</div>
</div>
</body>
</html>
- 修改depart_list.html
html
<tbody>
{% for obj in depart_list %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.title }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/depart/{{ obj.id }}/edit/">编辑</a>
<a class="btn btn-danger btn-xs" href="/depart/delete/?nid={{ obj.id }}">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
- 效果
七、模板继承
- 定义目版:
layout.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'plugin...min.css' %}">
{% block css %}{% endblock %}
</head>
<body>
<h1>标题</h1>
<div>
{% block content %}{% endblock %}
</div>
<h1>底部</h1>
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
{% block js %}{% endblock %}
</body>
</html>
- 继承母版
html
{% extends 'layout.html' %}
{% block css %}
<link rel="stylesheet" href="{% static 'pluxxx.css' %}">
<style>
...
</style>
{% endblock %}
{% block content %}
<h1>首页</h1>
{% endblock %}
{% block js %}
<script src="{% static 'js/jqxxxin.js' %}"></script>
{% endblock %}
八、用户管理
Django组件:
Form组件(简便)
ModelForm组件(最简便)
1.辨析三种方法
方法一:原始方法
- url
py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('depart/list/', views.depart_list),
path('depart/add/', views.depart_add),
path('depart/delete/', views.depart_delete),
path('depart/<int:nid>/edit/', views.depart_edit),
path('user/list/', views.user_list),
]
- views
py
def user_list(request):
# 获取所有用户列表
user_data = UserInfo.objects.all()
# 用 python 的语法获取数据
"""
for obj in user_data:
# obj.get_gender_display() 表示匹配 男/女,原始字段名为gender,obj.get_字段名称_display()
# obj.create_time.strftime("%Y-%m-%d") 表示将时间格式转换成固定格式的字符串
# obj.depart.title 表示获取depart_id对应的部门名称,因为我们在models中定义表时与另外一张表设置了级联关系,有外键
print(obj.id, obj.name, obj.password, obj.age, obj.account, obj.get_gender_display(), obj.depart.title, obj.create_time.strftime("%Y-%m-%d"))
"""
return render(request, "user_list.html", {"user_data": user_data})
- user_list.html
- 注意: HTML 中获取数据的方式与 Python 中有些不同,例如:
- HTML中引入函数不能带括号, obj.get_gender_display()
- 日期类型转字符串有Django自己的格式, obj.create_time|date:"Y-m-d"
html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div style="margin-bottom: 10px">
<a class="btn btn-primary" href="/depart/add/" target="_blank">新建用户</a>
</div>
<div>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">
<span class="glyphicon glyphicon-th-list" aria-hidden="true" style="margin-right: 5px;"></span>
<span>用户列表</span>
</div>
<!-- Table -->
<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 obj in user_data %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.name }}</td>
<td>{{ obj.password }}</td>
<td>{{ obj.age }}</td>
<td>{{ obj.get_gender_display }}</td>
<td>{{ obj.account }}</td>
<td>{{ obj.create_time|date:"Y-m-d" }}</td>
<td>{{ obj.depart.title }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/user/{{ obj.id }}/edit/">编辑</a>
<a class="btn btn-danger btn-xs" href="/user/delete/?nid={{ obj.id }}">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
- 不足:
- 数据校验较麻烦
- 页面没有错误提示
- 页面上每一个字段都需要重新写一遍
- 关联的数据,需要手动获取并循环展示在页面
方法二:Form组件(简便)
- url
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(request):
if request.method == "GET":
form = MyForm()
return render(request, "user_add.html", {"form": form})
- user_add.html
html
<form method="post">
{{ form.user }}
{{ form.pwd }}
{{ form.email }}
</form>
或者
<form method="post">
{% for field in form %}
{{ field }}
{% endfor %}
</form>
方法三:ModelForm组件(最简便)
py
from django.db import models
# Create your models here.
class Department(models.Model):
"""部门表"""
title = models.CharField(max_length=32, verbose_name='标题')
class UserInfo(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.DateTimeField(verbose_name="入职时间")
depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE, verbose_name="部门")
gender_choices = (
(1, "男"),
(2, "女"),
)
gender = models.SmallIntegerField(choices=gender_choices,verbose_name="性别")
- views
py
class MyForm(ModelForm):
class Meta:
field = ["name", "password", "age"]
def user_add(request):
if request.method == "GET":
form = MyForm()
return render(request, "user_add.html", {"form": form})
- user_add.html
html
<form method="post">
{{ form.user }}
{{ form.pwd }}
{{ form.email }}
</form>
或者
<form method="post">
{% for field in form %}
{{ field }}
{% endfor %}
</form>
2.用户添加(ModelForm)
- url
py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('depart/list/', views.depart_list),
path('depart/add/', views.depart_add),
path('depart/delete/', views.depart_delete),
path('depart/<int:nid>/edit/', views.depart_edit),
path('user/list/', views.user_list),
path('user/model/form/add/', views.user_model_form_add),
]
- views
py
########################## ModelForm 演示 #############################
from django import forms
class UserModelForm(forms.ModelForm):
### 自定义数据校验
# 例如: 用户名最小三个字符
#name = forms.CharField(min_length=3, label="用户名")
class Meta:
model = UserInfo
fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
# 逐一控制标签的样式
# widgets = {
# "name": forms.TextInput(attrs={"class": "form-control"}),
# "password": forms.PasswordInput(attrs={"class": "form-control"}),
# }
# 这里让日期可以手动点击鼠标选择,所以单独拎出来,加上日期插件
widgets = {
"create_time": forms.DateTimeInput(attrs={'class': 'form-control', 'id': 'myDate'}),
}
# 循环找到所有的插件,添加 "class": "form-control"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
# 可以排除指定的字段
if name == "create_time":
continue
print(name, field)
field.widget.attrs = {"class": "form-control"}
def user_model_form_add(request):
"""添加用户(ModelForm版本)"""
if request.method == "GET":
form = UserModelForm()
return render(request, "user_model_form_add.html", {"form": form})
# 用户POST请求提交数据,需要进行数据校验
form = UserModelForm(data=request.POST)
if form.is_valid():
print(form.cleaned_data)
# 直接保存至数据库
form.save()
return redirect("/user/list/")
# 校验失败(在页面上显示错误信息)
return render(request, "user_model_form_add.html", {"form": form})
- user_model_form_add.html
html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">添加用户</h3>
</div>
<div class="panel-body">
<form action="/user/model/form/add/" method="post" novalidate>
{% 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 %}
<button type="submit" class="btn btn-primary">保存</button>
</form>
</div>
</div>
</div>
{% endblock %}
</body>
</html>
- 修改models.py:目的是让自动生成的部门字段不显示"对象"本身,显示对象对应的"title"
py
class Department(models.Model):
"""部门表"""
title = models.CharField(max_length=32, verbose_name='标题')
def __str__(self):
return self.title
3.数据校验错误提示
-
user_model_form_add.html加入{{ field.errors.0 }}字段
-
修改settings.py:改为中文,目的是为了让页面提示错误信息时显示中文,否则会显示英文
py
#LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
- 效果
3.编辑用户
- url
py
path('user/<int:nid>/edit/', views.user_edit),
- views
py
def user_edit(request, nid):
"""编辑用户"""
row_obj = UserInfo.objects.filter(id=nid).first()
# GET请求
if request.method == "GET":
form = UserModelForm(instance=row_obj)
return render(request, "user_edit.html", {"form": form})
# POST请求
form = UserModelForm(data=request.POST, instance=row_obj)
if form.is_valid():
form.save()
return redirect("/user/list/")
return render(request, "user_edit.html", {"form": form})
- user_edit.html
html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">编辑用户</h3>
</div>
<div class="panel-body">
<form method="post" novalidate>
{% 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 %}
<button type="submit" class="btn btn-primary">保存</button>
</form>
</div>
</div>
</div>
{% endblock %}
</body>
</html>
-
只显示年月日就可以了,不应该显示时分秒,修改数据库models
-
效果:点击编辑
4.删除用户
- url
py
path('user/<int:nid>/delete/', views.user_delete),
- views
py
def user_delete(request, nid):
"""用户删除"""
UserInfo.objects.filter(id=nid).delete()
return redirect("/user/list/")
- user_list.html
py
<td>
<a class="btn btn-primary btn-xs" href="/user/{{ obj.id }}/edit/">编辑</a>
<a class="btn btn-danger btn-xs" href="/user/{{ obj.id }}/delete/">删除</a>
</td>