ORM

ORM

(1)什么是ORM

  • ORM是一种将对象与关系型数据库之间的映射的技术,主要实现了以下三个方面的功能:
    • 数据库中的表映射为Python中的类
    • 数据库中的字段映射为Python中的属性
    • 数据库中的记录映射为Python中的实例
  • ORM的主要优点是可以减少开发人员编写重复的SQL语句的时间和工作量,并且可以减少由于SQL语句的调整和更改所带来的错误。

(2)定义模型表

  • models.py 文件中定义模型表
from django.db import models
class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=20)
    age = models.IntegerField()
    register_time = models.DateField() # 年月日
    register_time1 = models.DateTimeField() # 年月日时分秒

(1)允许为空

class User(models.Model):
    name = models.CharField(max_length=50, null=True)  # 允许name字段为空

(2)指定默认值

class MyModel(models.Model):
    age = models.IntegerField(default=18)  # 默认年龄为18岁

(3)DateField两个重要参数

  • auto_now 每次操作数据的时候 该字段会自动将当前时间更新
  • auto_now_add 在创建数据的时候会自动将当前时间记录下来 之后只要不人为的修改 数据会一直保存
from django.db import models
class User(models.Model):
    register_time = models.DateField(auto_now = True ) # 年月日
    register_time1 = models.DateTimeField(auto_now_add = True ) # 年月日时分秒

(4)外键

  • 一对多ForeignKey
FK = models.ForeignKey(to='表名',on_delete=models.CASCADE)
  • 多对多ManyToManyField
MTM = models.ManyToManyField(to='表名')
  • 一对一OneToOneField
OTO = models.OneToOneField(to='表名',on_delete=models.CASCADE)

(5)总结

  • 以下是一些常用的语法
  1. 字段类型
    • CharField: 字符串字段
      • (max_length = )
    • TextField: 文本字段
    • IntegerField: 整数字段
    • FloatField: 浮点数字段
    • DecimalField: 十进制数字段
      • (max_digits= , decimal_places= )
    • BooleanField: 布尔字段
    • DateField: 日期字段
    • DateTimeField: 日期时间字段
    • TimeField: 时间字段
    • EmailField: 邮箱字段
    • FileField: 文件上传字段
    • ImageField: 图片上传字段
    • ForeignKey: 外键字段
    • ManyToManyField: 多对多关系字段
    • OneToOneField: 一对一关系字段
  2. 字段选项
    • verbose_name: 显示字段的名称
    • help_text:显示字段的说明
    • on_delete:级联更新级联删除
      • (on_delete=models.CASCADE)
    • max_length: 字符串字段的最大长度
    • default: 默认值
    • null: 数据库中字段是否可以为NULL
    • auto_now: 每次保存对象时自动更新为当前时间
    • auto_now_add: 创建对象时自动设置为当前时间

(4)数据库迁移

python manage.py makemigrations
python manage.py migrate

(5)ORM数据操作 单表查询

  • QuerySet对象索引取值或者循环取值
  • object对象可以用.取值

(1)增加 create

模型表名.objects.create(字段名=字段值)
User.objects.create(
    name="heart", 
    age=18,
    email="ssrheart@outlook.com"
)

(2)查询 all filter get

  • 获取到当前表中的全部数据 all()
模型表名.objects.all()
# <QuerySet [<Book: Book object (1)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>]>
  • 根据指定条件筛选数据 filter get
# 方式一
模型表名.objects.get(筛选字段名=筛选字段值)
# Book object (1) 取出来是对象 可以.取值

# 方式二
模型表名.objects.filter(筛选字段名=筛选字段值)
# <QuerySet [<Book: Book object (1)>]> 取出来是QuerySet对象 不能.取值 需要取值的话要在后面.first()

# 获取到第一个数据
模型表名.objects.filter(筛选字段名=筛选字段值).first()
# Book object (1) 取出来是对象 可以.取值
  • 去除指定条件的数据 exclude
