day16 Django开发

主题:员工管理系统

1.新建项目

2.创建app

python manage.py startapp app01


页面执行了“python manage.py”
注册app:

3.设计表结构(django)

from django.db import modelsclass 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="入职时间")# 特定条件1: 增加部门,存在一个特定的维表# 无约束# depart_id = models.BigIntegerField(verbose_name="部门ID")# 1.有约束(校验写入的数据是否OK,例如增加的部门只能在现有部门表中进行插入,如果在部门表中找不到,就报错)#   - 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)# 特定条件2: 某个字段的取值是一个有限的枚举类型。# 在django中做的约束gender_choices = ((1, "男"),(2, "女"),)gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)

4.在MySQL中生成表

  • 工具连接MySQL生成数据库。
create database gx_day16 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
  • 修改配置文件,连接MySQL
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'gx_day16',  # 数据库名字'USER': 'root','PASSWORD': 'xxx','HOST': '127.0.0.1',  # 那台机器安装了MySQL'PORT': 3306,}
}
  • django命令生成数据库表
python manage.py makemigrations
python manage.py migrate

表结构创建成功:
运行,根据上述的models.py成功创建了用户表和部门表

5.静态文件管理

static目录、 templates模板html文件夹

6.部门管理

体验,最原始方法来做。
Django中提供Form和ModelForm组件(方便)

期望后台部门管理的页面:

7.模板的继承

从一个页面跳转到另一个页面的查询逻辑顺序:
原始html文件中的href地址 ->
从urls.py中找到相应的views视图函数 ->
进入views.py中找到相应的函数 ->
进入该函数中的相应html文件,进行相应的页面跳转

from django.contrib import admin
from django.urls import path
from app01 import viewsurlpatterns = [# 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),  # url中增加动态参数path('user/list/', views.user_list),path('user/add/', views.user_add),path('user/model/form/add/', views.user_model_form_add),
]
from django.shortcuts import render, redirect
from app01 import modelsdef depart_list(request):""" 部门列表 """# 去数据库中获取所有的部门列表#  [对象,对象,对象]queryset = models.Department.objects.all()return render(request, 'depart_list.html', {'queryset': queryset})def depart_add(request):""" 添加部门 """if request.method == "GET":return render(request, 'depart_add.html')# 获取用户POST提交过来的数据(title输入为空)title = request.POST.get("title")# 保存到数据库models.Department.objects.create(title=title)# 重定向回部门列表return redirect("/depart/list/")def depart_delete(request):""" 删除部门 """# 获取ID http://127.0.0.1:8000/depart/delete/?nid=1nid = request.GET.get('nid')# 删除models.Department.objects.filter(id=nid).delete()# 重定向回部门列表return redirect("/depart/list/")def depart_edit(request, nid):""" 修改部门 """if request.method == "GET":# 根据nid,获取他的数据 [obj,]row_object = models.Department.objects.filter(id=nid).first()return render(request, 'depart_edit.html', {"row_object": row_object})# 获取用户提交的标题title = request.POST.get("title")# 根据ID找到数据库中的数据并进行更新# models.Department.objects.filter(id=nid).update(title=title,其他=123)models.Department.objects.filter(id=nid).update(title=title)# 重定向回部门列表return redirect("/depart/list/")
  • 部门列表
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="/depart/add/">   <!--1: 跳转页面,进行增加页面元素,采用当前页面打开(如果采用新开页面打开,需要设置相应的参数方法)--><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建部门</a></div><div class="panel panel-default"><!-- Default panel contents --><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>部门列表</div><!-- Table --><table class="table table-bordered"><thead><tr><th>ID</th><th>名称</th><th>操作</th></tr></thead><tbody>{% for obj in queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.title }}</td><td><a class="btn btn-primary btn-xs" href="/depart/{{ obj.id }}/edit/">编辑</a>  <!--2: 编辑当前行的数据--><a class="btn btn-danger btn-xs" href="/depart/delete/?nid={{ obj.id }}">删除</a>  <!--3:删除当前id行的数据 --></td></tr>{% endfor %}</tbody></table></div></div>
{% endblock %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
  • 添加部门
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><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">{% csrf_token %}<div class="form-group"><label>标题</label><input type="text" class="form-control" placeholder="标题" name="title"/></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
  • 编辑部门
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><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">{% csrf_token %}<div class="form-group"><label>标题</label><input type="text" class="form-control" placeholder="标题" name="title"value="{{ row_object.title }}"/></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->

问题描述: 一个后台系统中各个页面有共同的部分内容,同时这个内容是动态变化的,因此不可以在每个页面的html中单独写(如果这样的话,会导致- 如果需要修改,需要将所有相关的页面都进行修改调整,维护难度大),而是选择创建一个模板,将内容写在该模板中,让相关页面来继承即可。
定义模板:layout.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>

** 注:** 当不同页面中有的继承的模板不太完全一样的时候,可以采用自定义的{% block js %}{% endblock %}或者{% block css %}{% endblock %}来实现差异化的定义模板,然后在子页面中引用继承相应的模板内容即可,具体的定义模板和模板继承方法如上如下。
相应页面继承母版的固定格式:

{% extends 'layout.html' %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block css %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><link rel="stylesheet" href="{% static 'pluxxx.css' %}"><style>...</style>
{% endblock %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!--差异化继承不同的模板内容 --><h1>首页</h1>
{% endblock %}{% block js %}<script src="{% static 'js/jqxxxin.js' %}"></script>
{% endblock %}

8.用户管理

insert into app01_userinfo(name,password,age,account,create_time,gender,depart_id) values("韩超","666",23,100.68,"2020-01-11",2,1);
insert into app01_userinfo(name,password,age,account,create_time,gender,depart_id) values("刘东","123",23,100.68,"2010-11-11",1,4);
insert into app01_userinfo(name,password,age,account,create_time,gender,depart_id) values("朱虎飞","999",33,9900.68,"2021-05-11",1,1);
+-------------+---------------+------+-----+---------+----------------+
| Field       | Type          | Null | Key | Default | Extra          |
+-------------+---------------+------+-----+---------+----------------+
| id          | bigint(20)    | NO   | PRI | NULL    | auto_increment |
| name        | varchar(16)   | NO   |     | NULL    |                |
| password    | varchar(64)   | NO   |     | NULL    |                |
| age         | int(11)       | NO   |     | NULL    |                |
| account     | decimal(10,2) | NO   |     | NULL    |                |
| create_time | datetime(6)   | NO   |     | NULL    |                |
| gender      | smallint(6)   | NO   |     | NULL    |                |
| depart_id   | bigint(20)    | NO   | MUL | NULL    |                |
+-------------+---------------+------+-----+---------+----------------+

新建用户:

  • 原始方式理思路:不会采用(本质)【麻烦】
- 用户提交数据没有校验。
- 错误,页面上应该有错误提示。
- 页面上,没一个字段都需要我们重新写一遍。     [OK]
- 关联的数据,手动去获取并展示循环展示在页面。  [OK]
  • Django组件

    • Form组件(小简便)
    • ModelForm组件(最简便)

截止目前的项目代码和框架列表:

"""
Django settings for day16 project.Generated by 'django-admin startproject' using Django 3.2.9.For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""from pathlib import Path# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-hkrj5qe6)4-oe)g&+s-_)90r8$$fk_*a1w33=2wikt4!^4_h6c'# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = TrueALLOWED_HOSTS = []# Application definitionINSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01.apps.App01Config'
]MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]ROOT_URLCONF = 'day16.urls'TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [],'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],},},
]WSGI_APPLICATION = 'day16.wsgi.application'# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases# DATABASES = {#     'default': {#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': BASE_DIR / 'db.sqlite3',
#     }
# }DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'gx_day16',  # 数据库名字'USER': 'root','PASSWORD': 'root123','HOST': '127.0.0.1',  # 那台机器安装了MySQL'PORT': 3306,}
}# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validatorsAUTH_PASSWORD_VALIDATORS = [{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',},{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',},{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'TIME_ZONE = 'UTC'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = True# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/STATIC_URL = '/static/'# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-fieldDEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
from django.db import modelsclass Department(models.Model):""" 部门表 """title = models.CharField(verbose_name='标题', max_length=32)def __str__(self):return self.titleclass 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(verbose_name="部门", 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)
"""day16 URL ConfigurationThe `urlpatterns` list routes URLs to views. For more information please see:https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views1. Add an import:  from my_app import views2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views1. Add an import:  from other_app.views import Home2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf1. Import the include() function: from django.urls import include, path2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from app01 import viewsurlpatterns = [# 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/add/', views.user_add)
]
from django.shortcuts import render, redirect
from app01 import modelsdef depart_list(request):""" 部门列表 """# 去数据库中获取所有的部门列表#  [对象,对象,对象]queryset = models.Department.objects.all()return render(request, 'depart_list.html', {'queryset': queryset})def depart_add(request):""" 添加部门 """if request.method == "GET":return render(request, 'depart_add.html')# 获取用户POST提交过来的数据(title输入为空)title = request.POST.get("title")# 保存到数据库models.Department.objects.create(title=title)# 重定向回部门列表return redirect("/depart/list/")def depart_delete(request):""" 删除部门 """# 获取ID http://127.0.0.1:8000/depart/delete/?nid=1nid = request.GET.get('nid')# 删除models.Department.objects.filter(id=nid).delete()# 重定向回部门列表return redirect("/depart/list/")def depart_edit(request, nid):""" 修改部门 """if request.method == "GET":# 根据nid,获取他的数据 [obj,]row_object = models.Department.objects.filter(id=nid).first()return render(request, 'depart_edit.html', {"row_object": row_object})# 获取用户提交的标题title = request.POST.get("title")# 根据ID找到数据库中的数据并进行更新# models.Department.objects.filter(id=nid).update(title=title,其他=123)models.Department.objects.filter(id=nid).update(title=title)# 重定向回部门列表return redirect("/depart/list/")def user_list(request):""" 用户管理 """# 获取数据库中的所有用户列表 [obj, obj, obj]queryset = models.UserInfo.objects.all()"""# 用Python的语法获取数据;; 我们也可以直接选择在html中进行相应的循环获取数据for obj in queryset:print(obj.id, obj.name, obj.account, obj.create_time.strftime("%Y-%m-%d"), obj.gender, obj.get_gender_display(), obj.depart_id, obj.depart.title)# print(obj.name, obj.depart_id)# obj.depart_id  # 获取数据库中存储的那个字段值# obj.depart.title  # 根据id自动去关联的表(depart)中获取哪一行数据depart对象。"""return render(request, 'user_list.html', {"queryset": queryset})def user_add(request):""" 添加用户(原始方式) """if request.method == "GET": # 获取 get请求 的内容context = {'gender_choices': models.UserInfo.gender_choices,"depart_list": models.Department.objects.all()}return render(request, 'user_add.html', context)# 获取用户提交的数据  # 获取 post请求 的内容user = request.POST.get('user')pwd = request.POST.get('pwd')age = request.POST.get('age')account = request.POST.get('ac')ctime = request.POST.get('ctime')gender = request.POST.get('gd')depart_id = request.POST.get('dp')# 添加到数据库中models.UserInfo.objects.create(name=user, password=pwd, age=age,account=account, create_time=ctime,gender=gender, depart_id=depart_id)# 返回到用户列表页面return redirect("/user/list/")
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"><style>.navbar {border-radius: 0;}</style>
</head>
<body>
<nav class="navbar navbar-default"><div class="container"><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="/depart/list/">部门管理</a></li>  <!--导航菜单中的相应模块中,为相应的a标签增加 跳转的链接 --><li><a href="/user/list/">用户管理</a></li>    <!--导航菜单中的相应模块中,为相应的a标签增加 跳转的链接 --><li><a href="#">Link</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">武沛齐 <span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">个人资料</a></li><li><a href="#">我的信息</a></li><li role="separator" class="divider"></li><li><a href="#">注销</a></li></ul></li></ul></div></div>
</nav><div>{% block content %}{% endblock %}    <!-- 固定搭配: 编写各个页面都通用的模板内容页面,实现: 下游的html可以复用该模板-->
</div><script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
</body>
</html>
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="/depart/add/">   <!--1: 跳转页面,进行增加页面元素,采用当前页面打开(如果采用新开页面打开,需要设置相应的参数方法)--><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建部门</a></div><div class="panel panel-default"><!-- Default panel contents --><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>部门列表</div><!-- Table --><table class="table table-bordered"><thead><tr><th>ID</th><th>名称</th><th>操作</th></tr></thead><tbody>{% for obj in queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.title }}</td><td><a class="btn btn-primary btn-xs" href="/depart/{{ obj.id }}/edit/">编辑</a>  <!--2: 编辑当前行的数据--><a class="btn btn-danger btn-xs" href="/depart/delete/?nid={{ obj.id }}">删除</a>  <!--3:删除当前id行的数据 --></td></tr>{% endfor %}</tbody></table></div></div>
{% endblock %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><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">{% csrf_token %}<div class="form-group"><label>标题</label><input type="text" class="form-control" placeholder="标题" name="title"/></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><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">{% csrf_token %}<div class="form-group"><label>标题</label><input type="text" class="form-control" placeholder="标题" name="title"value="{{ row_object.title }}"/></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="/user/add/"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a><a class="btn btn-success" href="/user/model/form/add/"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户ModelForm</a></div><div class="panel panel-default"><!-- Default panel contents --><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></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 queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.name }}</td><td>{{ obj.password }}</td><td>{{ obj.age }}</td><td>{{ obj.account }}</td><td>{{ obj.create_time|date:"Y-m-d" }}</td> <!--将相应的时间calss转换成字符串,模板语言中的格式化--><td>{{ obj.get_gender_display }}</td>  <!-- 当出现映射mapping的(特定choices)时候,可以根据该函数实现gender的原始值获取; 在模板语言中,不允许加括号--><td>{{ obj.depart.title }}</td>  <!--用该函数获取相应字段title的值,关联其他表获取原始函数--><td><a class="btn btn-primary btn-xs" href="#">编辑</a><a class="btn btn-danger btn-xs" href="#">删除</a></td></tr>{% endfor %}</tbody></table></div></div>
{% endblock %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><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">{% csrf_token %}<div class="form-group"><label>姓名</label><input type="text" class="form-control" placeholder="姓名" name="user" /></div><div class="form-group"><label>密码</label><input type="text" class="form-control" placeholder="密码" name="pwd"/></div><div class="form-group"><label>年龄</label><input type="text" class="form-control" placeholder="年龄" name="age"/></div><div class="form-group"><label>余额</label><input type="text" class="form-control" placeholder="余额" name="ac"/></div><div class="form-group"><label>入职时间</label><input type="text" class="form-control" placeholder="入职时间" name="ctime"/></div><div class="form-group"><label>性别</label><select class="form-control" name="gd"> <!--下拉选择框-->{% for item in gender_choices %}<option value="{{ item.0 }}">{{ item.1 }}</option>{% endfor %}</select></div><div class="form-group"><label>部门</label><select class="form-control" name="dp">  <!--下拉选择框,并关联其他表获取相应的中文部门数据-->{% for item in depart_list %}<option value="{{ item.id }}">{{ item.title }}</option>{% endfor %}</select></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->

8.1 初识Form

1. views.py

class MyForm(Form): # 创建一个form类user = forms.CharField(widget=forms.Input) # 定义相应的字段,前端自动的识别成一个输入框pwd = form.CharFiled(widget=forms.Input)email = form.CharFiled(widget=forms.Input)account = form.CharFiled(widget=forms.Input)create_time = form.CharFiled(widget=forms.Input)depart = form.CharFiled(widget=forms.Input)gender = form.CharFiled(widget=forms.Input)def user_add(request):if request.method == "GET":form = MyForm() # 类的实例化return render(request, 'user_add.html',{"form":form})

2.user_add.html

<form method="post">{% for field in form%}{{ field }} <!--本质上,是用该命令生成相应的html中的input标签,如{{ from.user }},对于类似chioces等字段,会直接展示下拉框,级连其他表时也可以自动识别是否需要下拉框-->{% endfor %}<!-- 不用罗列的写如下html标签<input type="text"  placeholder="姓名" name="user" />,而是直接用上面的循环自动生成html标签 -->
</form>
<form method="post">{{ form.user }}{{ form.pwd }}{{ form.email }}<!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>

8.3 ModelForm(推荐)

0. models.py

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 = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE)gender_choices = ((1, "男"),(2, "女"),)gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)

