Django ORM单表和常用详细字段,常用方法,以及多数据库模式操作
ORM简介
- MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动
- ORM是“对象-关系-映射”的简称。
#sql中的表
#创建表:
CREATE TABLE employee(
id INT PRIMARY KEY auto_increment ,
name VARCHAR (20),
gender BIT default 1,
birthday DATA ,
department VARCHAR (20),
salary DECIMAL (8,2) unsigned,
);
#sql中的表纪录
#添加一条表纪录:
INSERT employee (name,gender,birthday,salary,department)
VALUES ("alex",1,"1985-12-12",8000,"保洁部");
#查询一条表纪录:
SELECT * FROM employee WHERE age=24;
#更新一条表纪录:
UPDATE employee SET birthday="1989-10-24" WHERE id=1;
#删除一条表纪录:
DELETE FROM employee WHERE name="alex"
# 用ORM就是用Django定义的类去实现代码转义成数据的语法进行执行
#缺点是效率低,需要转换在执行,优点兼容性搞,如果换数据库了,都可以兼容运行
#必须在类里继承 models.Model
#python的类
class Employee(models.Model):
#AutoField是自增
id=models.AutoField(primary_key=True)
#CharField 是字符串 最大不能超过32,和Mysql一样
name=models.CharField(max_length=32)
#DateField就是个日期类型日期格式必须是年-月-日 ,BooleanField是布尔
gender=models.BooleanField()
birthday=models.DateField()
department=models.CharField(max_length=32)
#DecimalField 是个浮点类型,max_digits是最大位数包含小数点后的数字
#decimal_places代表小数点后有几位
salary=models.DecimalField(max_digits=8,decimal_places=2)
#python的类对象
#添加一条表纪录:
emp=Employee(name="alex",gender=True,birthday="1985-12-12",epartment="保洁部")
emp.save()
#查询一条表纪录:
Employee.objects.filter(age=24)
#更新一条表纪录:
Employee.objects.filter(id=1).update(birthday="1989-10-24")
#删除一条表纪录:
Employee.objects.filter(name="alex").delete()
单表操作
- 创建APP后在APP中的models.py创建模型
from django.db import models
# Create your models here.
class Book(models.Model):
id=models.AutoField(primary_key=True)
title=models.CharField(max_length=32)
state=models.BooleanField()
pub_date=models.DateField()
price=models.DecimalField(max_digits=8,decimal_places=2)
publish=models.CharField(max_length=32)
- 更多的字段和参数
每个字段有一些特有的参数,例如,CharField需要max_length参数来指定VARCHAR数据库字段的大小。还有一些适用于所有字段的通用参数。 这些参数在文档中有详细定义,这里我们只简单介绍一些最常用的:
<1> CharField
字符串字段, 用于较短的字符串.
CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.
<2> IntegerField
#用于保存一个整数.
<3> FloatField
一个浮点数. 必须 提供两个参数:
参数 描述
max_digits 总位数(不包括小数点和符号)
decimal_places 小数位数
举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
models.FloatField(..., max_digits=5, decimal_places=2)
要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
models.FloatField(..., max_digits=19, decimal_places=10)
admin 用一个文本框(<input type="text">)表示该字段保存的数据.
<4> AutoField
一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段;
自定义一个主键:my_id=models.AutoField(primary_key=True)
如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.
<5> BooleanField
A true/false field. admin 用 checkbox 来表示此类字段.
<6> TextField
一个容量很大的文本字段.
admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框).
通常用的是变长而不是定长,但是根据不同数据库会有不同长度限制,比如mysql TextField 对应 TEXT 类型,最大存储容量为 65,535 字节。而sqllite和PostgreSQL则无长度限制,
因此如果使用Mysql用TextField需要存储的文章包含图片,文字等内容的,需要更大的存储容量,比如 MEDIUMTEXT(最大 16,777,215 字节)或者 LONGTEXT(最大 4,294,967,295 字节)
使用方法
class Article(models.Model):
title = models.CharField(max_length=255)
content = models.TextField() # 默认 TEXT 类型
long_content = models.TextField(db_type='longtext') # 改为 LONGTEXT 最大容量约42G
big_content = models.TextField(db_type='mediumtext') # 改为 MEDIUMTEXT 最大容量约16MB
<7> EmailField
一个带有检查Email合法性的 CharField,不接受 maxlength 参数.
<8> DateField
一个日期字段. 共有下列额外的可选参数:
Argument 描述
auto_now 当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳.
auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间.
(仅仅在admin中有意义...)
<9> DateTimeField
一个日期时间字段. 类似 DateField 支持同样的附加选项.
<9> TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
<10> ImageField
类似 FileField, 不过要校验上传对象是否是一个合法图片.#它有两个可选参数:height_field和width_field,
如果提供这两个参数,则图片将按提供的高度和宽度规格保存.
<11> FileField
一个文件上传字段.
要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting,
该格式将被上载文件的 date/time
替换(so that uploaded files don't fill up the given directory).
admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) .
注意:在一个 model 中使用 FileField 或 ImageField 需要以下步骤:
(1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件.
(出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对
WEB服务器用户帐号是可写的.
(2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django
使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT).
出于习惯你一定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来说,如果你的 ImageField
叫作 mug_shot, 你就可以在模板中以 {{ object.#get_mug_shot_url }} 这样的方式得到图像的绝对路径.
<12> URLField
用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且
没有返回404响应).
admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框)
<13> NullBooleanField
类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项
admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据.
<14> SlugField
"Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们通常用于URLs
若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. #在
以前的 Django 版本,没有任何办法改变50 这个长度.
这暗示了 db_index=True.
它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate
the slug, via JavaScript,in the object's admin form: models.SlugField
(prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields.
<13> XMLField
一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径.
<14> FilePathField
可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的.
参数 描述
path 必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目.
Example: "/home/images".
match 可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名.
注意这个正则表达式只会应用到 base filename 而不是
路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif.
recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录.
这三个参数可以同时使用.
match 仅应用于 base filename, 而不是路径全名. 那么,这个例子:
FilePathField(path="/home/images", match="foo.*", recursive=True)
...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif
<15> IPAddressField
一个字符串形式的 IP 地址, (i.e. "24.124.1.30").
<16> CommaSeparatedIntegerField
用于存放逗号分隔的整数值. 类似 CharField, 必须要有maxlength参数.
<17> SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
<18> PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
<19> PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
<20> BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
<21> UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
import uuid
print(uuid.uuid4()) # 01b338b8-b084-4f80-b89e-15a99916e4df
常见方法
| 方法 | 用处 | 调用对象 | 返回对象 | 例子1 | 例子2 | 例子3 | 例子4 | 例子5 | 例子6 |
|---|---|---|---|---|---|---|---|---|---|
| all() | 查询所有结果 | <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>]> | QuerySet | Book.object.all() | - | - | - | ||
| filter(**kwargs) | 筛选 | Queryset | Queryset | Book.object.all().filter(price=100) | Book.object.all().filter(**{"price":100}) | 1 | 1 | ||
| get(**kwargs): | 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,大于1个或者0个就报错 | Queryset | Module | Book.object.get(name="刘德华") | 1 | 1 | 1 | ||
| exclude(**kwargs) | 它包含了与所给筛选条件不匹配的对象 | QuerySet | QuerySet | Book.objects.exclude(name="刘德华") | 1 | 1 | 1 | ||
| order_by(*field) | 对查询结果排序 | QuerySet | QuerySet | Book.objects.order_by("-id") | id前加-代表降序, 不加符号代表升序 | Book.objects.order_by("id","-price") | 也可以对多个表头进行排序 | ||
| count() | 返回数据库中匹配查询(QuerySet)的对象数量 | QuerySet | INT | Student.objects.filter(sex=1).count() | 1 | 1 | 1 | ||
| first() | 返回第一条记录 | QuerySet | module对象 | Book.objects.all().first() | 1 | 1 | 1 | ||
| last() | 返回最后一条记录 | QuerySet | module对象 | Book.objects.all().last() | 1 | 1 | 1 | ||
| exists() | 如果QuerySet包含数据,就返回True,否则返回False | QuerySet | Bool | 因为他只取表中的一条数据,所以操作速度很快 | 1 | 1 | 1 | ||
| values(*field) | 返回一个ValueQuerySet——一个特殊的QuerySet | QuerySet | QuerySet | Book.objects.all().values("id","name","age") | 可以通过参数设置要转换的字段并返回 | 1 | 1 | ||
| values_list(*field) | 与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 | QuerySet | QuerySet | Book.objects.all().values_list("price") | 可以通过参数设置要转换的字段并返回 | 里面有flat和named 2个布尔值参数 , flat会返回简单的列表,named返回的namedtuples 列表 | 举例比如返回一个ID字段,默认返回的格式为<QuerySet [(2,), (1,)]>,flat属性返回的是<QuerySet [2, 1]> ,named返回的是 <QuerySet [{Row(id=1), Row(id=2)}]> | ||
| distinct() | 去重 | QuerySet | QuerySet对象 | Book.objects.all().values("price").distinct() | distinct()里加参数可以按照指定字段去重,在mysql中是不支持的,postgresql数据库支持 | 这样进行去重u.objects.filter(user_id=1).values('address', "signer_name", 'signer_mobile').distinct() | 1 | ||
| reverse() | 待补 | QuerySet | QuerySet对象 | Book.objects.all().reverse() | 1 | 1 | 1 |
更多参数
(1)
null
如果为True,Django
将用NULL
来在数据库中存储空值。 默认值是False.
(1)
blank
如果为True,该字段允许不填。默认为False。
要注意,这与
null
不同。null纯粹是数据库范畴的,而
blank
是数据验证范畴的。
如果一个字段的blank = True,表单的验证将允许该字段是空值。如果字段的blank = False,该字段就是必填的。
(2)
default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。
(3)
primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key = True,
Django
就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key = True。
(4)
unique
数据库中字段是否可以建立唯一索引
(5)
Datefield和DateField的两个参数
(6)
auto_now_add=True 创建数据记录的时候把当前的时间添加到数据库,并且时间不会改变
(7)
auto_now=True,每次更新数据记录的时候会更新改字段。
(8)
choices #sex_choice是一个列表或者元组都是可以的 sql的enum枚举作用
-在model表模型定义的时候给某个字段指定choice
sex_choice=((1,'男'),(2,'女'),(0,'未知'))
sex =models.IntegerField(default=1,choices=sex_choice)
-在使用的时候,直接取出中文
对象.get_sex_display()
#循环的取值 !!!
for author in res:
print(author.get_sex_display())
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name"
# 联合索引 3.o以前是用index_together字段
indexes = [
("pub_date", "deadline"),
]
# 联合唯一索引
unique_together = (("driver", "restaurant"),)
# 也可以使用constraints constraints可以定义多种类型的约束条件,例如主键约束、唯一性约束、外键约束等,而unique_together仅能定义多字段唯一性约束;
# 使用constraints定义唯一性约束时,会为该字段创建唯一索引;而使用unique_together定义唯一性约束则不会创建索引,需要手动添加索引以提高查询效率。
constraints=[
models.UniqueConstraint(fields=["driver","restaurant"],name="unique_driver_restaurant")
]
# admin中显示的表名称
verbose_name
# verbose_name加s
verbose_name_plural
枚举在html渲染的时候默认渲染的sex是前面的数字1,2,0,如果要渲染出后面对应的文字要用固定的语法字段 get_字段_display,也就是get_sex_display,
这样就会在蒙版渲染时候渲染对应的男,女,未知信息
(9)
db_column 数据库中字段的列名
db_tablespace
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引
auto_now_add 更新时,自动更新为当前时间,一般用于create_time
auto_now 创建时,自动生成时间,用于update_time
verbose_name Admin中显示的字段名称
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'}
validators 自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator, URLValidator, DecimalValidator, MaxLengthValidator, MinLengthValidator, MaxValueValidator, MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]
)
(10) limit_choices_to 当你在model中FK字段或者M2M字段中加上 limit_choices_to选项的时候,前端页面的用form组件渲染会根据你的限制条件来展示,这样就达到来限制用户选择的目的.
class ClassList(models.Model):
"""
班级表
"""
school = models.ForeignKey(verbose_name='校区', to='School', on_delete=models.CASCADE)
course = models.ForeignKey(verbose_name='课程名称', to='Course', on_delete=models.CASCADE)
semester = models.PositiveIntegerField(verbose_name='班级(期)')
price = models.PositiveIntegerField(verbose_name='学费')
start_date = models.DateField(verbose_name='开班日期')
graduate_date = models.DateField(verbose_name='结业日期', null=True, blank=True)
class_teacher = models.ForeignKey(verbose_name='班主任', to='UserInfo', related_name='classes',
on_delete=models.CASCADE, limit_choices_to={'depart__title': '教质部'})
tech_teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo', related_name='teach_classes',
limit_choices_to={'depart__title__in': ['Python教学部', 'Linux教学部']})
memo = models.TextField(verbose_name='说明', null=True, blank=True)
从上面的model字段可以看出, class_teacher字段限制了选择条件:{'depart__title': '教质部'}, 即在选择班主任的时候,只能选择关联班主任的外键表depart中名称为教质部的数据
ManyToMany字段的limit_choices_to指向了 {'depart__title__in':['xx','xx']} 一个列表,那在前端中只能选择这个列表中的数据
(11) class Meta 自定义orm表的信息
db_table = 'tb_student' # 指明数据库表名,如果没有指定表明,则默认为子应用目录名_模型名称,例如: users_student
verbose_name = '学生信息表' # 在admin站点中显示的名称
verbose_name_plural = verbose_name # 显示的复数名称
abstract = True # 设置当前模型为抽象模型, 当系统运行时, 不会认为这是一个数据表对应的模型.
unique_together = (('username','age'),) #联合唯一索引
indexes = (('username', 'age'),) # 联合索引(不唯一) 3.o以前是用index_together字段
使用示例
class Delete(models.Model):
deleted_status = models.BooleanField(verbose_name="已删除",default=False)
class Meta:
#如果想让用户表和班级表都添加一个逻辑删除字段,就可以让他们继承Delete表,但是就会同时生成一张Delete表
abstract = True # 设置abstract可以把该表当做抽象模型,不在数据库生成实际表.
class Clas(Delete): #继承Delete,使用delete这个字段
name = models.CharField(max_length=32, unique=True, verbose_name="班级名称")
class Meta:
db_table = "db_class" #自定义创建的表名,而不是用默认的"应用目录名_模型名称"创建表名
class Userinfo(Delete):
username = models.CharField(max_length=15)
索引使用注意事项:
1、联合索引不要以主键开头,不然联合索引和主键索引作用是一样的
2、当你的查询sql where条件中用到的多个字段在联合索引中的查询速度优于在单列索引的速度
3、使用联合索引时,当你的where条件中不包含联合索引中的第一个字段时,无法用到索引
4、根据最左原则,联合索引可以使用部分生效的索引。(a,b,c) 当 where a=1 and c=2 a 索引是生效的
5、单列索引不受字段最左原则限制,但受内容最左原则限制
最后通过两条数据库迁移命令即可在指定的数据库中创建表 :
python manage.py makemigrations
python manage.py migrate

