对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
ORM解决的主要问题是对象和关系的映射。它通常把一个类和一个表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。
官方文档
一、在Django项目的settings.py
中配置连接信息:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '数据库名',
'USER': '用户名',
'PASSWORD': '数据库密码',
'HOST': '数据库IP',
'POST': 3306
}
}
二、在Django项目的__init__.py
配置,告诉Django使用pymysql连接MySQL数据库:
import pymysql
pymysql.install_as_MySQLdb()
注意:Django1.x默认的还是使用MySQLdb:执行会报:ImportError: No module named ‘MySQLdb’
报错:django migrate django.db.utils.InternalError:
,一般是数据库编码不为utf8导致
三、执行命令
python manage.py makemigrations [appname]
–> 将models.py文件中的改动记录在(app/migrations/00xx_****.py)上
python manage.py migrate [appname]
–> 将改动翻译成SQL语句,去数据库中执行
AutoField(Field):int自增列;必须填入参数primary_key=True
BigAutoField(AutoField):bigint自增列;必须填入参数primary_key=True
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):正小整数;0 ~ 32767
IntegerField(Field):整数列(有符号的);2147483648 ~ 2147483647
SmallIntegerField(IntegerField):小整数;32768 ~ 32767
BigIntegerField(IntegerField):长整型(有符号的);9223372036854775808 ~ 9223372036854775807
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):正整数;0~2147483647
BooleanField(Field):布尔值类型
NullBooleanField(Field):可以为空的布尔值
CharField(Field):字符类型;必须提供max_length参数,max_length表示字符长度
TextField(Field):文本类型
EmailField(CharField):字符串类型;Django Admin以及ModelForm中提供验证机制
IPAddressField(Field):字符串类型;Django Admin以及ModelForm中提供验证 IPV4 机制
URLField(CharField):字符串类型;Django Admin以及ModelForm中提供验证 URL
SlugField(CharField):字符串类型;Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField):字符串类型;格式必须为逗号分割的数字
UUIDField(Field):字符串类型;Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field):字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能;
参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field):字符串,路径保存在数据库,文件上传到指定目录;
参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField):字符串,路径保存在数据库,文件上传到指定目录;
参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField):日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field):日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field):时间格式 HH:MM[:ss[.uuuuuu
DurationField(Field):长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field):浮点型
DecimalField(Field):10进制小数
参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field):二进制类型
null:表示这个字段可以为空
unique:表示该字段必须唯一
db_index:表示设置数据库索引
default:设置默认值
auto_now_add:创建数据记录的时候会把当前时间添加到数据库
auto_now:每次更新数据记录的时候会更新该字段
class FixedChardField(models.Field):
"""
自定义char类型
"""
def __init__(self, max_length, *args, **kwargs):
super().__init__(max_length=max_length, *args, **kwargs)
self.length = max_length
def db_type(self, connection):
"""
生成数据表的字段类型:(char,长度为length的值)
"""
return 'char(%s)' % self.length
class Person(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=25)
cname = FixedChardField(max_length=25)
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(25) | NO | | NULL | |
| cname | char(25) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
item | 关系 | 举个栗子 | 在数据库中的体现 |
---|---|---|---|
ForeignKey | 一对多 | 每个用户可能有多个评论,但某一个评论中只对应到一个用户 | 建立个字段,保存对方的主键 |
ManyToManyField | 多对多 | 每个文章有多个作者,每个用户可以发表多个文章 | 建立一个表。维护两个model数据的关系 |
OneToOneField | 一对一 | 每个用户只能有一个“安全邮箱" | 建立个字段,保存对方的主键,并建立约束 |
返回QuerySet对象
返回特殊QuerySet
返回具体对象
返回布尔值
返回数字
创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。
# 作者创建一本书,并指定出版社
models.Author.objects.first().books.create(title='python', publisher_id=2)
把指定的model对象添加到关联对象集中。
# 作者添加一本书
author_obj = models.Author.objects.get(id=1)
book_obj = models.Book.objects.get(id=3)
author_obj.books.add(book_obj)
# 作者添加多本书
books_objs = models.Book.objects.filter(id__gt=4)
author_obj = models.Author.objects.get(id=2)
author_obj.books.add(*books_objs)
# 直接添加id
author_obj = models.Author.objects.get(id=2)
author_obj.books.add(9)
从关联对象集中移除执行的model对象
# 删除title='java'的书
author_obj = models.Author.objects.get(id=2)
book_obj = models.Book.objects.get(title='java')
author_obj.books.remove(book_obj)
# 删除id=7的书
author_obj = models.Author.objects.get(id=2)
author_obj.books.remove(7)
从关联对象集中移除一切对象
# 清除所有关联对象
author_obj = models.Author.objects.get(id=2)
author_obj.books.clear()
创建一个学生表:
class Student(models.Model):
name = models.CharField(max_length=20)
gender = models.BooleanField(default=True)
age = models.IntegerField(default=0)
content = models.TextField(max_length=200)
email = models.EmailField()
class Meta:
db_table = 'student'
# 增
models.Student.objects.create(name='学生A', gender=False, age=122, content='学习优异', email='[email protected]')
student_obj = models.Student(name='学生B', gender=True, age=13, content='学习良好', email='[email protected]')
student_obj.save()
# 批量增加数据
student_objs = [models.Student(name='学生', gender=True, age=11, content="学生的描述") for i in range(10)]
models.Student.objects.bulk_create(student_objs)
# 删
models.Student.objects.get(id=1).delete()
# 改
obj = models.Student.objects.get(name='学生A')
obj.name = '学生AAA'
obj.save()
# 查
# all():查询所有结果;QuerySet
student_all = models.Student.objects.all()
# filter(**kwargs):查询条件匹配的对象;QuerySet
student_list = models.Student.objects.filter(name='学生B')
# get(**kwargs):查询条件相匹配的对象,对象类型,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
student_obj = models.Student.objects.get(name='学生C')
# exclude(**kwargs):筛选条件不匹配的对象
student_list = models.Student.objects.exclude(name='学生D')
# values(*field):返回指定列名的数据,每个对象是一个字典
student_list = models.Student.objects.values('id', 'name')
# value_list(*field):返回指定列名的数据,每个对象是一个元组
student_list = models.Student.objects.values_list('id', 'name')
# order_by(*field):排序;'+':升序,'-':降序
student_list = models.Student.objects.all().order_by('age','-id')
# reverse():顺序反转
# distince():去重
# count():统计对象数量
c = models.Student.objects.all().count()
# first():返回第一条数据;对象类型
# last():返回最后一条数据;对象类型
# exists():表里是否包含数据
has = models.Student.objects.exists()
# 查询id大于5小于10的数据
student_list = models.Student.objects.filter(id__gt=5, id__lt=10)
# 查询id为2,5,7的数据
student_list = models.Student.objects.filter(id__in=[2, 5, 7])
# 查询名字中包含'A'的数据
student_list = models.Student.objects.filter(name__contains='A')
# 不区分大小写
student_list = models.Student.objects.filter(name__icontains='a')
# 查询id范围从7到10
student_list = models.Student.objects.filter(id__range=[7, 10])
# 查询指定字符串开头
student_list = models.Student.objects.filter(name__startswith='学生')
通常一对一字段用来扩展已有字段
class Employee(models.Model):
name = models.CharField(max_length=32)
class Meta:
db_table = 'employee'
class EmployeeDetail(models.Model):
phone = models.CharField(max_length=11)
email = models.EmailField()
addr = models.CharField(max_length=108)
employee = models.OneToOneField(to='Employee')
class Meta:
db_table = 'employee_detail'
# 正向查找:EmployeeDetail->Employee
employee_detail = models.EmployeeDetail.objects.get(pk=1)
employee_detail.employee
# 反向查找:Employee->EmployeeDetail
employee = models.Employee.objects.get(pk=1)
employee.employeedetail
外键类型在ORM中用来表示外键关联关系,一般把ForeignKey
字段设置在 一对多中多的一方
class Publisher(models.Model):
"""
出版
"""
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32, unique=True, db_index=True)
def __str__(self):
return self.name
class Meta:
db_table = 'publisher'
class Book(models.Model):
"""
书籍
"""
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
# 外键联系
publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)
def __str__(self):
return self.title
class Meta:
db_table = 'book'
字段参数
to:设置关联的表
to_field:设置关联的表的字段
related_name:反向操作时,使用该字段名替代'表名_set'
on_delete:
models.CASCADE:删除关联数据,与之关联也删除
models.DO_NOTHING:删除关联数据,引发错误IntegrityError
models.PROTECT:删除关联数据,引发错误ProtectedError
models.SET_NULL:删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
models.SET_DEFAULT:删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
正向查询
通过外键查询
# 1.使用对象查询
book_obj = models.Book.objects.first() # 获取书对象
book_obj.publisher # 获取出版社对象
book_obj.publisher.name # 获取出版社名
# 2.使用双下划线
publishers = models.Book.objects.filter(id=1).values_list('publisher__name')
反向查询
# 1.通过对象查询
publisher_obj = models.Publisher.objects.first() # 获取出版社对象
books = publisher_obj.book_set.all() # 获取所有书
# 2.使用双下划线
books = models.Publisher.objects.filter(id=1).values_list('book__title')
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
books = models.ManyToManyField(to='Book')
class Book(models.Model):
title = models.CharField(max_length=32)
publish_date = models.DateField(auto_now_add=True)
正向查询
# 1.通过对象查询
book_list = models.Author.objects.get(id=1).books.all()
# 2.使用双下划线
book_list = models.Author.objects.filter(id=1).values_list('books__title')
反向查询
# 1.通过对象查询
author_list = models.Book.objects.get(id=2).author_set.all()
# 2.使用双下划线
author_list = models.Book.objects.filter(id=2).values_list('author__name')
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=32)
publish_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.title
class AuthorToBook(models.Model):
author = models.ForeignKey(to='Author')
book = models.ForeignKey(to='Book')
remark = models.CharField(max_length=300)
class Meta:
# 联合唯一约束
unique_together = ('author', 'book')
查询
# 查询author_id=1的书籍id
book_id_list = models.AuthorToBook.objects.filter(author_id=1).values_list('book_id')
# 查询指定书籍
book_list = models.Book.objects.filter(id__in=book_id_list)
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
books = models.ManyToManyField(to='Book', through='Author2Book', through_fields=('author', 'book'))
# through:指定第三张表
# through_fields:有顺序要求,field1关联ManyToManyField的外键,field2关联book外键
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=32)
publish_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.title
class Author2Book(models.Model):
author = models.ForeignKey(to='Author')
book = models.ForeignKey(to='Book')
remark = models.CharField(max_length=300)
class Meta:
# 联合唯一约束
unique_together = ('author', 'book')
查询
# 通过作者查找书籍
book_list = models.Author.objects.get(id=1).books.all()
# 通过书籍查找作者
author_list = models.Book.objects.get(id=3).author_set.all()
注意:
aggregate()是QuerySet的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
from django.db.models import Avg, Sum, Max, Min, Count
# 获取平均数
models.Book.objects.all().aggregate(Avg('price'))
# 指定命名
models.Book.objects.all().aggregate(price_avg=Avg('price'))
# 获取最大值
models.Book.objects.all().aggregate(Max('price'))
# 获取最小值
models.Book.objects.all().aggregate(Min('price'))
# 获取总价格
models.Book.objects.all().aggregate(Sum('price'))
# 获取价格出现的次数
models.Book.objects.all().aggregate(Count('price'))
class Employee(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
salary = models.DecimalField(max_digits=8, decimal_places=2)
province = models.CharField(max_length=32)
dept = models.CharField(max_length=32)
class Meta:
db_table = 'employee'
根据部门和平均工资分组:
使用原生SQL查询
select dept,avg(salary) from employee group by dept;
使用Django的ORM
models.Employee.objects.values('dept').annotate(avg=Avg('salary')).values('dept', 'avg')
说明:annotate前面是什么字段,就按什么分组
Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,用于比较同一个 model 实例中两个不同字段的值。
# 查询工资大于年龄的员工
from django.db.models import F
emploee_list = models.Employee.objects.filter(salary__gt=F('age'))
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
models.Employee.objects.filter(salary__gt=F('age')+6000)
修改char字段
from django.db.models import Value
from django.db.models.functions import Concat
models.Employee.objects.all().update(dept=Concat(F('dept'),Value('部门')))
filter()
等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR
语句),你可以使用Q对象
。
# 查询id或age
employee_list = models.Employee.objects.filter(Q(id=1) | Q(age=30))
你可以组合&
和|
操作符以及使用括号进行分组来编写任意复杂的Q
对象。同时,Q
对象可以使用~
操作符取反,这允许组合正常的查询和取反(NOT
) 查询
from django.db import connection
# 查询id=1的数据
cursor = connection.cursor()
cursor.execute('select * from person where id=%s', [1])
ret = cursor.fetchone()
print(ret)
# 查询salary>1000的数据
ret = models.Person.objects.all().extra(
select={'gt': '200'}
)
print(ret)
在Django项目的settings.py文件中,在最后复制粘贴如下代码:
[](javascript:void(0)
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
即为你的Django项目配置上一个名为django.db.backends的logger实例即可查看翻译后的SQL语句。
import os
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
import django
django.setup()
from app01 import models
books = models.Book.objects.all()
print(books)