1. views.py

class MyForm(ModelForm):xx = form.CharField*("...")class Meta:model = models.UserInfofields = ["name","password","age","xx"] ## 在此处写需要展示的字段,字段名和models.py中表字段一一对应即可def user_add(request):if request.method == "GET":form = MyForm()return render(request, 'user_add.html',{"form":form})

2.user_add.html

<form method="post">{% for field in form%}{{ field }}{% endfor %}<!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>
<form method="post">{{ form.user }}{{ form.pwd }}{{ form.email }}<!-- <input type="text"  placeholder="姓名" name="user" /> -->
</form>

截止目前项目的整体代码和框架总结:

"""
Django settings for day16 project.Generated by 'django-admin startproject' using Django 3.2.9.For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""from pathlib import Path# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-hkrj5qe6)4-oe)g&+s-_)90r8$$fk_*a1w33=2wikt4!^4_h6c'# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = TrueALLOWED_HOSTS = []# Application definitionINSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01.apps.App01Config'
]MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]ROOT_URLCONF = 'day16.urls'TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [],'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],},},
]WSGI_APPLICATION = 'day16.wsgi.application'# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases# DATABASES = {#     'default': {#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': BASE_DIR / 'db.sqlite3',
#     }
# }DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'gx_day16',  # 数据库名字'USER': 'root','PASSWORD': 'root123','HOST': '127.0.0.1',  # 那台机器安装了MySQL'PORT': 3306,}
}# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validatorsAUTH_PASSWORD_VALIDATORS = [{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',},{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',},{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'TIME_ZONE = 'UTC'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = True# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/STATIC_URL = '/static/'# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-fieldDEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
from django.db import modelsclass Department(models.Model):""" 部门表 """title = models.CharField(verbose_name='标题', max_length=32)def __str__(self):return self.titleclass 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(verbose_name="部门", 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)
"""day16 URL ConfigurationThe `urlpatterns` list routes URLs to views. For more information please see:https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views1. Add an import:  from my_app import views2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views1. Add an import:  from other_app.views import Home2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf1. Import the include() function: from django.urls import include, path2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from app01 import viewsurlpatterns = [# 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/add/', views.user_add),path('user/model/form/add/', views.user_model_form_add),
]
from django.shortcuts import render, redirect
from app01 import modelsdef depart_list(request):""" 部门列表 """# 去数据库中获取所有的部门列表#  [对象,对象,对象]queryset = models.Department.objects.all()return render(request, 'depart_list.html', {'queryset': queryset})def depart_add(request):""" 添加部门 """if request.method == "GET":return render(request, 'depart_add.html')# 获取用户POST提交过来的数据(title输入为空)title = request.POST.get("title")# 保存到数据库models.Department.objects.create(title=title)# 重定向回部门列表return redirect("/depart/list/")def depart_delete(request):""" 删除部门 """# 获取ID http://127.0.0.1:8000/depart/delete/?nid=1nid = request.GET.get('nid')# 删除models.Department.objects.filter(id=nid).delete()# 重定向回部门列表return redirect("/depart/list/")def depart_edit(request, nid):""" 修改部门 """if request.method == "GET":# 根据nid,获取他的数据 [obj,]row_object = models.Department.objects.filter(id=nid).first()return render(request, 'depart_edit.html', {"row_object": row_object})# 获取用户提交的标题title = request.POST.get("title")# 根据ID找到数据库中的数据并进行更新# models.Department.objects.filter(id=nid).update(title=title,其他=123)models.Department.objects.filter(id=nid).update(title=title)# 重定向回部门列表return redirect("/depart/list/")def user_list(request):""" 用户管理 """# 获取数据库中的所有用户列表 [obj, obj, obj]queryset = models.UserInfo.objects.all()"""# 用Python的语法获取数据;; 我们也可以直接选择在html中进行相应的循环获取数据for obj in queryset:print(obj.id, obj.name, obj.account, obj.create_time.strftime("%Y-%m-%d"), obj.gender, obj.get_gender_display(), obj.depart_id, obj.depart.title)# print(obj.name, obj.depart_id)# obj.depart_id  # 获取数据库中存储的那个字段值# obj.depart.title  # 根据id自动去关联的表(depart)中获取哪一行数据depart对象。"""return render(request, 'user_list.html', {"queryset": queryset})def user_add(request):""" 添加用户(原始方式) """if request.method == "GET": # 获取 get请求 的内容context = {'gender_choices': models.UserInfo.gender_choices,"depart_list": models.Department.objects.all()}return render(request, 'user_add.html', context)# 获取用户提交的数据  # 获取 post请求 的内容user = request.POST.get('user')pwd = request.POST.get('pwd')age = request.POST.get('age')account = request.POST.get('ac')ctime = request.POST.get('ctime')gender = request.POST.get('gd')depart_id = request.POST.get('dp')# 添加到数据库中models.UserInfo.objects.create(name=user, password=pwd, age=age,account=account, create_time=ctime,gender=gender, depart_id=depart_id)# 返回到用户列表页面return redirect("/user/list/")# ################################# ModelForm 示例 #################################
from django import formsclass UserModelForm(forms.ModelForm): # 1:用户信息提交的页面表单name = forms.CharField(min_length=3, label="用户名") # 在此处增加相应的格式定义 - 自定义相应字段的要求,比如姓名的最小长度为3;密码的最小长度和正则表达式规范等class Meta:model = models.UserInfofields = ["name", "password", "age", 'account', 'create_time', "gender", "depart"]# widgets = {#     "name": forms.TextInput(attrs={"class": "form-control"}),#     "password": forms.PasswordInput(attrs={"class": "form-control"}),#     "age": forms.TextInput(attrs={"class": "form-control"}),# }def __init__(self, *args, **kwargs): # 针对类似关联其他表查询页面出现object时,可以采用该方法实现实例获取super().__init__(*args, **kwargs)# 循环找到所有的插件,添加了class="form-control" 为标签增加相应的样式for name, field in self.fields.items():# if name == "password":  # 判断对某个字段不加相应的样式#     continuefield.widget.attrs = {"class": "form-control", "placeholder": field.label} # 批量设置form-control样式和默认底纹值def user_model_form_add(request):  # 2:校验用户提交的信息,并往数据库中添加数据信息""" 添加用户(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():# 如果数据合法,保存到数据库# {'name': '123', 'password': '123', 'age': 11, 'account': Decimal('0'), 'create_time': datetime.datetime(2011, 11, 11, 0, 0, tzinfo=<UTC>), 'gender': 1, 'depart': <Department: IT运维部门>}# print(form.cleaned_data)# models.UserInfo.objects.create(..)form.save()return redirect('/user/list/')# 校验失败(在页面上显示原始填写的信息 + 相应的错误信息)return render(request, 'user_model_form_add.html', {"form": form})
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"><style>.navbar {border-radius: 0;}</style>
</head>
<body>
<nav class="navbar navbar-default"><div class="container"><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="/depart/list/">部门管理</a></li>  <!--导航菜单中的相应模块中,为相应的a标签增加 跳转的链接 --><li><a href="/user/list/">用户管理</a></li>    <!--导航菜单中的相应模块中,为相应的a标签增加 跳转的链接 --><li><a href="#">Link</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">武沛齐 <span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">个人资料</a></li><li><a href="#">我的信息</a></li><li role="separator" class="divider"></li><li><a href="#">注销</a></li></ul></li></ul></div></div>
</nav><div>{% block content %}{% endblock %}    <!-- 固定搭配: 编写各个页面都通用的模板内容页面,实现: 下游的html可以复用该模板-->
</div><script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
</body>
</html>
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="/depart/add/">   <!--1: 跳转页面,进行增加页面元素,采用当前页面打开(如果采用新开页面打开,需要设置相应的参数方法)--><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建部门</a></div><div class="panel panel-default"><!-- Default panel contents --><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>部门列表</div><!-- Table --><table class="table table-bordered"><thead><tr><th>ID</th><th>名称</th><th>操作</th></tr></thead><tbody>{% for obj in queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.title }}</td><td><a class="btn btn-primary btn-xs" href="/depart/{{ obj.id }}/edit/">编辑</a>  <!--2: 编辑当前行的数据--><a class="btn btn-danger btn-xs" href="/depart/delete/?nid={{ obj.id }}">删除</a>  <!--3:删除当前id行的数据 --></td></tr>{% endfor %}</tbody></table></div></div>
{% endblock %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><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">{% csrf_token %}<div class="form-group"><label>标题</label><input type="text" class="form-control" placeholder="标题" name="title"/></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><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">{% csrf_token %}<div class="form-group"><label>标题</label><input type="text" class="form-control" placeholder="标题" name="title"value="{{ row_object.title }}"/></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="/user/add/"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a><a class="btn btn-success" href="/user/model/form/add/"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户ModelForm</a></div><div class="panel panel-default"><!-- Default panel contents --><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></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 queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.name }}</td><td>{{ obj.password }}</td><td>{{ obj.age }}</td><td>{{ obj.account }}</td><td>{{ obj.create_time|date:"Y-m-d" }}</td> <!--将相应的时间calss转换成字符串,模板语言中的格式化--><td>{{ obj.get_gender_display }}</td>  <!-- 当出现映射mapping的(特定choices)时候,可以根据该函数实现gender的原始值获取; 在模板语言中,不允许加括号--><td>{{ obj.depart.title }}</td>  <!--用该函数获取相应字段title的值,关联其他表获取原始函数--><td><a class="btn btn-primary btn-xs" href="#">编辑</a><a class="btn btn-danger btn-xs" href="#">删除</a></td></tr>{% endfor %}</tbody></table></div></div>
{% endblock %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %} <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><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">{% csrf_token %}<div class="form-group"><label>姓名</label><input type="text" class="form-control" placeholder="姓名" name="user" /></div><div class="form-group"><label>密码</label><input type="text" class="form-control" placeholder="密码" name="pwd"/></div><div class="form-group"><label>年龄</label><input type="text" class="form-control" placeholder="年龄" name="age"/></div><div class="form-group"><label>余额</label><input type="text" class="form-control" placeholder="余额" name="ac"/></div><div class="form-group"><label>入职时间</label><input type="text" class="form-control" placeholder="入职时间" name="ctime"/></div><div class="form-group"><label>性别</label><select class="form-control" name="gd"> <!--下拉选择框-->{% for item in gender_choices %}<option value="{{ item.0 }}">{{ item.1 }}</option>{% endfor %}</select></div><div class="form-group"><label>部门</label><select class="form-control" name="dp">  <!--下拉选择框,并关联其他表获取相应的中文部门数据-->{% for item in depart_list %}<option value="{{ item.id }}">{{ item.title }}</option>{% endfor %}</select></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->
{% extends 'layout.html' %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->{% block content %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用--><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> <!-- 相应的错误信息展示,.0实现只展示第一个错误信息--></div>{% endfor %}<button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>{% endblock %}  <!-- 固定搭配使用:继承了上述layout.html中的模板内容,复用-->

day17 Django开发

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

8.4 编辑用户

  • 点击编辑,跳转到编辑页面(将编辑行的ID携带过去)。
  • 编辑页面(默认数据,根据ID获取并设置到页面中)
  • 提交:
    • 错误提示
    • 数据校验
    • 在数据库更新
models.UserInfo.filter(id=4).update(...)

8.5 删除

def pretty_delete(request, nid):models.PrettyNum.objects.filter(id=nid).delete()return redirect('/pretty/list/')

9.靓号管理

9.1 表结构

根据表结构的需求,在models.py中创建类(由类生成数据库中的表)。

class PrettyNum(models.Model):""" 靓号表 """mobile = models.CharField(verbose_name="手机号", max_length=11)# 想要允许为空 null=True, blank=Trueprice = models.IntegerField(verbose_name="价格", default=0)level_choices = ((1, "1级"),(2, "2级"),(3, "3级"),(4, "4级"),)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("111111111",19,1,1);
mysql> select * from app01_prettynum;
+----+-----------+-------+-------+--------+
| id | mobile    | price | level | status |
+----+-----------+-------+-------+--------+
|  1 | 111111111 |    19 |     1 |      1 |
|  2 | 111111111 |    19 |     1 |      1 |
|  3 | 111111111 |    19 |     1 |      1 |
|  4 | 111111111 |    19 |     1 |      1 |
+----+-----------+-------+-------+--------+
4 rows in set (0.01 sec)

9.2 靓号列表

  • URL
  • 函数
    • 获取所有的靓号
    • 结合html+render将靓号罗列出来
id   号码  价格  级别(中文)    状态(中文)

9.3 新建靓号

  • 列表点击跳转:/pretty/add/
  • URL
  • ModelForm类
from django import formsclass PrettyModelForm(forms.ModelForm):...
  • 函数

    • 实例化类的对象
    • 通过render将对象传入到HTML中。
    • 模板的循环展示所有的字段。
  • 点击提交
    • 数据校验
    • 保存到数据库(例如手机号的特定校验)
    • 跳转回靓号列表

9.4 编辑靓号

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

不允许手机号修改可以采用如图的方法;或者直接在fields = [‘mobile’, ‘price’, ‘level’, ‘status’]中去掉mobile。
不允许手机号重复。

  • add添加模块:【手机号正则表达式】【手机号不能存在】
# [obj,obj,obj]
queryset = models.PrettyNum.objects.filter(mobile="1888888888")obj = models.PrettyNum.objects.filter(mobile="1888888888").first()# True/False
exists = models.PrettyNum.objects.filter(mobile="1888888888").exists()
  • edit编辑模块:【手机号正则表达式】【手机号不能存在】
排除自己以外,其他的数据是否手机号是否重复?# id!=2 and mobile='1888888888'
models.PrettyNum.objects.filter(mobile="1888888888").exclude(id=2)

9.5 搜索手机号

models.PrettyNum.objects.filter(mobile="19999999991",id=12)data_dict = {"mobile":"19999999991","id":123}
models.PrettyNum.objects.filter(**data_dict)
models.PrettyNum.objects.filter(id=12)       # 等于12
models.PrettyNum.objects.filter(id__gt=12)   # 大于12
models.PrettyNum.objects.filter(id__gte=12)  # 大于等于12
models.PrettyNum.objects.filter(id__lt=12)   # 小于12
models.PrettyNum.objects.filter(id__lte=12)  # 小于等于12data_dict = {"id__lte":12}  # 小于等于12
models.PrettyNum.objects.filter(**data_dict)
models.PrettyNum.objects.filter(mobile="999")               # 等于
models.PrettyNum.objects.filter(mobile__startswith="1999")  # 筛选出以1999开头
models.PrettyNum.objects.filter(mobile__endswith="999")     # 筛选出以999结尾
models.PrettyNum.objects.filter(mobile__contains="999")     # 筛选出包含999data_dict = {"mobile__contains":"999"}  # 筛选出包含999
models.PrettyNum.objects.filter(**data_dict)

9.6 分页

queryset = models.PrettyNum.objects.all()queryset = models.PrettyNum.objects.filter(id=1)[0:10]# 第1页:前10条
queryset = models.PrettyNum.objects.all()[0:10]# 第2页:第2个10条
queryset = models.PrettyNum.objects.all()[10:20]# 第3页:第3个10条
queryset = models.PrettyNum.objects.all()[20:30]
data = models.PrettyNum.objects.all().count() # 数据表的整体条数
data = models.PrettyNum.objects.filter(id=1).count() # 符合相应条件的数据表条数
  • 分页的逻辑和处理规则
  • 封装分页类
    • 从头到尾开发
    • 写项目用【pagination.py】公共组件。
  • 小Bug,搜索 + 分页情况下: 点击分页时候,需要先保留原来的搜索条件
http://127.0.0.1:8000/pretty/list/?q=888
http://127.0.0.1:8000/pretty/list/?page=1
http://127.0.0.1:8000/pretty/list/?q=888&page=23

"""
自定义的分页组件,以后如果想要使用这个分页组件,你需要做如下几件事:1: 在views.py视图函数中:def pretty_list(request):# 1.根据自己的情况去筛选自己的数据queryset = models.PrettyNum.objects.all()# 2.实例化分页对象page_object = Pagination(request, queryset)context = {"queryset": page_object.page_queryset,  # 分完页的数据"page_string": page_object.html()       # 生成页码}return render(request, 'pretty_list.html', context)2: 在HTML页面中{% for obj in queryset %}{{obj.xx}}{% endfor %}<ul class="pagination">{{ page_string }}</ul>"""from django.utils.safestring import mark_safeclass 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中传递的获取分页的参数,例如:/etty/list/?page=12:param plus: 显示当前页的 前或后几页(页码)例如本文的前后展示5页"""from django.http.request import QueryDictimport copyquery_dict = copy.deepcopy(request.GET)query_dict._mutable = Trueself.query_dict = query_dictself.page_param = page_parampage = request.GET.get(page_param, "1")if page.isdecimal():page = int(page)else:page = 1self.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]  # 页面从start到end的展示total_count = queryset.count()  # 计算出总数据条数,然后获取需要展示的总页码。total_page_count, div = divmod(total_count, page_size)if div:   # 如果div(余数)>0,需要加一total_page_count += 1self.total_page_count = total_page_countself.plus = plusdef html(self):# 该函数的目标:生成相应的页码列表: 计算出,显示当前页的前5页、后5页if self.total_page_count <= 2 * self.plus + 1:# 数据库中的数据比较少,都没有达到11页。start_page = 1 # 第一页end_page = self.total_page_count # 最后一页else:# 数据库中的数据比较多 > 11页。# 当前页<5时(小极值)if self.page <= self.plus:start_page = 1end_page = 2 * self.plus + 1else: # 当前页 > 5(大极值)# 当前页+5 > 总页面if (self.page + self.plus) > self.total_page_count:start_page = self.total_page_count - 2 * self.plusend_page = self.total_page_countelse:start_page = self.page - self.plusend_page = self.page + self.plus# 页码page_str_list = []# 1:首页self.query_dict.setlist(self.page_param, [1])page_str_list.append('<li><a href="?{}">首页</a></li>'.format(self.query_dict.urlencode()))# 2:上一页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)# 3:当前页面for i in range(start_page, end_page + 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)#当前页面的active样式else:ele = '<li><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)page_str_list.append(ele)# 4:下一页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)# 5:尾页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()))# 6:跳转页的实现search_string = """<li><form style="float: left;margin-left: -1px" method="get"><input name="page"style="position: relative;float:left;display: inline-block;width: 80px;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)page_string = mark_safe("".join(page_str_list)) # 用mark_safe包裹下需要展示的分页导航数据return page_string

⚠️: 分页和导航菜单栏,是可以整体拿出来复用的,因此单独写成公共组件,便于页面进行复用。相应具体的使用方法见pagination.py中的使用方法。

  • 第一步:在相应的views.py中导入分页组件,然后根据上图中的方式纳入分页
  • 第二步: 在相应的html文件中底部导入分页数据
<!--以下是实现分页的功能 -->
<div class="clearfix"><ul class="pagination">{{ page_string }}</ul></div>

10.时间插件

<link rel="stylesheet" href="static/plugins/bootstrap-3.4.1/css/bootstrap.css">
<link rel="stylesheet" href="static/plugins/bootstrap-datepicker/css/bootstrap-datepicker.css"><input type="text" id="dt" class="form-control" placeholder="入职日期"><script src="static/js/jquery-3.6.0.min.js"></script>
<script src="static/plugins/bootstrap-3.4.1/js/bootstrap.js"></script>
<script src="static/plugins/bootstrap-datepicker/js/bootstrap-datepicker.js"></script>
<script src="static/plugins/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js"></script><script>$(function () {$('#dt').datepicker({format: 'yyyy-mm-dd',startDate: '0',language: "zh-CN",autoclose: true});})
</script>

11.ModelForm和BootStrap

  • 第一版:ModelForm可以帮助我们生成HTML标签。
class UserModelForm(forms.ModelForm):class Meta:model = models.UserInfofields = ["name", "password",]form = UserModelForm()
"""
例如, form自定义帮助生成的input标签 -input输入框如下:
{{form.name}}
{{form.password}}
"""
  • 第二版:定义插件
class UserModelForm(forms.ModelForm):class Meta:model = models.UserInfofields = ["name", "password",]widgets = {"name": forms.TextInput(attrs={"class": "form-control"}),"password": forms.PasswordInput(attrs={"class": "form-control"}),"age": forms.TextInput(attrs={"class": "form-control"}),}
  • 第三版:重新定义的init方法,批量设置
class UserModelForm(forms.ModelForm):class Meta:model = models.UserInfofields = ["name", "password", "age",]def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)# 循环ModelForm中的所有字段,给每个字段的插件设置(但是全部都设置不太好)for name, field in self.fields.items():field.widget.attrs = {"class": "form-control", "placeholder": field.label}
  • 第四版:自定义类
class BootStrapModelForm(forms.ModelForm):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.labelelse:field.widget.attrs = {"class": "form-control", "placeholder": field.label}

操作

  • 提取公共的类, 例如:bootstrap.py; pagination.py

  • ModelForm拆分出来: 写成from.py

  • 视图函数的归类


  • html - 页面

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"><style>.navbar {border-radius: 0;}</style>{% block css %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-default"><div class="container"><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="/depart/list/">部门管理</a></li><li><a href="/user/list/">用户管理</a></li><li><a href="/pretty/list/">靓号管理</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">武沛齐 <span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">个人资料</a></li><li><a href="#">我的信息</a></li><li role="separator" class="divider"></li><li><a href="#">注销</a></li></ul></li></ul></div></div>
</nav><div>{% block content %}{% endblock %}
</div><!-- -->
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
{% block js %}{% endblock %}
</body>
</html>
{% extends 'layout.html' %}{% block content %}<div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="/depart/add/"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建部门</a></div><div class="panel panel-default"><!-- Default panel contents --><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>部门列表</div><!-- Table --><table class="table table-bordered"><thead><tr><th>ID</th><th>名称</th><th>操作</th></tr></thead><tbody>{% for obj in queryset %}<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></table></div></div>
{% endblock %}
{% 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">{% csrf_token %}<div class="form-group"><label>标题</label><input type="text" class="form-control" placeholder="标题" name="title"/></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %}
{% 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">{% csrf_token %}<div class="form-group"><label>标题</label><input type="text" class="form-control" placeholder="标题" name="title"value="{{ row_object.title }}"/></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %}
{% extends 'layout.html' %}{% block content %}<div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="/user/add/"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a><a class="btn btn-success" href="/user/model/form/add/"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户ModelForm</a></div><div class="panel panel-default"><!-- Default panel contents --><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></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 queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.name }}</td><td>{{ obj.password }}</td><td>{{ obj.age }}</td><td>{{ obj.account }}</td><td>{{ obj.create_time|date:"Y-m-d" }}</td><td>{{ obj.get_gender_display }}</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/{{ obj.id }}/delete/">删除</a></td></tr>{% endfor %}</tbody></table></div><ul class="pagination">{{ page_string }}</ul></div>
{% endblock %}
{% extends 'layout.html' %}
{% load static %}{% block css %}<link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.min.css' %}">
{% endblock %}{% 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">{% csrf_token %}<div class="form-group"><label>姓名</label><input type="text" class="form-control" placeholder="姓名" name="user"/></div><div class="form-group"><label>密码</label><input type="text" class="form-control" placeholder="密码" name="pwd"/></div><div class="form-group"><label>年龄</label><input type="text" class="form-control" placeholder="年龄" name="age"/></div><div class="form-group"><label>余额</label><input type="text" class="form-control" placeholder="余额" name="ac"/></div><div class="form-group"><label>入职时间</label><input id="dt"  type="text" autocomplete="off" class="form-control" placeholder="入职时间" name="ctime"/></div><div class="form-group"><label>性别</label><select class="form-control" name="gd">{% for item in gender_choices %}<option value="{{ item.0 }}">{{ item.1 }}</option>{% endfor %}</select></div><div class="form-group"><label>部门</label><select class="form-control" name="dp">{% for item in depart_list %}<option value="{{ item.id }}">{{ item.title }}</option>{% endfor %}</select></div><button type="submit" class="btn btn-primary">提 交</button></form></div></div></div>
{% endblock %}
<!--如下是标准化的时间选择填写格式,在相应的html标签文件中进行编写 -->
{% block js %}<script src="{% static 'plugins/bootstrap-datepicker/js/bootstrap-datepicker.js' %}"></script><script src="{% static 'plugins/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js' %}"></script><script>$(function () {$('#dt').datepicker({format: 'yyyy-mm-dd',startDate: '0',language: "zh-CN",autoclose: true});})</script>
{% endblock %}
{% 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 %}
{% extends 'layout.html' %}
{% load static %}{% block css %}<link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.min.css' %}">
{% endblock %}{% 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 %}{% block js %}<script src="{% static 'plugins/bootstrap-datepicker/js/bootstrap-datepicker.js' %}"></script><script src="{% static 'plugins/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js' %}"></script><script>$(function () {$('#id_create_time').datepicker({format: 'yyyy-mm-dd',startDate: '0',language: "zh-CN",autoclose: true});})</script>
{% endblock %}
{% extends 'layout.html' %}{% block content %}<div class="container"><div style="margin-bottom: 10px" class="clearfix"><a class="btn btn-success" href="/pretty/add/"><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="Search for..."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><div class="panel panel-default"><!-- Default panel contents --><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>靓号列表</div><!-- Table --><table class="table table-bordered"><thead><tr><th>ID</th><th>号码</th><th>价格</th><th>级别</th><th>状态</th><th>操作</th></tr></thead><tbody>{% for obj in queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.mobile }}</td><td>{{ obj.price }}</td><td>{{ obj.get_level_display }}</td>   <!-- choices中的中文展示 --><td>{{ obj.get_status_display }}</td>  <!-- choices中的中文展示 --><td><a class="btn btn-primary btn-xs" href="/pretty/{{ obj.id }}/edit/">编辑</a><a class="btn btn-danger btn-xs" href="/pretty/{{ obj.id }}/delete/">删除</a></td></tr>{% endfor %}</tbody></table></div><!--以下是实现分页的功能 --><div class="clearfix"><ul class="pagination">{{ page_string }}</ul></div></div>
{% endblock %}
{% 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> <!-- 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 %}
{% 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 %}
  • utils – 抽象出来的复用组件
from django import formsclass BootStrapModelForm(forms.ModelForm):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.labelelse:field.widget.attrs = {"class": "form-control","placeholder": field.label}
from app01 import models
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django import forms
from app01.utils.bootstrap import BootStrapModelFormclass UserModelForm(BootStrapModelForm):name = forms.CharField(min_length=3,label="用户名",widget=forms.TextInput(attrs={"class": "form-control"}))class Meta:model = models.UserInfofields = ["name", "password", "age", 'account', 'create_time', "gender", "depart"]class PrettyModelForm(BootStrapModelForm):# 验证:方式1【字段+正则】mobile = forms.CharField(label="手机号",validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误'), ],)class Meta:model = models.PrettyNum# fields = "__all__"   ## 获取表中所有的字段# exclude = ['level']  ## 获取表中除了level后的所有字段fields = ["mobile", 'price', 'level', 'status']# 验证:方式2【钩子方法】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_mobileclass PrettyEditModelForm(BootStrapModelForm):# mobile = forms.CharField(disabled=True, label="手机号")mobile = forms.CharField(label="手机号",validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误'), ],)class Meta:model = models.PrettyNumfields = ['mobile', 'price', 'level', 'status']# 验证:方式2def clean_mobile(self):# print(self.instance.pk) # 获取 当前编辑的哪一行的IDtxt_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
"""
自定义的分页组件,以后如果想要使用这个分页组件,你需要做如下几件事:1: 在views.py视图函数中:def pretty_list(request):# 1.根据自己的情况去筛选自己的数据queryset = models.PrettyNum.objects.all()# 2.实例化分页对象page_object = Pagination(request, queryset)context = {"queryset": page_object.page_queryset,  # 分完页的数据"page_string": page_object.html()       # 生成页码}return render(request, 'pretty_list.html', context)2: 在HTML页面中{% for obj in queryset %}{{obj.xx}}{% endfor %}<ul class="pagination">{{ page_string }}</ul>"""from django.utils.safestring import mark_safeclass 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中传递的获取分页的参数,例如:/etty/list/?page=12:param plus: 显示当前页的 前或后几页(页码)例如本文的前后展示5页"""from django.http.request import QueryDictimport copyquery_dict = copy.deepcopy(request.GET)query_dict._mutable = Trueself.query_dict = query_dictself.page_param = page_parampage = request.GET.get(page_param, "1")if page.isdecimal():page = int(page)else:page = 1self.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]  # 页面从start到end的展示total_count = queryset.count()  # 计算出总数据条数,然后获取需要展示的总页码。total_page_count, div = divmod(total_count, page_size)if div:   # 如果div(余数)>0,需要加一total_page_count += 1self.total_page_count = total_page_countself.plus = plusdef html(self):# 该函数的目标:生成相应的页码列表: 计算出,显示当前页的前5页、后5页if self.total_page_count <= 2 * self.plus + 1:# 数据库中的数据比较少,都没有达到11页。start_page = 1 # 第一页end_page = self.total_page_count # 最后一页else:# 数据库中的数据比较多 > 11页。# 当前页<5时(小极值)if self.page <= self.plus:start_page = 1end_page = 2 * self.plus + 1else: # 当前页 > 5(大极值)# 当前页+5 > 总页面if (self.page + self.plus) > self.total_page_count:start_page = self.total_page_count - 2 * self.plusend_page = self.total_page_countelse:start_page = self.page - self.plusend_page = self.page + self.plus# 页码page_str_list = []# 1:首页self.query_dict.setlist(self.page_param, [1])page_str_list.append('<li><a href="?{}">首页</a></li>'.format(self.query_dict.urlencode()))# 2:上一页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)# 3:当前页面for i in range(start_page, end_page + 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)#当前页面的active样式else:ele = '<li><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)page_str_list.append(ele)# 4:下一页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)# 5:尾页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()))# 6:跳转页的实现search_string = """<li><form style="float: left;margin-left: -1px" method="get"><input name="page"style="position: relative;float:left;display: inline-block;width: 80px;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)page_string = mark_safe("".join(page_str_list)) # 用mark_safe包裹下需要展示的分页导航数据return page_string
  • views – 相应模块的自定义视图函数 (与页面一一对应)
from django.shortcuts import render, redirect
from app01 import modelsfrom app01.utils.pagination import Pagination
from app01.utils.form import UserModelForm, PrettyModelForm, PrettyEditModelFormdef depart_list(request):""" 部门列表 """# 去数据库中获取所有的部门列表#  [对象,对象,对象]queryset = models.Department.objects.all()return render(request, 'depart_list.html', {'queryset': queryset})def depart_add(request):""" 添加部门 """if request.method == "GET":return render(request, 'depart_add.html')# 获取用户POST提交过来的数据(title输入为空)title = request.POST.get("title")# 保存到数据库models.Department.objects.create(title=title)# 重定向回部门列表return redirect("/depart/list/")def depart_delete(request):""" 删除部门 """# 获取ID http://127.0.0.1:8000/depart/delete/?nid=1nid = request.GET.get('nid')# 删除models.Department.objects.filter(id=nid).delete()# 重定向回部门列表return redirect("/depart/list/")def depart_edit(request, nid):""" 修改部门 """if request.method == "GET":# 根据nid,获取他的数据 [obj,]row_object = models.Department.objects.filter(id=nid).first()return render(request, 'depart_edit.html', {"row_object": row_object})# 获取用户提交的标题title = request.POST.get("title")# 根据ID找到数据库中的数据并进行更新# models.Department.objects.filter(id=nid).update(title=title,其他=123)models.Department.objects.filter(id=nid).update(title=title)# 重定向回部门列表return redirect("/depart/list/")
from django.shortcuts import render, redirect
from app01 import modelsfrom app01.utils.pagination import Pagination
from app01.utils.form import UserModelForm, PrettyModelForm, PrettyEditModelFormdef user_list(request):""" 用户管理 """queryset = models.UserInfo.objects.all()page_object = Pagination(request, queryset, page_size=2)context = {"queryset": page_object.page_queryset,"page_string": page_object.html(),}return render(request, 'user_list.html', context)def user_add(request):""" 添加用户(原始方式) """if request.method == "GET":context = {'gender_choices': models.UserInfo.gender_choices,"depart_list": models.Department.objects.all()}return render(request, 'user_add.html', context)# 获取用户提交的数据user = request.POST.get('user')pwd = request.POST.get('pwd')age = request.POST.get('age')account = request.POST.get('ac')ctime = request.POST.get('ctime')gender = request.POST.get('gd')depart_id = request.POST.get('dp')# 添加到数据库中models.UserInfo.objects.create(name=user, password=pwd, age=age,account=account, create_time=ctime,gender=gender, depart_id=depart_id)# 返回到用户列表页面return redirect("/user/list/")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():# 如果数据合法,保存到数据库# {'name': '123', 'password': '123', 'age': 11, 'account': Decimal('0'), 'create_time': datetime.datetime(2011, 11, 11, 0, 0, tzinfo=<UTC>), 'gender': 1, 'depart': <Department: IT运维部门>}# print(form.cleaned_data)# models.UserInfo.objects.create(..)form.save()return redirect('/user/list/')# 校验失败(在页面上显示错误信息)return render(request, 'user_model_form_add.html', {"form": form})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})def user_delete(request, nid):models.UserInfo.objects.filter(id=nid).delete()return redirect('/user/list/')
from django.shortcuts import render, redirect
from app01 import modelsfrom app01.utils.pagination import Pagination
from app01.utils.form import UserModelForm, PrettyModelForm, PrettyEditModelFormdef pretty_list(request):""" 靓号列表 : 在此处增加了: 搜索功能、分页功能"""data_dict = {}search_data = request.GET.get('q', "") # 默认是所有的,如果传递了搜素参数,则展示相应的搜索结果列表if search_data:# mobile__contains方法判断当前输入手机号是否已经存在。双下划线前是字段,后是函数data_dict["mobile__contains"] = search_dataqueryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")# 以下是分页的功能page_object = Pagination(request, queryset)context = {"search_data": search_data,"queryset": page_object.page_queryset,  # 分完页的数据"page_string": page_object.html()  # 页码}return render(request, 'pretty_list.html', context)def pretty_add(request):""" 添加靓号 """# 第一步: 如果是get请求,直接跳转到相应的add页面,让用户填写if request.method == "GET":form = PrettyModelForm()return render(request, 'pretty_add.html', {"form": form})# 第二步:如果是post请求,首先获取从add页面收集到的信息,然后校验合法性和写入数据库,如合法完成后重定向用户列表form = PrettyModelForm(data=request.POST)if form.is_valid():form.save()return redirect('/pretty/list/')# 第三步:如果不合法,则重定向到add页面重新收集。(将上次填写提交信息设置成默认值,并提示错误信息)return render(request, 'pretty_add.html', {"form": form})def pretty_edit(request, nid):""" 编辑靓号 """# 第一步: 从数据库获取当前行的数据row_object = models.PrettyNum.objects.filter(id=nid).first()# 第二步: 如果get请求, 当用户点击编辑按钮的时候,跳转到用户信息编辑页面,同时显示用户的原始默认值(默认值设置采用instance方法)if request.method == "GET":form = PrettyEditModelForm(instance=row_object)return render(request, 'pretty_edit.html', {"form": form})# 第三步: 当编辑完成(post请求)后,用户下次请求该函数将页面当前记录更新的数据传入,并完成校验和保存到数据库,并且重定向到用户列表页面form = PrettyEditModelForm(data=request.POST, instance=row_object)  #该instance会实现从原始信息修改成新的信息if form.is_valid():form.save()return redirect('/pretty/list/')# 第4四步: 如果数据校验不合法,则重定向到用户编辑页面,重新修改更新数据。return render(request, 'pretty_edit.html', {"form": form})def pretty_delete(request, nid):models.PrettyNum.objects.filter(id=nid).delete()return redirect('/pretty/list/')
  • 其他配置文件
from django.db import modelsclass Department(models.Model):""" 部门表 """title = models.CharField(verbose_name='标题', max_length=32)def __str__(self):return self.titleclass 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="入职时间") # datetime格式create_time = models.DateField(verbose_name="入职时间")  # date格式# 无约束# depart_id = models.BigIntegerField(verbose_name="部门ID")# 1.有约束#   - to,与那张表关联#   - to_field,表中的那一列关联# 2.django自动#   - 写的depart#   - 生成数据列 depart_id# 3.部门表被删除# ### 3.1 级联删除depart = models.ForeignKey(verbose_name="部门", 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)class PrettyNum(models.Model):""" 靓号表 """mobile = models.CharField(verbose_name="手机号", max_length=11)# 想要允许为空 null=True, blank=Trueprice = models.IntegerField(verbose_name="价格", default=0)level_choices = ((1, "1级"),(2, "2级"),(3, "3级"),(4, "4级"),)level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1)status_choices = ((1, "已占用"),(2, "未使用"))status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)
"""day16 URL ConfigurationThe `urlpatterns` list routes URLs to views. For more information please see:https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views1. Add an import:  from my_app import views2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views1. Add an import:  from other_app.views import Home2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf1. Import the include() function: from django.urls import include, path2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import pathfrom app01.views import depart, user, prettyurlpatterns = [# path('admin/', admin.site.urls),# 部门管理path('depart/list/', depart.depart_list),path('depart/add/', depart.depart_add),path('depart/delete/', depart.depart_delete),path('depart/<int:nid>/edit/', depart.depart_edit),# 用户管理path('user/list/', user.user_list),path('user/add/', user.user_add),path('user/model/form/add/', user.user_model_form_add),path('user/<int:nid>/edit/', user.user_edit),path('user/<int:nid>/delete/', user.user_delete),# 靓号管理path('pretty/list/', pretty.pretty_list),path('pretty/add/', pretty.pretty_add),path('pretty/<int:nid>/edit/', pretty.pretty_edit),path('pretty/<int:nid>/delete/', pretty.pretty_delete),]
"""
Django settings for day16 project.Generated by 'django-admin startproject' using Django 3.2.9.For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""from pathlib import Path# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-hkrj5qe6)4-oe)g&+s-_)90r8$$fk_*a1w33=2wikt4!^4_h6c'# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = TrueALLOWED_HOSTS = []# Application definitionINSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01.apps.App01Config'
]MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]ROOT_URLCONF = 'day16.urls'TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [],'APP_DIRS': True,'OPTIONS': {'context_processors': ['django.template.context_processors.debug','django.template.context_processors.request','django.contrib.auth.context_processors.auth','django.contrib.messages.context_processors.messages',],},},
]WSGI_APPLICATION = 'day16.wsgi.application'# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases# DATABASES = {#     'default': {#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': BASE_DIR / 'db.sqlite3',
#     }
# }DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'gx_day16',  # 数据库名字'USER': 'root','PASSWORD': 'root123','HOST': '127.0.0.1',  # 那台机器安装了MySQL'PORT': 3306,}
}# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validatorsAUTH_PASSWORD_VALIDATORS = [{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',},{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',},{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',},
]# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'TIME_ZONE = 'UTC'USE_I18N = TrueUSE_L10N = TrueUSE_TZ = True# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/STATIC_URL = '/static/'# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-fieldDEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