模型表名.objects.exclude(筛选字段名=筛选字段值)
# 获取表中所有数据 QuerySet对象
user = User.objects.all()

# 根据特定条件筛选数据(方式一)
res = User.objects.get(name="heart")

# 根据特定条件筛选数据(方式二)
res = User.objects.filter(name="heart").first()

# 去除满足指定条件的数据
# 所有数据内过滤掉年龄等于18岁的记录 
exclude_res = User.objects.exclude(age=18)
# <QuerySet [<Author: Author object (2)>, <Author: Author object (3)>, <Author: Author object (4)>]>
  • 获取到一个字段的数据 values

  • 返回的数据格式为列表套字典 本质上是一个QuerySet对象,而不是真的列表

user_obj = models.User.objects.values("name")
# <QuerySet [{'name': 'heart'}, {'name': 'god'}]> 列表套字典
  • 获取到多个字段的数据 values_list 列表套元组
user_obj = models.User.objects.values_list("name","age")
print(user_obj) 
# <QuerySet [('heart', 18), ('god', 20)]> 列表套元组
  • 去重 distinct
user_obj = models.User.objects.values('name', 'age').distinct()
print(user_obj)
# <QuerySet [{'name': 'heart', 'age': 18}, {'name': 'god', 'age': 20}]>
  • 排序
  • 默认升序
user_obj = models.User.objects.order_by('age')
  • 降序
user_obj = models.User.objects.order_by('-age')
  • 反转
user_obj = models.User.objects.order_by('age').reverse()
  • 统计个数
user_obj = models.User.objects.count()
  • 是否存在
user_obj = models.User.objects.filter(name="heart").exists()

(3)更改 update

  • 方式一:先查询直接修改
模型表名.objects.filter(筛选字段名=筛选字段值).update(修改字段名=修改字段值)
User.objects.filter(age=18).update(age=24) 
  • 方式二:先查询后修改
obj = 模型表名.objects.get(筛选字段名=筛选字段值)
obj.修改字段名=修改字段值
obj.save()
# 更新已有记录的值,可先通过查询获取对象实例后调用 .save() 方法
res = User.objects.get(name="张三")
res.age = 26
res.save()

(4)删除 delete

  • 方式一:直接delete()
模型表名.objects.filter(筛选字段名=筛选字段值).delete()
User.objects.filter(username=username).delete()
  • 方式二:删除需要先查询再删除
res = User.objects.get(筛选字段名=筛选字段值)
res.delete()

(6)测试脚本&查看内部sql语句

from django.test import TestCase
import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day07.settings")

    import django
    django.setup()
    
    # 在下面书写我们需要测试的代码
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

(7)必知必会十三条

# 以下方法的前提均为 models.模型表名.objects 之后的方法
# 【0】新增数据
.create(字段名=字段值)
# 【1】查询全部数据
.all()
# 【2】带有筛选条件的过滤
.filter(筛选字段名=筛选字段值)
# 【3】获取筛选结果的第一条数据
.filter(筛选字段名=筛选字段值).first()
# 【4】获取筛选结果的最后一条数据
.filter(筛选字段名=筛选字段值).last()
# 【4.1】获取筛选后的结果进行修改
.filter(筛选字段名=筛选字段值).update(字段名=字段值)
# 【4.2】获取筛选后的结果进行删除
.filter(筛选字段名=筛选字段值).delete()
# 【5】根据指定条件获取数据对象,条件不存在或者数据大于2则会报错
.get(筛选字段名=筛选字段值)
# 【6】获取单个字段名所对应的数据
.values(字段名)
# 【7】获取多个字段名所对应的数据
.value_list(字段名1,字段名2)
# 【8】去重,拿到指定字段的数据后,对筛选出的数据进行去重
.values(字段名1,字段名2).distinct()
# 【9】排序
# 正序
.order_by(字段名)
# 倒序
.order_by(-字段名)
# 【10】反转的已经经过排序过的数据
.order_by(字段名).reverse()
# 【11】统计当前数据对象的个数
.count()
# 【12】排除指定条件的数据
.exclude(字段名=字段值)
# 【13】判断符合当前条件的数据是否存在
.filter(筛选字段名=筛选字段值).exists()