注意1:NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建 USER和PASSWORD分别是数据库的用户名和密码。设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。然后,启动项目,会报错:no module named MySQLdb 。这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb 对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名文件下的__init__,在里面写入:
import pymysql
pymysql.install_as_MySQLdb()
注意2:确保项目配置文件中的INSTALLED_APPS中写入我们创建的app名称

注意3:如果报错如下:有2中解决办法django.core.exceptions.ImproperlyConfigured: mysqlclient1.3.3 or neweris required; you have0.7.11.None
-
在项目文件下的__init__.py文件下写入,这个版本号根据错误提示的去填写
pymysql.version_info = (1, 3, 3, "final", 0)

-
适合Django 2.0的解决办法
通过查找路径C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql
这个路径里的文件把注释掉 就OK了。
if version < (1,3,3):
raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)
注意4如果想打印orm转换过程中的sql语句,需要在settings中进行如下配置:
LOGGING= {
'version':1,
'disable_existing_loggers':False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate':True,
'level':'DEBUG',
},
}
}
多数据库模式
官方文档 https://docs.djangoproject.com/zh-hans/3.1/topics/db/multi-db/#topics-db-multi-db-hints
多数据库简介
使用至少2个数据库服务。通过 DATABASES 配置来将指定的数据库链接放入一个字典,以此来映射数据库别名,数据库别名是在整个Django中引用特定数据库的一种方式。这里配置了2个库,他们的对应关系是
default > day05db 192.168.1.2 master 负责写
bak > day05bak 192.168.2.12 slave 负责读
DATABASES = {
"default": {
'ENGINE': 'dj_db_conn_pool.backends.mysql',
'NAME': 'day05db', # 数据库名字
'USER': 'root',
'PASSWORD': 'root123',
'HOST': '192.168.1.2', # ip
'PORT': 3306,
'OPTIONS': {
'charset': 'utf8mb4', # 连接选项配置,mysql8.0以上无需配置
},
'POOL_OPTIONS': {
'POOL_SIZE': 10, # 最小
'MAX_OVERFLOW': 10, # 在最小的基础上,还可以增加10个,即:最大20个。
'RECYCLE': 24 * 60 * 60, # 连接可以被重复用多久,超过会重新创建,-1表示永久。
'TIMEOUT': 30, # 池中没有连接最多等待的时间。
}
},
"bak": {
'ENGINE': 'dj_db_conn_pool.backends.mysql',
'NAME': 'day05bak', # 数据库名字
'USER': 'root',
'PASSWORD': 'root123',
'HOST': '192.168.2.12', # ip
'PORT': 3306,
'POOL_OPTIONS': {
'POOL_SIZE': 10, # 最小
'MAX_OVERFLOW': 10, # 在最小的基础上,还可以增加10个,即:最大20个。
'RECYCLE': 24 * 60 * 60, # 连接可以被重复用多久,超过会重新创建,-1表示永久。
'TIMEOUT': 30, # 池中没有连接最多等待的时间。
}
},
}
多数据库模式使用migrate命令会和之前不同,之前的命令是
python manage.py migrate此时migrate作用的是配置文件中default下的.可以在后面加参数指定appname和数据库,示例如下
python manage.py migrate app_label --database # migrate后面可以加app的name, --database是配置文件中的数据库
# 该命令实现的功能是实现对制定的appname,制定的数据库库名 进行数据库迁移.
python manage.py migrate --database=default
python manage.py migrate --database=bak
在开发中,如果有多个数据库,可以用手动的模式指定我们要操作的数据库,比如
models.UserInfo.objects.using("default").create(title="aaa")
my_object.save(using='legacy_users')
这里用using方法进行手动选择数据库.
除了手动模式,也可以配置自动模式
- 编写router类
class DemoRouter(object):
def db_for_read(...):
return "bak"
def db_for_write(...):
return "default"
- 配置文件加入配置DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.PrimaryReplicaRouter']
读写分离