day18 Django开发

12. 管理员操作

在导航菜单栏,新增一个模块 - 管理员,主要实现:

  • 点击进入,管理员列表,并在列表中支持: 新增管理员、编辑管理员信息、删除管理员信息、重置密码等

13. 用户登录

一般而言,http无状态短连接
一次请求和响应之后就会断开连接,即是短连接。
下次双方的请求响应连接,并不会携带上次请求响应的数据,即无状态。

问题: 如果要实现,一定时间内,记住上次/最近曾经连接过的记录和相应的状态,就需要用到cookie和session
什么是cookie和session?

  • 响应体: 本次请求连接需要返回的参数内容,从网站给到浏览器。
  • 响应头: 含cookie(如上述的k1=xxx 用于标记凭证)
    • cookie: 1) 保存在浏览器上的键值对;2) 发送请求时,自动携带到网站,然后用用于双方识别凭证。
    • session: 服务端存储的信息(jango默认将该信息存储在数据库)
http://127.0.0.1:8000/admin/list/
https://127.0.0.1:8000/admin/list/

  • 管理/用户列表进入之前,需要先检查用户是否已登录,如登录则继续往下走;否则,返回登录页面
  • 当用户发来请求时,先获取cookie中随机字符串,拿着随机字符串看session中有没有,如果有就允许直接到页面,否则就返回登录页面让用户登录。

