ORM操作数据库--增删改查、表关系
pycharm链接数据库(MySQL)
这个时候pycharm就是数据库的客户端了,类似于Navicat。
链接MySQL的一些操作
1.pycharm中数据库位置
方式1:右上角的Database
方式2:左下角
2.选择MySQL
3.连接MySQL
第一次链接要下载驱动
4.点击下选框tables是查看表结构
5.双击表名是查看表数据
6.提交数据
Django链接数据库(MySQL)(掌握)
settings的设置
django默认自带的有一个小型数据库,sqlite3。
功能比较少,主要用于本地测试。我们实际项目中都会替换掉它。
# sqlite3配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
链接MySQL数据库
修改配置文件,把Django中的default注释掉,增加下面的代码。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db7', # 链接的数据库名称,一定要提前建好
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD': '1113',
'CHARSET': 'utf8',
}
}
以上配置改完之后,框架启动不起来了。是因为djang框架底层链接MySQL用的模块是MySQLdb模块,这个模块的兼容性很差,所以,我们不用。需要我们人为的把MySQLdb模块改为pymysql模块。
修改pymysql模块
如果没有安装pymysql模块,需要先下载pymysql模块。
在Django框架的任意__init__.py文件中加入一下两句话:
import pymysql
pymysql.install_as_MySQLdb()
#这句话的意思其实就是猴子补丁
如果使用pymysql模块的话,每次都要加上面两句话,也很不方便。也可以安装mysqlclient模块(后期学习),这个模块的兼容性非常强,就不用在加任何东西。但是这个模块有个缺点:一般情况下,安装不上。
Django的ORM操作
ORM: 对象关系映射。
- 作用:我们以后再操作数据库的时候,就不用在写原生sql语句,用面向对象的代码去写,然后它给你翻译成原生sql语句。
- 缺点:封装程度太高,ORM的sql执行效率没有原生sql执行的效率高,这个效率的影响可以暂时忽略。
关系映射:
类 >>>: 表
对象 >>>: 一条条数据
属性 >>>: 字段
创建表
models.py
ORM的相关代码是在models.py文件中书写
# 创建一张表出来
from django.db import models
# Create your models here.
# 每一个类都必须继承models.Model类
class User(models.Model): # 表名: User
# 创建字段---> 就是属性
# id int primary key auto_increment
id = models.AutoField(primary_key=True) # 字段名 = 字段类型 + 约束条件
# username varchar(32)
'''max_length参数必填'''
username = models.CharField(max_length=32)
# password varchar(32)
password = models.CharField(max_length=64)
迁移命令
代码写完之后,需要执行两个命令才能把表创建出来
python36 manage.py makemigrations
python36 manage.py migrate
# 第一句话是记录迁移过程的所有事情
# 第二句话才是真正的创建表
'''上面两句话缺一不可,都要执行!!!'''
'''只要修改了跟数据库相关的代码,都要执行上面两句话'''
命令的结果是:
E:\python project\day50D>python36 manage.py makemigrati
ons
Migrations for 'app01':
app01\migrations\0001_initial.py
- Create model User
E:\python project\day50D>python36 manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying app01.0001_initial... OK
Applying app01.0002_auto_20230422_1131... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying sessions.0001_initial... OK
执行了上面两句话后,会创建一堆表,只有一张表是新建的表,剩下的表都是迁移过程的记录表格。
django_migrations表是执行第一句话产生的表,是迁移记录表。
不想要迁移这么多张表格,可以在配置文件中把应用名注释掉。注释掉那个,就不会生成对应记录的表。
INSTALLED_APPS = [
# 'django.contrib.admin',
# 'django.contrib.auth',
# 'django.contrib.contenttypes',
# 'django.contrib.sessions',
# 'django.contrib.messages',
# 'django.contrib.staticfiles',
'app01.apps.App01Config',
]
表名以后都是应用名_表名,可以去掉前缀应用名(后面学习)
主键字段可以不用创建
由于每张表都应该有一个主键字段,并且主键字段名都叫id,如果满足这两个条件,那么,可以省略不写,Django自动帮你创建主键字段出来。
如果是想要创建一个uid(其他名字的主键字段)主键,就要自己写
# 创建作者表
class Author(models.Model):
# username varchar(32)
username = models.CharField(max_length=32) # 字段名 = 字段类型 + 约束条件
# password varchar(32)
password = models.CharField(max_length=32)
执行迁移命令的结果:
E:\python project\day50D>python36 manage.py makemigrati
ons
Migrations for 'app01':
app01\migrations\0003_author.py
- Create model Author
E:\python project\day50D>python36 manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttype
s, sessions
Running migrations:
Applying app01.0003_author... OK
迁移表只会在应用第一次创建表时增加,之后再建的表,不会再创建一堆迁移表了。只有自己的表了。
orm针对字段的增删改查
增加
方式一:增加age,执行命令后,在控制台设置默认值
方式二:新增字段age,直接设置默认值
给作者表增加age,直接在models.py的Author类中写age,只要修改了跟数据库相关的代码,都要执行两句话。
class Author(models.Model):
# username varchar(32)
username = models.CharField(max_length=32)
# password varchar(32)
password = models.CharField(max_length=32)
# 新增加字段age
# age int
age = models.IntegerField()
# Django中只提供了int整型,想要使用smallint,bigint目前不能做,----> 需要后面我们自定义方法。在方法中用smallint,bigint。
执行两句话产生的结果:
E:\python project\day50D>python36 manage.py makemigrations
Migrations for 'app01':
You are trying to add a non-nullable field 'age' to author without a default;(你正试图添加一个没有默认值的非空字段'年龄'到作者表中)
we can't do that (the databapulate existing rows).
se needs something to populate existing rows). (我们不能这样做(数据库的现有行需要一些东西来填充现有行))
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column) # (现在提供一次性默认值(将在所有现有行上设置此列的空值))
2) Quit, and let me add a default in models.py # 2是退出,在models.py中添加一个默认值
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> 24
Migrations for 'app01':
app01\migrations\0004_author_age.py
- Add field age to author
E:\python project\day50D>python36 manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contentty
pes, sessions
Running migrations:
Applying app01.0004_author_age... OK
方式二:新增字段时,设置默认值
# 创建字段时,直接给默认值
book = models.CharField(max_length=32, default='书书书')
修改字段
直接在字段上面修改,执行两句话
删除字段
直接注释,然后执行两句话。所以,以后在models.py文件中,不要轻易注释
ORM针对数据的增删改查
models.UserInfo.objects.create() # insert into
models.UserInfo.objects.filter() # where
models.UserInfo.objects.filter().update() # update
models.UserInfo.objects.filter().delete() # delete from
补充知识点:模板变量的分配
render中的第三个参数:
方式一:放一个字典数据,{'自己定义的名字':'视图函数中的变量名'}
方式二:locals()
具体使用:
views.py:
def ab_render(request):
'''模板变量展示'''
# 分配变量到模板中第一种方式:
user_dict = {'username': 'kevin', 'password': '123'}
user_dict1 = {'aaa': 111}
# return render(request, 'ab_render.html', {'user_dict': user_dict, 'user_dict1': user_dict1})
# 第二种方式:
# 会把该函数局部里面的所有变量都分配到模板文件中
print(locals())
# {'user_dict1': {'aaa': 111}, 'user_dict': {'username': 'kevin', 'password': '123'}, 'request': <WSGIRequest: GET '/ab_render/'>}
return render(request, 'ab_render.html', locals())
ab_render.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
{{ user_dict }}
{{ user_dict.username }}
{{ user_dict.password }}
<div>
{{ user_dict1 }}
</div>
</body>
</html>
用户数据的增加
# 方式一:对象.create()方法
models.Userinfo.objects.create(username=name, password=pwd)
# 方式二:对象.save()
obj = models.Userinfo(username=name, password=pwd)
obj.save()
根据用户注册的功能来展示ORM中对数据的增删改查操作
增加数据步骤:
-
写对应关系和视图函数框架
-
先向用户展示一个注册页面
-
Django链接MySQL,配置文件修改
-
model.py文件中建表结构
-
接收前端传来的数据
-
添加到表中
-
数据展示给客户,新页面,重定向redirect
models.py创建表结构
from django.db import models
# Create your models here.
class Userinfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
is_delete = models.IntegerField(default=0)
views.py文件:
from app01 import models
def register(request):
if request.method == 'POST':
# 1.获取前端表单中提交过来的数据
name = request.POST.get('username')
pwd = request.POST.get('password')
# 2.把数据插入到表中
# 第一种方式:对象.create()方法
res = models.Userinfo.objects.create(username=name, password=pwd)
# # res:返回的是当前插入的对象
# print(res) # Userinfo object
# print(res.username) # 字段=对象.属性
# print(res.password)
# 第二种方式:对象.save()
# obj = models.Userinfo(username=name, password=pwd)
# print(obj) # Userinfo object
# obj.save()
# 3.重定向到数据展示功能
return redirect('/userlist/')
return render(request, 'register.html')
register.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{# <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">#}
{# <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>#}
{# <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>#}
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h1 class="text-center">注册页面</h1>
<div class="container">
<div class="row">
<form action="" method="post">
<div class="form-group">
用户名: <input type="text" class="form-control" name="username">
</div>
<div class="form-group">
密码: <input type="password" class="form-control" name="password">
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary btn-block">
</div>
</form>
</div>
</div>
</body>
</html>
用户数据的查询
user_data = models.Userinfo.objects.filter()
print(user_data)
具体使用:
views.py
# 展示数据
def user_list(request):
'''展示用户数据'''
# select * from userinfo
user_data = models.Userinfo.objects.filter(is_delete=False) # 如果用软删除查询要先过滤筛选
# user_data = models.Userinfo.objects.filter()
# user_data = models.Userinfo.objects.filter().all() # 加不加all()都是取所有数据
# 列表套对象的形式
# <QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>
print(user_data)
print(user_data[0]) # Userinfo object
return render(request, 'userlist.html', locals())
userlist.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户展示数据</title>
{# <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">#}
{# <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>#}
{# <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>#}
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<div class="container">
<div class="row">
<h1 class="text-center">用户列表</h1>
<div class="col-md-8 col-offset-2">
<table class="table table-bordered table-hover">
<thead>
<tr>
<td>ID</td>
<td>用户名</td>
<td>密码</td>
<td>操作按钮</td>
</tr>
</thead>
<tbody>
{% for user_obj in user_data %}
<tr>
<td>{{ user_obj.id }}</td>
<td>{{ user_obj.username }}</td>
<td>{{ user_obj.password }}</td>
<td>
<a href="/edit/?edit_id={{ user_obj.id }}" class="btn btn-primary">修改</a>
<a href="/del_user/?del_id={{ user_obj.pk }}" class="btn btn-danger delBtn">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<script>
{#给删除添加事件#}
$('delBtn').click(function() {
let res = confilm('你确定要删除吗?')
if (res) {
}else{
{#阻止后续事件执行#}
return false
}
})
</script>
</body>
</html>
用户数据的修改
步骤:
- 点击修改按钮,应该跳转一个修改页面,把id值也传输到网址后面 问号传值
<a href="/edit/?edit_id={{ user_obj.id }}" class="btn btn-primary">修改</a>
- 后端接收前端GET请求问号后面传来的edit_id值
request.GET.get('edit_id')
-
然后拿着这个id值当成查询条件去数据库中查询,得到这条记录的对象
-
页面中应该展示出你修改的这条记录的原来的值
render(request, 'edit.html', locals())
- 把查询出来的数据对象,分配到模板文件中,展示出来
设置前端value="{{ edit_data.username }}"
-
用户修改数据,提交(POST提交方式),后端接收数据
-
用Django中的对象(字段)点方法修改数据库中的数据
方式一:
models.Userinfo.objects.filter(pk=edit_id).update(username=name, password=pwd)
方式二:
对象.属性 = 新值
edit_data.save()
- 重定向到数据展示页面
具体使用:
views.py
def edit(request):
'''修改用户的功能'''
# 1.接收前端提交过来的id
edit_id = request.GET.get('edit_id')
# 2.查询想要修改的数据对象,展示给用户一个默认值
# edit_data = models.Userinfo.objects.filter(id=edit_id).first()
# 如果查询的条件的是主键,就可以直接写成pk
edit_data = models.Userinfo.objects.filter(pk=edit_id).first()
# print(edit_data) # Userinfo object
if request.method == 'POST':
# 3.接收前端提交过来的数据
name = request.POST.get('username')
pwd = request.POST.get('password')
# 4.修改数据
# 第一种方式
# update userinfo set username=username, password=password where id = edit_id;
models.Userinfo.objects.filter(pk=edit_id).update(username=name, password=pwd)
print(name)
print(name)
print(name)
# 第二种方式:对象.save()方法
# 对象.属性 就是记录.字段
# edit_data.username = name
# edit_data.password = pwd
# edit_data.save()
return redirect('/userlist/')
return render(request, 'edit.html', locals())
edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>修改用户的页面</title>
{# <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">#}
{# <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>#}
{# <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>#}
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h1 class="text-center">修改用户的页面</h1>
<div class="container">
<div class="row">
<form action="" method="post">
<div class="form-group">
用户名: <input type="text" class="form-control" name="username" value="{{ edit_data.username }}">
</div>
<div class="form-group">
密码: <input type="password" class="form-control" name="password" value="{{ edit_data.password }}">
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary btn-block">
</div>
</form>
</div>
</div>
</body>
</html>
</body>
</html>
用户数据的删除
物理删除
步骤:
- 点击删除按钮,应该跳转一个删除页面,把id值也传输到网址后面,问号传值
<a href="/del_user/?del_id={{ user_obj.pk }}" class="btn btn-danger">删除</a>
- 后端接收前端GET请求问号后面传来删除的del_id值
request.GET.get('del_id')
- 删除数据
models.Userinfo.objects.filter(pk=del_id).delete()
- 重定向到数据展示页面
删除二次确认:
可以用事件绑定来确认
1.给删除的a标签加一个类delBtn
2.绑定点击事件
3.判断confilm确认框,用户选择确定还是取消
4.if判断一下,点击取消,直接return阻止后续事件
软删除
步骤:
- 点击删除按钮,应该跳转一个删除页面,把id值也传输到网址后面,问号传值
<a href="/del_user/?del_id={{ user_obj.pk }}" class="btn btn-danger">删除</a>
- 后端接收前端GET请求问号后面传来删除的del_id值
request.GET.get('del_id')
- 数据要新增一列is_delete
# models.py中添加:
is_delete = models.IntegerField(default=0)
-
更新字段的值,把is_delete的原来的0改为1,用True表示
-
此时,查询数据就不能用所有了。
软删除,可以防止误删操作
具体使用:
def del_user(request):
'''删除用户的功能'''
# 接收前端提交过来的删除数据的id
del_id = request.GET.get('del_id')
# 删除方式一:
# 物理删除,直接从磁盘中把数据删掉了
# models.Userinfo.objects.filter(pk=del_id).delete()
# 删除方式二:
# 软删除
# 更新字段的值,把is_delete的原来的0改为1
models.Userinfo.objects.filter(pk=del_id).update(is_delete=True)
return redirect('/userlist/')
ORM创建表关系
表关系有:
- 一对一
- 一对多
- 多对多
我们以图书表、出版社表、作者表、作者详情表为例。
-
作者与作者详情表是一对一的关系
外键字段建在任何一方都可以,一般建在查询频率较高的表。 -
图书和作者是多对多的关系
外键字段建在第三张表,使用虚拟字段建立第三张表,虚拟字段建在任何一张表中都是可以的,建在查询频率较高的一张表。 -
图书和出版社是一对多的关系
外键字段建在多的一方,书是多,外键字段建在book表中。
用ORM创建以上三种关系:
创建外键关系的时候,先创建一张表中的基础字段,再创建外键字段
model.py代码:
# 图书表
class Book(models.Model):
title = models.CharField(max_length=64)
"""
max_digits=None, 总位数
decimal_places=None 小数后面的位数
"""
# price decimal(8,2)
price = models.DecimalField(max_digits=8, decimal_places=2)
# 如何创建一对多的关系
# publish_id = models.ForeignKey(to='Publish', to_field='id') # to_field='id'可以省略
publish = models.ForeignKey(to='Publish') # 默认是与Publish表的主键建立关系
'''如果是外键字段的话,会自动帮你拼接_id,如果你自己写了,它也会给你拼上,所以以后就不需要自己写_id了'''
# 如何创建多对多的关系
authors = models.ManyToManyField(to='Author')
'''
authors字段是一个虚拟字段,意思是不会在book表中创建出来authors字段,它就是用来告诉django给我创建出来第三张表
'''
# 出版社表
class Publish(models.Model):
title = models.CharField(max_length=64)
addr = models.CharField(max_length=255)
# 作者表
class Author(models.Model):
name = models.CharField(max_length=64)
# 创建一对一关系
# author_detail_id = models.OneToOneField(to='AuthorDetail', to_field='id')
# author_detail_id = models.OneToOneField(to=AuthorDetail) # 如果作者详情表的代码在上面可以不加引号,Django也能找见,但是建议加上
'''如果是外键字段的话,会自动帮你拼接_id,如果你自己写了,它也会给你拼上'''
author_detail = models.OneToOneField(to='AuthorDetail')
'''以后都写在引号里面,防止找不到'''
# 作者详情表
class AuthorDetail(models.Model):
phone = models.CharField(max_length=64)
qq = models.CharField(max_length=64)
wechat = models.CharField(max_length=64)
Django2中
'''在django1中,默认是级联删除级联更新'''
'''在django2中,现在的创建代码就不行了,必须指定一个参数on_delete'''
# 如何创建一对多的关系
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) # 指定级联更新,级联删除
# 如何创建多对多的关系
authors = models.ManyToManyField(to='Author') # 这个不需要
# 创建一对一关系
author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE) # 指定级联更新,级联删除
on_delete参数
on_delete参数
当删除关联表中的数据时,当前表与其关联的行的行为。
可选值:
models.CASCADE
删除关联数据,与之关联也删除
models.DO_NOTHING
删除关联数据,引发错误IntegrityError
models.PROTECT
删除关联数据,引发错误ProtectedError
models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
models.SET
删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)