代码逻辑如下:
class DemoRouter(object):
def db_for_read(self,model,**hints):
print(model._meta.app_label) # 打印当前应用名字 比如app01
print(model._meta.model_name) # 打印当前模型的对象名字 比如Userinfo
# 我们可以在这里做逻辑判断,比如default里的app01下有Userinfo表,而bak里app02下有Role表
# 那么我需要实现的逻辑是读取Userinfo,则返回default这个配置下的库,而读取Role表则返回bak下的库
if model._meta.model_name=="userinfo": # 这里表明会变小写
return "default"
return "bak"
分库模式1 (多个app对应多个数据库)
假设有100张表,50表放到app01应用下的A数据库,另外50表放到app02下的-B数据库。
按不同的应用不同的数据库里创建的表信息如下
-
app01/models
from django.db import models class UserInfo(models.Model): title = models.CharField(verbose_name="标题", max_length=32) -
app02/models
from django.db import models class Role(models.Model): title = models.CharField(verbose_name="标题", max_length=32) -
不同应用下的不同数据库进行数据库迁移
python manage.py makemigrationspython manage.py migrate app01 --database=defaultpython manage.py migrate app02 --database=bak

- 不同应用不同库的操作(手动模式)
from django.shortcuts import render, HttpResponse
from app01 import models as m1
from app02 import models as m2
def index(request):
# app01中的操作 -> default
v1 = m1.UserInfo.objects.all()
print(v1)
# app02中的操作 -> bak
v2 = m2.Role.objects.using('bak').all()
print(v2)
return HttpResponse("返回")
- 不同应用不同库的操作(自动模式)