13.1 登录

登录成功后:

  • cookie,随机字符串
  • session,用户信息

需求:在其他需要登录才能访问的页面中,都需要加入如下的鉴权“用户是否登录”,如果登录过则直接跳转,否则跳转到登录页面先进行登录。

def index(request):info = request.session.get("info")if not info:return redirect('/login/')...

因此,需要在18个视图函数前面统一加入判断。

info = request.session.get("info")
if not info:return redirect('/login/')

没有可复用的逻辑,因此在此引用“中间件”的逻辑,如下介绍:

13.2 中间件的体验

  • step1: 定义中间件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponseclass M1(MiddlewareMixin):""" 中间件1 """def process_request(self, request):# 如果方法中没有返回值(返回None),继续向后走# 如果有返回值 HttpResponse、render 、redirectprint("M1.process_request")return HttpResponse("无权访问")def process_response(self, request, response):print("M1.process_response")return responseclass M2(MiddlewareMixin):""" 中间件2 """def process_request(self, request):print("M2.process_request")def process_response(self, request, response):print("M2.process_response")return response
  • step2: 应用中间件 setings.py
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware','app01.middleware.auth.M1','app01.middleware.auth.M2',
]

  • step3: 在中间件的process_request方法
# 如果方法中没有返回值(返回None),继续向后走 (如上黑色框模块)
# 如果有返回值 HttpResponse、render 、redirect,则不再继续向后执行  (如上红色框模块)。