# 补充:查看当前ORM语句的SQL查询语句
# 注意可以使用此方法的必须是 QuerySet 对象
.query

(8)双下划线查询

(1)条件大于__gt

  • 年龄大于35岁的数据
res = models.User.objects.filter(age__gt=35)
print(res)

(2)条件小于__lt

  • 年龄小于35岁的数据
res = models.User.objects.filter(age__lt=35)
print(res)

(3)条件大于等于__gte

  • 年龄大于等于35岁的数据
res = models.User.objects.filter(age__gte=35)
print(res)

(4)条件小于等于__lte

  • 年龄小于等于35岁的数据
res = models.User.objects.filter(age__lte=35)
print(res)

(5)或条件__in

  • 年龄是18或者32或者40
res = models.User.objects.filter(age__in=(18, 32, 40))
print(res)

(6)两个条件之间__range

  • 年龄是18-40之间
  • 首尾都要
res = models.User.objects.filter(age__range=(18, 40))
print(res)

(7)模糊查询__contains

  • 查询出名字中含有 n 的数据 -- 模糊查询
res = models.User.objects.filter(name__contains='n')
print(res)
  • 默认区分大小写
res = models.User.objects.filter(name__contains='N')
print(res)
  • 忽略大小写__icontains
res = models.User.objects.filter(name__icontains='N')
print(res)

(8)以指定条件开头/结尾

  • 以什么开头/结尾__startswith
res = models.User.objects.filter(name__startswith='d')
print(res)
  • 以什么结尾__endswith
res = models.User.objects.filter(name__endswith='m')
print(res)

(9)查询时间日期

  • 查询出注册时间是2020年1月份的数据/年/月/日
res = models.User.objects.filter(register_time__month='1')
print(res)
res = models.User.objects.filter(register_time__year='2020')
print(res)
res = models.User.objects.filter(register_time__day='28')
print(res)

(9)ORM数据操作 多表查询

  • 创建模型表
from django.db import models

# Create your models here.

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)

    # 一对多
    publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
    # 多对多
    authors = models.ManyToManyField(to='Author')

class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    email = models.EmailField()

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    # 一对一
    author_detail = models.OneToOneField(to='Author_Detail',on_delete=models.CASCADE)

class Author_Detail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=40)
python manage.py makemigrations
python manage.py migrate

(1)一对多外键增删改

(1)增 create

  • 直接写实际 字段id
models.Book.objects.create(title='黑色柳丁', price=99.99, publish_id=1)
  • 虚拟字段 对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='蝴蝶', price=999.99,publish=publish_obj)

(2)删 delete

  • 级联更新 级联删除
models.Book.objects.filter(pk=6).delete()

(3)修改 update

  • 直接写实际 字段id
models.Book.objects.filter(pk=2).update(publish_id=2)
  • 虚拟字段 对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=2).update(publish=publish_obj)

(2)多对多外键删改

  • 多对多,增删改查,其实就是在操作第三张表

(1)增 add

  • 如何给书籍添加作者?
book_obj = models.Book.objects.filter(pk=8).first()
book_obj.authors.add(1,2) # 书籍id为8的书籍绑定主键为1和2的作者

# 也支持先查出来作者的(id)对象再add添加
    book_obj = models.Book.objects.filter(pk=2).first()
    author_obj = models.Author.objects.filter(pk=1).first()
    author_obj1 = models.Author.objects.filter(pk=2).first()
    author_obj2 = models.Author.objects.filter(pk=3).first()
    book_obj.authors.add(author_obj)
    book_obj.authors.add(author_obj1,author_obj2)
# 书籍id为2的书籍绑定主键为1,2,3的作者