分库模式2 (单个app对应多个数据库)
这里我们假设app01这个应用下有3张表
其中1表需要放到defalut数据库;2表放到bak数据库。
- 那么模型写在一个model里
# Userinfo需要放到default这个配置里的数据库
class Userinfo(models.Model):
title = models.CharField(verbose_name="标题")
# 另外两张表需要放到bak这个配置里的数据库
class Role(models.Model):
title = models.CharField(verbose_name="标题")
class Depart(models.Model):
title = models.CharField(verbose_name="标题")
- 此时进行数据库迁移的时候不能按照默认的来,因为默认的迁移会将所有的表都建在defalt库里.
我们需要对路由类中添加allow_migrate方法在数据库迁移的时候就进行设置.
这个方法会决定是否允许迁移操作在别名为 db 的数据库上运行。如果操作运行,那么返回 True ,如果没有运行则返回 False ,或路由没有意见则返回 None 。
详情看官网文档 https://docs.djangoproject.com/zh-hans/3.1/topics/db/multi-db/#database-routers
class DemoRouter(object):
def allow_migrate(self, db, app_label, model_name=None, **hints):
if db == "bak":
if model_name in ["role", "depart"]:
return True
else:
return False
if db == "default":
if model_name in ["userinfo"]:
return True
else:
return False
然后执行数据库迁移命令
python manage.py migrate app01 --database=default
python manage.py migrate app01 --database=bak
注意事项
一定不要跨数据库做关联 -> django不支持
怎么办?
尽可能的将有关联的表放在一个库中。

浙公网安备 33010602011771号