13.3 中间件实现登录校验

  • 编写中间件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirectclass AuthMiddleware(MiddlewareMixin):def process_request(self, request):# 0.排除那些不需要登录就能访问的页面#   request.path_info 获取当前用户请求的URL /login/if request.path_info == "/login/":return# 1.读取当前访问的用户的session信息,如果能读到,说明已登录过,就可以继续向后走。info_dict = request.session.get("info")print(info_dict)if info_dict: # 若非空,继续想后面走(如有其他中间件,则走其他中间件,没有继续向后)return# 2.没有登录过,重新回到登录页面return redirect('/login/')
  • 应用中间件
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware','app01.middleware.auth.AuthMiddleware',   ## 新增的自定义中间件路径
]

13.4 注销

目标: 实现 - 当前登录用户的信息注销,下次进入需要重新登录;即将本次登录的session清空。

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

13.5 当前用户

目标: 实现 - 在页面的右上角,进行用户个人信息的获取并显示。

14.图片验证码

14.1 生成图片

pip install pillow
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilterdef check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28):code = []img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))draw = ImageDraw.Draw(img, mode='RGB')def rndChar():"""生成随机字母:return:"""return chr(random.randint(65, 90))def rndColor():"""生成随机颜色:return:"""return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))# 写文字font = ImageFont.truetype(font_file, font_size)for i in range(char_length):char = rndChar()code.append(char)h = random.randint(0, 4)draw.text([i * width / char_length, h], char, font=font, fill=rndColor())# 写干扰点for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())# 写干扰圆圈for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())x = random.randint(0, width)y = random.randint(0, height)draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())# 画干扰线for i in range(5):x1 = random.randint(0, width)y1 = random.randint(0, height)x2 = random.randint(0, width)y2 = random.randint(0, height)draw.line((x1, y1, x2, y2), fill=rndColor())img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)return img, ''.join(code)if __name__ == '__main__':img, code_str = check_code()print(code_str)with open('code.png', 'wb') as f:img.save(f, format='png')