image

总结:

​ add给第三张关系表添加数据

​ 括号内既可以传id也可以传对象 并且都支持多个

(2)删 remove

  • 删除书籍id为2,作者id为2的绑定关系
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.remove(2)

# 也可以支持放对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
book_obj.authors.remove(author_obj,author_obj1)

总结:

​ remove给第三张关系表删除数据

​ 括号内既可以传id也可以传对象 并且都支持多个

(3)修改 set

  • 给书籍id为8的书籍修改为作者id是1和2的作者
book_obj = models.Book.objects.filter(pk=8).first()
book_obj.authors.set([1,2])

# 也可以支持放对象
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set([author_obj,author_obj1])

总结:

​ set括号内必须传一个可迭代对象,该对象内既可以传id也可以传对象 并且都支持多个

(4)清空 clear

  • 在第三张关系表中清空某个书籍与作者的绑定关系
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.clear()
# 清空了书籍id为2的所有绑定关系

总结:

​ clear括号内不要加任何参数

(3)正反向的概念

  • 其实就是看外键字段在哪里

(1)正向

  • 外键字段在我手上,我查你就是正向
# 比如书查出版社
book >>> publish (外键字段在书里面)就是正向

(2)反向

  • 外键字段不在手上,我查你就是反向
# 比如出版社查书
publish >>> book (外键字段在书里面)就是反向

正向查询按字段

反向查询按表名小写 _set

(4)多表查询

(1)子查询(基于对象的跨表查询)

  • 查询书籍主键为2的出版社名称
book_obj = models.Book.objects.filter(pk=2).first()
res = book_obj.publish  # 获取出版社对象
print(res.name)  # 北方出版社
  • 查询书籍主键为8的作者
  • 这个地方如果查询出来如果有多个结果,一定要使用all()
    • 不然会出现booksystem01.Author.None这种情况
  • 然后使用for循环遍历结果取出想要的值
book_obj = models.Book.objects.filter(pk=8).first()
# res = book_obj.authors  # booksystem01.Author.None
res = book_obj.authors.all()
print(res)  # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>
  • 查询作者heart的电话号码
author_obj = models.Author.objects.filter(name='heart').first()
res = author_obj.author_detail # 获取作者对象
print(res.phone) # 110
  • 查询出版社是东方出版社出版的书
  • 出版社查书是反向 表名小写加_set
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set.all()
print(res)  # <QuerySet [<Book: Book object (3)>, <Book: Book object (5)>, <Book: Book object (7)>]>
  • 查询作者是heart写过的书
author_obj = models.Author.objects.filter(name='heart').first()
res = author_obj.book_set.all()
  • 查询手机号是110的作者姓名
# author_detail_obj = models.Author_Detail.objects.filter(phone=110).first()
res = author_detail_obj.author 
# 当查询结果只有一个的时候 不需要加_set.all()

反向查询的时候:

​ 当查询结果有多个的时候 就必须加_set.all()

​ 当查询结果只有一个的时候 不需要加_set.all()

(2)联表查询(基于下划线的跨表查询)

  • 查询heart的手机号
res = models.Author.objects.filter(name='heart').values('author_detail__phone')
print(res) # <QuerySet [{'author_detail__phone': 110}]>

# 反向
res = models.Author_Detail.objects.filter(author__name='heart').values('phone','author__name')
print(res) # <QuerySet [{'phone': 110, 'author__name': 'heart'}]>
  • 查询书籍主键为2的出版社名称和书的名称
res = models.Book.objects.filter(pk=2).values('publish__name','title')
print(res) # <QuerySet [{'publish__name': '北方出版社', 'title': '红楼梦'}]>

# 反向
res = models.Publish.objects.filter(book__id=2).values('name', 'book__title')
print(res) # <QuerySet [{'name': '北方出版社', 'book__title': '蝴蝶'}]>
  • 查询书籍主键为2的作者姓名
