Django数据库
MySQL驱动程序安装
使用 Django 来操作 MySQL ,实际上底层还是通过 Python 来操作的。因此想要用 Django 来操作 MySQL ,首先还是需要安装一个驱动程序。
在 Python3 中,驱动程序有多种选择。比如有 pymysql 以及 mysqlclient 等。这里我们就使用 mysqlclient 来操作。
mysqlclient 安装非常简单
pip install mysqlclient
配置连接数据库
在操作数据库之前,首先要连接数据库,这里以配置 MySQL 为例。
Django 连接数据库,不需要单独的创建一个连接对象。只需要在 settings.py
文件中找到DATABASES
字段做好数据库相关的配置就可以了。
DATABASES = {
"default": {
# 数据库引擎(是mysql还是oracle等)
"ENGINE": "django.db.backends.mysql",
# 数据库的名字
"NAME": "database_django",
# 连接mysql数据库的用户名
"USER": "root",
# 连接mysql数据库的密码
"PASSWORD": "root",
# mysql数据库的主机地址
"HOST": "127.0.0.1",
# mysql数据库的端口号
"PORT": "3306",
}
}
其中 engine
的选择还有以下:
'django.db.backends.postgresql'
'django.db.backends.mysql'
'django.db.backends.sqlite3'
'django.db.backends.oracle'
在Django中操作数据库
在 Django 中操作数据库有两种方式。第一种方式就是使用原生 sql 语句操作,第二种就是使用 ORM 模型来操作。
本笔记记录使用ORM 模型来操作,这里稍微记录一下原生 sql 语句操作
在 Django 中使用原生 sql 语句操作其实就是使用 python db api 的接口来操作。如果你的 mysql 驱动使用的是 pymysql ,那么你就是使用 pymysql 来操作的,只不过 Django 将数据库连接的这一部分封装好了,我们只要在 settings.py 中配置好了数据库连接信息后直接使用 Django 封装好的接口就可以操作了。
# 使用django封装好的connection对象,会自动读取settings.py中数据库的配置信息
from django.db import connection
# 获取游标对象
cursor = connection.cursor()
# 拿到游标对象后执行sql语句
cursor.execute("select * from book")
# 获取所有的数据
rows = cursor.fetchall()
# 遍历查询到的数据
for row in rows:
print(row)
以上的 execute 以及 fetchall 方法都是 Python DB API 规范中定义好的。
ORM模型介绍
ORM
,全称 Object Relational Mapping ,中文叫做对象关系映射,通过 ORM 我们可以通过类的方式去操作数据库,而不用再写原生的SQL语句。通过把表映射成类,把行作实例,把字段作为属性, ORM
在执行对象操作的时候最终还是会把对应的操作转换为数据库原生语句。
创建ORM模型
ORM
模型一般都是放在 app
的 models.py
文件中。每个 app
都可以拥有自己的模型。
并且如果这个模型想要映射到数据库中,那么这个 app
必须要放在 settings.py
的 INSTALLED_APP
中进行安装。
以下是写一个简单的书籍 ORM 模型。
# 从Django的 django.db 模块中导入了 models 模块
# models 模块包含了用于定义各种数据库字段类型和模型的类
from django.db import models
# 定义了一个名为 Book 的类,并继承自 models.Model
# 继承 models.Model 表示这个类是一个Django模型,将映射到数据库中的一个表
class Book(models.Model):
# 包含四个字段:name、author、pub_time 和 price
name = models.CharField(max_length=20,null=False)
author = models.CharField(max_length=20,null=False)
pub_time = models.DateTimeField(auto_add_now=True)
price = models.FloatField(default=0)
以上便定义了一个模型。
这个模型继承自 django.db.models.Model
,如果这个模型想要映射到数据库中,就必须继承自这个类。这个模型以后映射到数据库中,表名是模型名称的小写形式,为 book 。
在这个表中,有四个字段,还有一个字段没有写,就是主键 id
,在 django 中,如果一个模型没有定义主键,那么将会自动生成一个自动增长的 int 类型的主键,并且这个主键的名字就叫做 id
。
映射模型到数据库中
将 ORM 模型映射到数据库中,总结起来就是以下几步:
1.在 settings.py
中,配置好 DATABASES
,做好数据库相关的配置。
2.在 app
中的 models.py
中定义好模型,这个模型必须继承自 django.db.models 。
3.将这个 app
添加到 settings.py
的 INSTALLED_APP
中的末尾。
INSTALLED_APPS = [
...
"django.contrib.messages",
"django.contrib.staticfiles",
#
"APP 名称",
]
4在命令行终端,进入到项目所在的路径,然后执行命令 python manage.py makemigrations
来生成迁移脚本文件。
5.同样在命令行中,执行命令 python manage.py migrate
来将迁移脚本文件映射到数据库中。
模型常用Field和参数
在 Django 中,定义了一些 Field 来与数据库表中的字段类型来进行映射。
在Django模型中,字段用于定义模型的属性,代表数据库表中的列。
每个字段代表一个特定的数据类型,并定义可以存储在相应列中的数据类型。
Django提供了多种字段类型来处理不同的数据类型和约束。
常用字段的示例:
from django.db import models
# CharField类
class Book(models.Model):
title = models.CharField(max_length=100) # 存储书名的字段(最大长度:100个字符)
# IntegerField类
class Student(models.Model):
age = models.IntegerField() # 存储学生年龄的字段
# FloatField类
class Product(models.Model):
price = models.FloatField() # 存储产品价格的字段
# DateField类
class Event(models.Model):
event_date = models.DateField() # 存储事件日期的字段
# DateTimeField类
class Post(models.Model):
pub_date = models.DateTimeField() # 存储帖子发布日期和时间的字段
# BooleanField类
class Task(models.Model):
is_completed = models.BooleanField() # 存储任务完成状态的字段
# ForeignKey类
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE) # 外键字段,建立一对多关系
# ManyToManyField类
class Course(models.Model):
name = models.CharField(max_length=100)
class Student(models.Model):
name = models.CharField(max_length=100)
courses = models.ManyToManyField(Course) # 多对多字段,建立多对多关系
# EmailField类
class Contact(models.Model):
email = models.EmailField() # 存储联系人电子邮件地址的字段
# ImageField类
class UserProfile(models.Model):
profile_picture = models.ImageField(upload_to='profile_pics/') # 存储用户个人资料图片的字段
常用字段
AutoField:
映射到数据库中是 int
类型,可以有自动增长的特性。一般不需要使用这个类型,如果不指定主键,那么模型会自动的生成一个叫做 id
的自动增长的主键。如果你想指定一个其他名字的并且具有自动增长的主键,使用 AutoField
也是可以的。
BigAutoField:
64位的整形,类似于 AutoField
,只不过是产生的数据的范围是从 1-9223372036854775807 。
BooleanField:
在模型层面接收的是 True/False
。在数据库层面是 tinyint
类型。如果没有指定默认值,默认值是None
。
CharField:
在数据库层面是 varchar
类型。在 Python 层面就是普通的字符串。这个类型在使用的时候必须要指定最大的长度,也即必须要传递 max_length
这个关键字参数进去。
DateField:
日期类型。在 Python 中是 datetime.date
类型,可以记录年月日。在映射到数据库中也是 date
类型。使用这个 Field
可以传递以下几个参数:
auto_now
:在每次这个数据保存的时候,都使用当前的时间。比如作为一个记录修改日期的字段,可以将这个属性设置为 True
。
auto_now_add
:在每次数据第一次被添加进去的时候,都使用当前的时间。比如作为一个记录第一次入库的字段,可以将这个属性设置为 True
。
DateTimeField:
日期时间类型,类似于 DateField
。不仅仅可以存储日期,还可以存储时间。映射到数据库中是datetime
类型。这个 Field
也可以使用 auto_now
和 auto_now_add
两个属性。
TimeField:
时间类型。在数据库中是 time
类型。在 Python 中是 datetime.time
类型。
EmailField:
类似于 CharField
。在数据库底层也是一个 varchar
类型。最大长度是254个字符。
FileField:
用来存储文件的
ImageField:
用来存储图片文件的
FloatField:
浮点类型。映射到数据库中是 float
类型。
IntegerField:
整形。值的区间是 -2147483648---2147483647 。
BigIntegerField
大整形。值的区间是 -9223372036854775808---9223372036854775807 。
PositiveIntegerField:
正整形。值的区间是 0---2147483647 。
SmallIntegerField:
小整形。值的区间是 -32768---32767 。
PositiveSmallIntegerField:
正小整形。值的区间是 0---32767 。
TextField:
大量的文本类型。映射到数据库中是longtext
类型。
UUIDField:
只能存储 uuid
格式的字符串。 uuid
是一个32
位的全球唯一的字符串,一般用来作为主键。
URLField:
类似于 CharField
,只不过只能用来存储 url
格式的字符串。并且默认的 max_length
是200
。
Field的常用参数
在Django模型中初始化字段时,可以使用各种参数来自定义字段的行为。每个参数都有特定的目的,并允许您定义字段的特性和约束。
以下是初始化字段时使用的一些常见参数:
from django.db import models
# max_length:指定字段的最大长度
# min_length:指定字段的最低长度
class Book(models.Model):
title = models.CharField(max_length=100, min_length=1)
# null:确定字段是否可以存储NULL值
class Student(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField(null=True)
# blank:指示字段是否允许为空
class Article(models.Model):
title = models.CharField(max_length=100, blank=True)
# default:为字段设置默认值
class Order(models.Model):
order_number = models.CharField(max_length=10, default="0000")
# choices:为字段提供选项列表
GENDER_CHOICES = [
('M', 'Male'),
('F', 'Female'),
('O', 'Other'),
]
class Person(models.Model):
name = models.CharField(max_length=100)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
# primary_key:指示字段是否为模型的主键
class UserProfile(models.Model):
user_id = models.AutoField(primary_key=True)
username = models.CharField(max_length=100)
# unique:指定字段的值必须在所有模型实例中唯一
class Product(models.Model):
product_code = models.CharField(max_length=10, unique=True)
# verbose_name:为字段设置一个人类可读的名称
class Car(models.Model):
make = models.CharField(max_length=100, verbose_name="Car Make")
# help_text:为字段提供额外的描述性文本
class Employee(models.Model):
name = models.CharField(max_length=100, help_text="Employee's full name")
# validators:允许您为字段指定验证器列表
from django.core.validators import MinValueValidator, MaxValueValidator
class Score(models.Model):
score = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(100)])
# upload_to:与ImageField一起使用,指定上传文件的目录
class UserProfile(models.Model):
name = models.CharField(max_length=100)
profile_pic = models.ImageField(upload_to='profile_pics/')
# db_index:指示字段是否应该有数据库索引以加快查询速度
class Person(models.Model):
name = models.CharField(max_length=100, db_index=True)
# editable:确定字段在表单和admin接口中是否可编辑
class Task(models.Model):
task_name = models.CharField(max_length=100, editable=False)
# db_column: 重命名当前字段,保存到数据时不使用name,而使用tag_name作为表的字段名
class Tag(models.Model):
name = models.CharField(max_length=20,db_column="tag_name")
max_length :指定字段的最大长度。这个参数适用于如CharField
和CommaSeparatedIntegerField
这样的字段。
null :确定字段是否可以在数据库中存储NULL
值。如果设置为True
,则字段可以是NULL
的;如果设置为False
,则字段将有一个必填值。在使用字符串相关的Field (CharField
/ TextField
)的时候,官方推荐尽量不要使用这个参数,也就是保持默认值 False
。
blank :指示字段是否允许为空(即,包含一个空值)。如果设置为True
,则字段可以为空;如果设置为False
,则字段必须有一个值。这个和 null
是有区别的, null
是一个纯数据库级别的。而 blank
是表单验证级别的。
default :为字段设置默认值。如果在创建对象时没有提供值,则将使用默认值。可以为一个值,或者是一个函数,但是不支持 lambda
表达式。并且不支持列表/字典/集合等可变的数据结构。
choices :为字段提供选项列表。这个参数与如CharField
和IntegerField
这样的字段一起使用,以限制可能的值。
primary_key:指示字段是否为模型的主键。只能有一个字段被设置为主键。
unique:指定字段的值必须在所有模型实例中唯一。
verbose_name:为字段设置一个人类可读的名称。这在表单或admin接口中显示字段时用作标签。
help_text:为字段提供额外的描述性文本。当用户与字段交互时,此文本显示为工具提示或提示。
validators:允许您为字段指定验证器列表。验证器是验证字段值的函数。
upload_to :与ImageField
一起使用,以指定上传文件应存储的目录。
db_index:指示字段是否应该有数据库索引以加快
editable:确定字段在表单和admin接口中是否可编辑。这对于不应该在admin界面中更改的字段(如自动生成的字段)很有用。
db_column:这个字段在数据库中是表下的字段的名字。如果没有设置这个参数,那么将会使用模型中属性的名字。
这些参数为Django模型中的字段行为提供了灵活性和控制。通过适当使用这些参数,您可以根据特定要求和约束定制字段。
更多 Field 参数详情可以参考官方文档:https://docs.djangoproject.com/zh-hans/5.0/ref/models/fields/
模型中Meta配置
对于一些模型级别的配置。我们可以在模型中定义一个类,叫做 Meta 。然后在这个类中添加一些类属性来控制模型的作用。
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,name='description',db_column="description1")
pub_date = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'book_model'
ordering = ['pub_date']
db_table:这个模型映射到数据库中的表名。如果没有指定这个参数,那么在映射的时候将会使用模型名来作为默认的表名
ordering:设置在提取数据的排序方式,比如我想在查找数据的时候根据添加的时间排序
模型 Meta
选项官方文档:https://docs.djangoproject.com/zh-hans/5.0/ref/models/options/
外键和表关系
外键
在 MySQL 中,表有两种引擎,一种是 InnoDB
,另外一种是 myisam
。如果使用的是 InnoDB
引擎,是支持外键约束的。外键的存在使得 ORM
框架在处理表关系的时候异常的强大。
外键在 Django 中的使用,类定义为 class ForeignKey(to,on_delete,**options)
。第一个参数是引用的是哪个模型,第二个参数是在使用外键引用的模型数据被删除了,这个字段该如何处理,比如有 CASCADE
、 SET_NULL
等。
这里以一个实际案例来说明。比如有一个 User
和一个 Article
两个模型。一个 User
可以发表多篇文章,一个 Article
只能有一个 Author
,并且通过外键进行引用。那么相关的示例代码如下:
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100)
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey("User",on_delete=models.CASCADE)
以上使用 ForeignKey
来定义模型之间的关系。即在 article
的实例中可以通过 author
属性来操作对应的 User
模型。这样使用起来非常的方便。
article = Article(title='abc',content='123')
author = User(username='张三',password='111111')
article.author = author
article.save()
# 修改article.author上的值
article.author.username = '李四'
article.save()
使用了 ForeignKey
后,就能通过 author
访问到对应的 user
对象的原因,是因为在底层, Django 为Article
表添加了一个 属性名_id
的字段(比如author
的字段名称是author_id
),这个字段是一个外
键,记录着对应的作者的主键。以后通过 article.author
访问的时候,实际上是先通过 author_id
找到对应的数据,然后再提取 User
表中的这条数据,形成一个模型。
如果想要引用另外一个 app
的模型,那么应该在传递 to
参数的时候,使用 app.model_name
进行指定。以上例为例,如果 User
和 Article
不是在同一个 app
中,那么在引用的时候的示例代码如下:
# User模型在user这个app中
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100)
# Article模型在article这个app中
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey("user.User",on_delete=models.CASCADE)
如果模型的外键引用的是本身自己这个模型,那么 to
参数可以为 'self'
,或者是这个模型的名字。在论坛开发中,一般评论都可以进行二级评论,即可以针对另外一个评论进行评论,那么在定义模型的时
候就需要使用外键来引用自身。示例代码如下:
class Comment(models.Model):
content = models.TextField()
origin_comment = models.ForeignKey('self',on_delete=models.CASCADE,null=True)
# 或者
# origin_comment = models.ForeignKey('Comment',on_delete=models.CASCADE,null=True)
外键删除操作
如果一个模型使用了外键。那么在对方那个模型被删掉后,该进行什么样的操作。可以通过 on_delete 来指定。可以指定的类型如下:
CASCADE :级联操作。如果外键对应的那条数据被删除了,那么这条数据也会被删除。
PROTECT :受保护。即只要这条数据引用了外键的那条数据,那么就不能删除外键的那条数据。
SET_NULL :设置为空。如果外键的那条数据被删除了,那么在本条数据上就将这个字段设置为空。如果设置这个选项,前提是要指定这个字段可以为空。
SET_DEFAULT :设置默认值。如果外键的那条数据被删除了,那么本条数据上就将这个字段设置为默认值。如果设置这个选项,前提是要指定这个字段一个默认值。
SET() :如果外键的那条数据被删除了。那么将会获取 SET 函数中的值来作为这个外键的值。 SET函数可以接收一个可以调用的对象(比如函数或者方法),如果是可以调用的对象,那么会将这个对象调用后的结果作为值返回回去。
DO_NOTHING :不采取任何行为。一切全看数据库级别的约束。
以上这些选项只是Django级别的,数据级别依旧是RESTRICT!
表关系
表之间的关系都是通过外键来进行关联的。而表之间的关系,无非就是三种关系:一对一、一对多(多对一)、多对多等。
一对多
应用场景:比如文章和作者之间的关系。一个文章只能由一个作者编写,但是一个作者可以写多篇文章。文章和作者之间的关系就是典型的多对一的关系。
实现方式:一对多或者多对一,都是通过 ForeignKey 来实现的。
还是以文章和作者的案例进行记录。
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100)
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey("User",on_delete=models.CASCADE)
那么以后在给 Article
对象指定 author
,就可以使用以下代码来完成:
article = Article(title='abc',content='123')
author = User(username='zhiliao',password='111111')
# 要先保存到数据库中
author.save()
article.author = author
article.save()
并且以后如果想要获取某个用户下所有的文章,可以通过 article_set 来实现。
user = User.objects.first()
# 获取第一个用户写的所有文章
articles = user.article_set.all()
for article in articles:
print(article)
一对一
应用场景:比如一个用户表和一个用户信息表。在实际网站中,可能需要保存用户的许多信息,但是有些信息是不经常用的。如果把所有信息都存放到一张表中可能会影响查询效率,因此可以把用户的一些不常用的信息存放到另外一张表中我们叫做 UserExtension
。但是用户表 User
和用户信息表 UserExtension
就是典型的一对一了。
实现方式: Django 为一对一提供了一个专门的 Field
叫做 OneToOneField
来实现一对一操作。
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100)
class UserExtension(models.Model):
birthday = models.DateTimeField(null=True)
school = models.CharField(blank=True,max_length=50)
user = models.OneToOneField("User", on_delete=models.CASCADE)
在 UserExtension
模型上增加了一个一对一的关系映射。其实底层是在 UserExtension
这个表上增加了一个 user_id
,来和 user
表进行关联,并且这个外键数据在表中必须是唯一的,来保证一对一。
多对多
应用场景:比如文章和标签的关系。一篇文章可以有多个标签,一个标签可以被多个文章所引用。因此标签和文章的关系是典型的多对多的关系。
实现方式: Django 为这种多对多的实现提供了专门的 Field
。叫做 ManyToManyField
。还是拿文章和标签为例进行讲解。
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
tags = models.ManyToManyField("Tag",related_name="articles")
class Tag(models.Model):
name = models.CharField(max_length=50)
在数据库层面,实际上 Django 是为这种多对多的关系建立了一个中间表。这个中间表分别定义了两个外键,引用到 article
和 tag
两张表的主键。
related_name和related_query_name
related_name
还是以 User
和 Article
为例来进行。如果一个 article
想要访问对应的作者,那么可以通过author
来进行访问。
但是如果有一个 user
对象,想要通过这个 user
对象获取所有的文章,这时候可以通过 user.article_set
来访问,这个名字的规律是 模型名字小写_set
。
user = User.objects.get(name='张三')
user.article_set.all()
如果不想使用 模型名字小写_set
的方式,想要使用其他的名字,那么可以在定义模型的时候指定related_name
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 传递related_name参数,以后在方向引用的时候使用articles进行访问
author = models.ForeignKey("User",on_delete=models.SET_NULL,null=True,related_name='articles')
以后在反向引用的时候。使用 articles 可以访问到这个作者的文章模型。
user = User.objects.get(name='张三')
user.articles.all()
如果不想使用反向引用,那么可以指定 related_name='+'
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 传递related_name参数,以后在方向引用的时候使用articles进行访问
author = models.ForeignKey("User",on_delete=models.SET_NULL,null=True,related_name='+')
以后将不能通过 user.article_set
来访问文章模型了
related_query_name
在查找数据的时候,可以使用 filter
进行过滤。使用 filter
过滤的时候,不仅仅可以指定本模型上的某个属性要满足什么条件,还可以指定相关联的模型满足什么属性。比如现在想要获取写过标题为 abc
的所有用户,那么可以这样写:
users = User.objects.filter(article__title='abc')
如果设置了 related_name
为 articles
,因为反转的过滤器的名字将使用 related_name
的名字,那么上例代码将改成如下:
users = User.objects.filter(articles__title='abc')
可以通过 related_query_name
将查询的反转名字修改成其他的名字。比如 article
。示例代码如下:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 传递related_name参数,以后在方向引用的时候使用articles进行访问
author = models.ForeignKey("User",on_delete=models.SET_NULL,null=True,related_name='articl es',related_query_name='article')
那么在做反向过滤查找的时候就可以使用以下代码:
users = User.objects.filter(article__title='abc')