同时,Monaco.ttf字体文件包需要放到根目录下。

  • 用户登录页面需要包含的内容

    • 填写用户名、密码; 输入图片校验码内容并校验;注销;用户个人信息右上角展示;
    • 在utils中写可复用的 code.py生成图片校验码的组件(含字体包)
    • 在account.py中写入相应的图片校验码产出、存储、消费逻辑;并在登录页面中嵌入; 注销实现
    • login.html中修改相应的验证码模块
    • 修改右上角个人信息展示逻辑,需要修改母版文件 - layout.html

15. Ajax请求

浏览器向网站发送请求时:URL 和 表单的形式提交。

  • GET
  • POST

此类提交存在一个特点:页面刷新。
除此之外,也可以基于Ajax向后台发送请求(偷偷的发送请求)。

  • 依赖jQuery
  • 编写ajax代码
$.ajax({url:"发送的地址",type:"get",data:{n1:123,n2:456},success:function(res){console.log(res);}
})

15.1 GET请求

$.ajax({url: '/task/ajax/',type: "get",data: {n1: 123,n2: 456},success: function (res) {console.log(res);}
})
from django.shortcuts import render, HttpResponsedef task_ajax(request):print(request.GET)return HttpResponse("成功了")

15.2 POST请求

$.ajax({url: '/task/ajax/',type: "post",data: {n1: 123,n2: 456},success: function (res) {console.log(res);}
})
from django.shortcuts import render, HttpResponse
from django.views.decorators.csrf import csrf_exempt # 免除校验 @csrf_exempt
def task_ajax(request):print(request.GET)print(request.POST)return HttpResponse("成功了")