res = models.Book.objects.filter(pk=2).values('authors__name')
print(res) # <QuerySet [{'authors__name': 'heart'}, {'authors__name': 'god'}]>

# 反向
res = models.Author.objects.filter(book__id=2).values('name')
print(res) # <QuerySet [{'name': 'heart'}, {'name': 'god'}]>
  • 查询书籍主键是2的作者的手机号和姓名
res = models.Book.objects.filter(pk=2).values('authors__author_detail__phone')
print(res) # <QuerySet [{'authors__author_detail__phone': 110}, {'authors__author_detail__phone': 120}]>

(5)聚合查询 aggregate

  • 只要是跟数据库相关的模块 基本都在django.db.models里面
  • 如果上述没有那么应该在django.db里面
  • from django.db.models import Max,Min,Count,Sum,Avg
from django.db.models import Max,Min,Count,Sum,Avg
  • 所有书的平均价格
from django.db.models import Max,Min,Count,Sum,Avg
res = models.Book.objects.aggregate(Avg('price'))
print(res) # {'price__avg': Decimal('16959.253333')}
  • 一次性使用
res = models.Book.objects.aggregate(Max('price'),Min('price'),Count('pk'),Sum('price'))
print(res) # {'price__max': Decimal('99999.99'), 'price__min': Decimal('99.99'), 'pk__count': 6, 'price__sum': Decimal('101755.52')}

(6)分组查询 annotate

  • 需要搭配聚合查询使用

    • from django.db.models import Max,Min,Count,Sum,Avg
  • models后面点什么,就是按什么分组

  • 统计每一本书的作者个数

from django.db.models import Max,Min,Count,Sum,Avg
res = models.Book.objects.annotate(author_num=Count('authors')).values('title','author_num')
print(res)
  • 统计每个出版社卖的最便宜的书的价格
from django.db.models import Max,Min,Count,Sum,Avg
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)
  • 统计不止一个作者的图书
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1)
print(res) # <QuerySet [<Book: Book object (2)>, <Book: Book object (4)>, <Book: Book object (8)>]>
  • 查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)

(7)F查询

  • 能直接获取到某个字段对应的数据
from django.db.models import F
  • 查询卖出数大于库存数的书籍
res = models.Book.objects.filter(maichu__gt=F('kucun'))
print(res)
  • 将所有书籍的价格提升500块
models.Book.objects.update=(price=F('price') + 500)
  • 将所有书的名字后面加上爆款两个字
    • 在操作字符类型的数据的时候 F不能够直接做到字符串的拼接 需要借助Concat 和 Value
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('爆款')))

(8)Q查询

  • Q查询允许以一种更简洁和易读的方式来构建复杂的查询条件

  • from django.db.models import Q

  • 查询卖出数大于100或者价格小于600的书籍

filter括号内多个参数是and关系
res = models.Book.objects.filter(maichu__gt=100,price__lt=600)

res = models.Book.objects.filter(Q(maichu__gt=100), Q(price__lt=600))  # , and关系
res = models.Book.objects.filter(Q(maichu__gt=100) | Q(price__lt=600))  # | or关系
res = models.Book.objects.filter(~Q(maichu__gt=100) | Q(price__lt=600))  # ~ not关系

(1)Q的高阶用法

  • 能够将查询条件的左边也变成字符串的形式
  • 查询卖出数大于100或者价格小于600的书籍
q = Q()
q.connector = 'or'
q.children.append(('maichu__gt', 100))
q.children.append(('price__lt', 600))
res = models.Book.objects.filter(q) # 默认是and关系
print(res)

(9)事务

  • ACID
    • 原子性
    • 一致性
    • 隔离性
    • 持久性
from django.db import transaction
with transaction.atomic():
    # sql1
    # sql2
    # 在with代码块内书写的所有orm操作都是属于同一个事务
posted @ 2024-03-11 21:29  ssrheart  阅读(4)  评论(0编辑  收藏  举报