15.3 关闭绑定事件

{% extends 'layout.html' %}{% block content %}<div class="container"><h1>任务管理</h1><h3>示例1</h3><input id="btn1" type="button" class="btn btn-primary" value="点击"/></div>
{% endblock %}{% block js %}<script type="text/javascript">$(function () {// 页面框架加载完成之后代码自动执行bindBtn1Event();})function bindBtn1Event() {$("#btn1").click(function () {$.ajax({url: '/task/ajax/',type: "post",data: {n1: 123,n2: 456},success: function (res) {console.log(res);}})})}</script>
{% endblock %}

15.4 ajax请求的返回值

一般都会返回JSON格式。后端是json格式,前端初始识别成string,需要进行类型转换

{% extends 'layout.html' %}{% block content %}<div class="container"><h1>任务管理</h1><h3>示例1</h3><input id="btn1" type="button" class="btn btn-primary" value="点击"/></div>
{% endblock %}{% block js %}<script type="text/javascript">$(function () {// 页面框架加载完成之后代码自动执行bindBtn1Event();})function bindBtn1Event() {$("#btn1").click(function () {$.ajax({url: '/task/ajax/',type: "post",data: {n1: 123,n2: 456},dataType: "JSON", <!-- 虽然后端传过来的data_dict是json格式,但是前端会识别成string,因此需要进行类型转换 -->success: function (res) {console.log(res);console.log(res.status);console.log(res.data);}})})}</script>
{% endblock %}
import json
from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exemptdef task_list(request):""" 任务列表 """return render(request, "task_list.html")@csrf_exempt
def task_ajax(request):print(request.GET)print(request.POST)data_dict = {"status": True, 'data': [11, 22, 33, 44]}return HttpResponse(json.dumps(data_dict))

ajax请求案例实现,通过task模块借助展示:

from django.db import modelsclass Admin(models.Model):""" 管理员 """username = models.CharField(verbose_name="用户名", max_length=32)password = models.CharField(verbose_name="密码", max_length=64)def __str__(self):return self.usernameclass Department(models.Model):""" 部门表 """title = models.CharField(verbose_name='标题', max_length=32)def __str__(self):return self.titleclass 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="入职时间")create_time = models.DateField(verbose_name="入职时间")# 无约束# depart_id = models.BigIntegerField(verbose_name="部门ID")# 1.有约束#   - to,与那张表关联#   - to_field,表中的那一列关联# 2.django自动#   - 写的depart#   - 生成数据列 depart_id# 3.部门表被删除# ### 3.1 级联删除depart = models.ForeignKey(verbose_name="部门", 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)class PrettyNum(models.Model):""" 靓号表 """mobile = models.CharField(verbose_name="手机号", max_length=11)# 想要允许为空 null=True, blank=Trueprice = models.IntegerField(verbose_name="价格", default=0)level_choices = ((1, "1级"),(2, "2级"),(3, "3级"),(4, "4级"),)level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1)status_choices = ((1, "已占用"),(2, "未使用"))status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)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)
import json
from django import forms
from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt  # 免除校验from app01 import models
from app01.utils.bootstrap import BootStrapModelFormclass TaskModelForm(BootStrapModelForm):class Meta:model = models.Taskfields = "__all__"widgets = {# "detail": forms.Textarea,"detail": forms.TextInput}def task_list(request):""" 任务列表 """form = TaskModelForm()return render(request, "task_list.html", {"form": form})@csrf_exempt
def task_ajax(request):print(request.GET)print(request.POST)data_dict = {"status": True, 'data': [11, 22, 33, 44]}return HttpResponse(json.dumps(data_dict))# return JsonResponse(data_dict)@csrf_exempt
def task_add(request):# {'level': ['1'], 'title': ['sdfsdfsdfsd'], 'detail': ['111'], 'user': ['8']}# print(request.POST)# 1.用户发送过来的数据进行校验(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, ensure_ascii=False))
{% extends 'layout.html' %}{% block content %}<div class="container"><div class="panel panel-default"><div class="panel-heading">表单</div><div class="panel-body"><form id="formAdd">  <!-- ID用于标识,下文中的js中相应的绑定事件--><div class="clearfix">{% for field in form %}<div class="col-xs-6"><div class="form-group" style="position: relative;margin-bottom: 20px;"><label>{{ field.label }}</label>{{ field }}<span class="error-msg" style="color: red;position: absolute;"></span></div></div>{% endfor %}<div class="col-xs-12"><button id="btnAdd" type="button" class="btn btn-primary">提 交</button></div></div></form></div></div><hr/><h1>Ajax学习</h1><h3>示例1</h3><input id="btn1" type="button" class="btn btn-primary" value="点击1"/><h3>示例2</h3><input type="text" id="txtUser" placeholder="姓名"/><input type="text" id="txtAge" placeholder="年龄"/><input id="btn2" type="button" class="btn btn-primary" value="点击2"/><h3>示例3</h3><form id="form3"><input type="text" name="user" placeholder="姓名"/><input type="text" name="age" placeholder="年龄"/><input type="text" name="email" placeholder="邮箱"/><input type="text" name="more" placeholder="介绍"/></form><input id="btn3" type="button" class="btn btn-primary" value="点击3"/></div>
{% endblock %}{% block js %}<script type="text/javascript">$(function () {// 页面框架加载完成之后代码自动执行bindBtn1Event();bindBtn2Event();bindBtn3Event();bindBtnAddEvent();})function bindBtn1Event() {$("#btn1").click(function () {$.ajax({url: '/task/ajax/',type: "post",data: {n1: 123,  <!-- 固定值-->n2: 456},dataType: "JSON",   <!-- 虽然后端传过来的data_dict是json格式,但是前端会识别成string,因此需要进行类型转换 -->success: function (res) {console.log(res);console.log(res.status);console.log(res.data);}})})}function bindBtn2Event() {$("#btn2").click(function () {$.ajax({url: '/task/ajax/',type: "post",data: {name: $("#txtUser").val(),  <!-- 获取用户填写的数据-->age: $("#txtAge").val()},dataType: "JSON",success: function (res) {console.log(res);console.log(res.status);console.log(res.data);}})})}function bindBtn3Event() {$("#btn3").click(function () {$.ajax({url: '/task/ajax/',type: "post",data: $("#form3").serialize(),  <!-- 直接序列化,打包获取用户填写的数据-->dataType: "JSON",success: function (res) {console.log(res);console.log(res.status);console.log(res.data);}})})}function bindBtnAddEvent() {$("#btnAdd").click(function () {$(".error-msg").empty(); <!-- 先清空error的数据信息 -->$.ajax({url: '/task/add/',type: "post",data: $("#formAdd").serialize(),  <!-- #formAdd就是上文中的id,用于相互标识,绑定事件-->dataType: "JSON",success: function (res) {if (res.status) {alert("添加成功");} else {$.each(res.error, function (name, data) {// console.log(name,data);$("#id_" + name).next().text(data[0]);})}}})})}</script>
{% endblock %}

python-web开发[16-18]之Django开发相关推荐

  1. Python Web前后端分离框架Django+Vue搭建

    Python Web前后端分离框架Django+Vue搭建 对前面所学知识的归纳整理,感兴趣的可以看看,欢迎指正. 一.前后端分离框架介绍 本项目基于 Python 的 Web 框架开发,采用前后端分 ...

  2. python产品发布会_阅读虫师django开发发布会系统以及django开发Web接口分享

    最近阅读了虫师的书籍,关于虫师分析django Web开发以及django 开发Web接口,通过阅读虫师的书籍,学到了很多东西,在这里分享一下,和简友一起学习和讨论 1.创建项目django-admi ...

  3. python django web典型模块开发实战下载_Django实战 Python Web典型模块与项目开发

    本书结合样例,介绍 Django 的基础知识.主要模块的开发以及权限管理等高级内容,并且通过图书管理系统.博客系统.车费管理系统 3 个项目的开发实战,使读者既能掌握 Django 的重要开发技术,又 ...

  4. Django Python Web应用程序框架简介

    在这个由四部分组成的系列文章的前三篇文章中,比较了不同的Python Web框架,我们介绍了Pyramid , Flask和Tornado Web框架. 我们已经构建了同一个应用程序3次,最终进入了D ...

  5. 第七课 Python Web企业门户网站-部署

    目录 7.1 Python WSGI部署原理 7.2 本地服务器部署 7.2.1 准备部署环境 7.2.2 安装和配置IIS 7.2.3 开放端口 7.2.4 部署 7.3 云服务器部署 7.3.1 ...

  6. python构建知识库_手把手教导实战Python Web项目

    手把手教导实战Python Web项目 一.前言 Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的框架模式,即模型M,视图V和控制器C.Django的主要目的是简便.快 ...

  7. django的优缺点总结 - Python Web开发面试必备

    Django是利用Python语言从事Web开发的首选框架.如果你以后想从事python web开发工作,就必需了解其优缺点.这些都可能会是你将来的面试题哦. Django的优点 功能完善.要素齐全: ...

  8. python web项目案例教程_Python Web开发案例教程(慕课版)——使用Flask、Tornado、Django...

    第1章 Web开发基础1 1.1 Web概述 2 1.1.1 什么是Web 2 1.1.2 Web应用程序的工作原理 2 1.1.3 Web的发展历程 2 1.2 Web前端开发基础 4 1.2.1 ...

  9. Python Web开发——Django框架学习

    文章目录 Web开发简介 Web应用技术 Web前端开发 Web后端开发 使用 Django 开发后端服务 项目目标 管理员操作 登录界面 客户管理界面 添加客户界面 药品管理界面 添加药品界面 订单 ...

  10. Django企业开发实战 高效Python Web框架指南 笔记 (一)

    Django企业开发实战 高效Python Web框架指南 笔记 (一) 内容: 作者是 the5fire,他的博客地址:https://www.the5fire.com/957.html 2016年 ...

最新文章

  1. 开源神经网络框架Caffe2全介绍
  2. 如何:在OpenText Workflow 6.5模型中保存和读取多行数据
  3. computer science sticker
  4. QT的QStandardItemEditorCreator类的使用
  5. MFC中OnCtlColor的用法(改变控件颜色)
  6. c++语言程序的结构特点,C++的特点(特性)
  7. 互补品的交叉弹性系数_重庆事业单位综合备考:需求价格弹性和需求交叉价格弹性有何区别...
  8. js的client、scroll、offset详解与兼容性
  9. php函数实验,PHP函数
  10. 语C、耽美、盲盒、Lolita……这些95后文化,你看懂了吗?
  11. 2018蓝桥模拟赛(一) 数独(dfs)
  12. 解决apache的the requested operation has failed
  13. 那点房事(难以齿口)
  14. 关于java的文件操作
  15. 10015---MySQL--innodb_flush_log_at_trx_commit参数
  16. 心情随笔(一):五月随笔满满的正能量
  17. java.util包
  18. 中国商用机身组件市场趋势报告、技术动态创新及市场预测
  19. 使用pyecharts遇到的坑(去掉地图标识小红点)!
  20. MySql:事务的ACID特性

热门文章

  1. Recovered from a route's Handler('github.com/kataras/iris/mvc.(*ControllerActivator).handlerOf.func2
  2. Git-删除文件后找回
  3. coursera 助学金申请模版
  4. 怎么把一个PDF文件拆分成多个?
  5. Java学习笔记: HashMap 和 HashSet
  6. 第四十九篇: JAVA加密解密之凯撒加密(Caesar cipher)算法
  7. QGIS插件开发-执法业务计算
  8. Android 修改AlertDialog原生setPositiveButton的字体颜色背景颜色大小边距位置
  9. 计算机网络实验4 — 网线制作
  10. 不拽术语,如何通俗地讲解机器学习?