Django学习笔记(三)ORM相关操作

1. ORM

1.1 概念

对象关系映射,Object Relational Mapping

1.2 映射关系

ORM数据库
数据表
对象数据行
属性字段

在代码层面来看,每一个类都将被映射到数据库中对应的一张表中。每一个实例都将对应一行数据,而对应的属性就是数据表中的字段。

1.3 准备工作

必须先在settings.py文件中配置好数据库。
本次使用mysql数据库,如果无法使用MySQLdb模块,可以安装pymysql,在__init__.py文件中:

import pymysql
pymysql.install_as_MySQLdb()

1.4 使用流程

  1. 创建好对应的model
  2. 使用脚本自动生成对应的数据表
  3. 通过特定语法增删改查

2. model的创建

如同类的声明,在app下的models.py中声明

class Publisher(models.Model):
    name = models.CharField(max_length=32)      #会对应生成varchar(32)字段

3. ORM常用字段类型

3.1 AutoField

int型自增字段,必须加上primary_key=True 即设置主键

id = models.AutoField(primary_key=True) 

3.2 BigAutoField

bigint型自增字段,必须加上primary_key=True属性

3.3 IntegerField

从 -2^31 (-2,147,483,648) 到 2^31 - 1 (2,147,483,647) 的整型

3.4 CharField

字符类型,必须填入max_length参数,max_length即长度

 name = models.CharField(max_length=32) 

3.5 DateField

日期

3.6 DateTimeField

日期与时间
日期字段特有属性:

  1. auto_now_add 新增数据时,自动保存当前时间
  2. auto_now 每次新增和修改时,自动保存当前时间
  3. default 默认值,不填的时候填充默认值

上述三个属性互斥

3.7 BooleanField

布尔值

3.8 NullBooleanField

可空布尔值

3.9 TextField

长文本

3.10 FloatField

浮点型

3.11 DecimalField

参数:

  1. max_digits: 总长度
  2. decimal_places:小数位长度
rate = models.DecimalField(max_digits=10, decimal_places=4)

4. 外键关系

   class Book(models.Model):
       name = models.CharField(max_length=32)
       publisher_id = models.ForeignKey('Publisher',on_delete=models.CASCADE)
       # 会自动给外键加'_id'后缀
   
       # on_delete(2.0版本改为必填)的可选项:
       #     models.CASCADE  级联删除,主表删除后子表对应数据也删除
       #     models.PROTECT  保护模式,如果主表对应子表中还有数据,则不允许删除
       #     models.SET(v)   主表删除时,外键设置为某值
       #     models.SET_DEFAULT(v)   主表删除时,外键设置为默认值,需要在创建时加入default属性
       #     models.SET_NULL()   主表删除时,外键设置为NULL
       #     modesl.DO_NOTHING() 什么都不做

通过ForeignKey直接引入对应的类名,即可绑定数据表之间的外键关系

5. 自动生成数据表

将目录切换到项目目录下,通过终端执行如下语句:

   python manage.py makemigrations  	# 生成变更文件
   python manage.py migrate				# 将变更同步至数据库中

执行后将会根据model中声明的结构,自动创建对应的数据表。(上述命令只会查找在settings中注册的应用)

6. 实际操作

models.py

class Publisher(models.Model):
	name = models.CharField(max_length=32) #varchar(32)
  
class Book(models.Model):
  name = models.CharField(max_length=32) #varchar(32)
  pub = models.ForeignKey('Publisher', on_delete=models.CASCADE)

查:

from app01 import models
# 获取所有的数据 QuerySet 对象列表
models.Publisher.objects.all()

# 获取存在且唯一(不唯一则报错)的数据 对象
models.Publisher.objects.get()

# 获取多条数据 对象列表 不会报错
models.Publisher.objects.filter()

ret = models.Book.objects.all() #对象列表
for book in ret:
  print(book)
  print(book.id/book.pk)
  print(book.name)
  print(book.pub) #书籍相关的出版社的对象
  print(book.pub_id) #书籍相关的出版社的ID

新增:

from app01 import models
models.Publisher.objects.create(name='xxx')

models.Book.objects.create(name='xxx',pub=出版社的对象)
models.Book.objects.create(name='xxx',pub_id=出版社的对象的ID)

删除

models.Publisher.objects.get(pk=1).delete()
models.Publisher.objects.filter(pk=1).delete() # 批量删除

编辑

方式一:
book_obj.name = 'xxx'
book_obj.pub = 出版社对象
book_obj.pub_id = 出版社id
book_obj.save() # 保存到数据库

方式二:
models.Book.objects.filter(pk=1).update(name='xx', pub_id=出版社的id) # 相当于批量更新,且仅更新修改的字段,上面的方法会整行更新

使用第一种,相当于更新book表的全部字段

使用方式二,只会更新赋值的字段,且不用save,并且可以批量更新。

Objects.get()无法调用update方法

7. 自定义字段的方式

   类型定义:
   class MyCharField(models.Field):
   
       def __init__(self, max_length, *args, **kwargs):
           self.max_length = max_length
           super(MyCharField, self).__init__(max_length=max_length,*args, **kwargs)
   
       def db_type(self, connection):
           # 限定数据库内生成的字段类型是char 长度为设置的max_length
           return 'char(%s)' % self.max_length
   声明:
   class Person(models.Model):
       phone = MyCharField(max_length=11)

8. models字段常用的参数

# 该字段在数据库中可以为空
null=True
# Django admin 提交时可空 因此上述两条应该同时使用
blank=True
# 数据库中字段列名指定(与python中使用时的名称不一定一致,仅设置数据库列名)
db_column='xx'
# 默认值
default = xx
# 索引
db_index=True
# 唯一约束
unique=True
# admin管理平台显示的字段名称(可为中文)
verbose_name='xx'
# 给用户显示可选的参数
choices
# models.BooleanField(choices=((True,'男'),(False,'女')))
# 加上choices后变为下拉列表,加之前是单选框

9. models表的常用参数

 # 在对应Model中添加,注意缩进!!!
 class Meta:
         # 数据库中生成的表名 默认:app名称_类名
         db_table = "person"
         # admin管理平台展示的表名
         verbose_name = '个人信息'
         # 原先界面显示的verbose_name 加 s
         verbose_name_plural = '所有用户信息'
 
         # 联合索引
         index_together= [
             # 两个存在的字段
             ("name","age")
         ]
         
         # 联合唯一索引 加入以后对应字段组合唯一
         unique_together = (("name","age"),)

10. 查询实践

 # -*- coding = utf-8 -*-
 # @Time     :2021/1/3 下午8:08
 # @Author   :Asher
 # @File     :1.必知必会13条.py
 # @SoftWare :PyCharm
 import os
 import django
 # 如此声明可以直接在一个py文件中进行orm相关调试
 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
 django.setup()
 from app01 import models
 
 # all 查询所有数据        QuerySet     对象列表       【对象,一行数据】
 ret = models.Person.objects.all()
 # get 获取一个有且唯一的数据  没有或者返回多个的时候会报错
 ret = models.Person.objects.get(pk=2)
 # print 的时候调用的是 __str__ 方法,但是如果是输出列表,展示了列表中的元素,调用的是__repr__ 方法
 # 可以自己重写__repr__ 也可以直接写 __repr__=__str__ 不能带空格  这样可直接输出__str__内容
 
 # filter 获取满足条件的数据  QuerySet    对象列表    【对象,一行数据】  返回满足条件的0到任意条数据
 ret = models.Person.objects.get(pk=2)
 
 # order_by('字段名') 排序(默认升序)
 # order_by('-字段名') 降序排序
 # order_by('字段名1','字段名2')  支持多字段排序
 ret = models.Person.objects.filter(age=2).order_by('age')
 
 # reverse   对已经排序的对象列表做反转,如果没有做过排序,则无效
 ret = models.Person.objects.filter(age=2).order_by('age').reverse()
 
 # values()    QuerySet    [{}]   对象列表中每一个对象都是字典
 # 不指定字段,不传参获取数据所有的字段名和值
 # 如果传参,传入对应的字段名,则字典内容只会查出对应的字段
 ret = models.Person.objects.all().values('pid')
 
 # values_list()     QuerySet    [()]    对象列表中每个对象都是元组  没有字段名
 # 不指定字段,不传参获取数据所有的值
 # 如果传参,传入对应的字段名,则字典内容只会查出对应的字段
 ret = models.Person.objects.all().values_list()
 
 # distinct() 去重 可以配合values()获取指定字段进行去重
 ret = models.Person.objects.all().distinct()
 
 # count() 计数   len() 同样可以计数,但count方法效率更高
 ret = models.Person.objects.all().count()
 ret = len(models.Person.objects.all())
 
 # first() 获取第一个元素
 ret = models.Person.objects.all().first()
 
 # last() 获取第一个元素
 ret = models.Person.objects.all().last()
 
 # exists() 判断是否有结果
 ret = models.Person.objects.filter(pk=2).exists()
 
 # exclude() 获取不满足条件的数据
 ret = models.Person.objects.exclude(pk=2)
 
 print(ret)
 
 """
 根据返回值不同类型划分:
 返回对象列表
     all
     filter
     exclude
     order_by
     reverse
     values          [{},{}]
     values_list     [(),()]
     distinct
 返回对象
     get
     first
     last
 返回数字
     count   
 返回布尔值
     exists
 """

11. 数据比较方法

 # 大于 great than
 models.Person.object.filter(id__gt=4)
 # 小于 less than
 models.Person.object.filter(id__lt=4)
 # 大于等于 great than equal
 models.Person.object.filter(id__gte=4)
 # 小于等于
 models.Person.object.filter(id__lte=4)
 
 # 范围,左右都包含
 models.Person.object.filter(id__range=[1,4])
 # 成员判断
 models.Person.object.filter(id__in=[1,4])
 
 # like
 models.Person.object.filter(name__contains='alex')
 # like 忽略大小写
 models.Person.object.filter(name__icontains='alex')
 
 # 以。。开头
 models.Person.object.filter(name__startswith='alex')
 # 以。。开头忽略大小写
 models.Person.object.filter(name__istartswith='alex')
 
 # 以。。结尾
 models.Person.object.filter(name__endswith='alex')
 # 以。。结尾忽略大小写
 models.Person.object.filter(name__iendswith='alex')
 
 # 年份筛选
 models.Person.object.filter(birth__year='2020')
 # 不支持月份筛选,因此可使用contains做过滤
 models.Person.object.filter(birth__contains='2020-02')
 
 # 校验字段呢是否为null
 models.Person.object.filter(name__isnull=True)

12. 有关外键的查询

    #model
    class Publisher(models.Model):
    	name=models.CharField(max_length=32)
    	
    class Book(models.Model):
    	name=models.CharField(max_length=32)	 pub=models.ForeginKey('Publisher',on_delete=models.CASCADE,related_name='books') # pub_id
   # 基于对象的查询
   book_obj=Book.objects.get(pk=1)
   # 正向查询
   book_obj.pub		# 所关联的对象
   book_obj.pub_id	# 所关联对象的id
   
   #反向查询
   pub_obj=Publisher.objects.get(pk=1)
   
   # 不指定related_name时,使用类名小写_set
   pub_obj.book_set  # 关系管理对象
   pub_obj.book_set.all()	# 关联的所有对象
   
   # 指定related_name='books'后(在models文件中指定)
   pub_obj.books.all()
   
   # 基于字段的查询
   ret=Book.objects.filter(name='xxx')
   ret=Book.objects.filter(pub__name='出版社')
   
   ret=Publisher.objects.filter(pk=1)
   # 不指定related_name
   ret=Publisher.objects.filter(book__name='书名')
   # 指定related_name='books'后
   ret=Publisher.objects.filter(books__name='书名')
   # 指定related_query_name='xxx'后 优先级最高
   ret=Publisher.objects.filter(xxx__name='书名')

13. 多对多关系的操作

    #model
    class Book(models.Model):
    	name=models.CharField(max_length=32)	 pub=models.ForeginKey('Publisher',on_delete=models.CASCADE,related_name='books') # pub_id
      
    class Author(models.Model):
      name=models.CharField(max_length=32)
      books=models.ManyToManyField('Book')
 author_obj = models.Author.filter(pk=1)
 author_obj.books    # 关系管理对象
 # all 查询所有关联对象
 author_obj.books.all()	
 
 # set 设置关系	[1,2]  [对象,对象]
 author_obj.books.set([1,2])# 传入[]=删除所有关系
 
 # add 新增关系 id  或 对象
 author_obj.books.add(1,2)
 
 # remove 删除关系 id 或 对象
 author_obj.books.remove(1,2)
 
 # clear 清空关系
 author_obj.clear()
 
 # create 新增一个所关联的对象,并和当前对象绑定关系
 author_obj.books.create(name='xxx',pub_id=1)
 
 # 一对多关系中反向查询获取到的关系管理对象
 # 1.参数中只能使用对象,不能传入id
 # 2.当ForginKey中有参数null=True时,才能使用remove和clear操作。
 # 因为删除关系相当于把外键中绑定的id置为Null,字段如果不允许为null,就不允许使用此方法

14. 聚合和分组

 # aggregate(聚合) 终止子句 因为查询结果已经变为字典,无法再使用.xxx(filter之类的)做相关查询
 # 使用aggregate时可以不使用all()方法,默认当前表所有数据,不过可以使用filter先做一次过滤
 ret = models.Book.objects.all().aggregate(Max('price')) # {'price__max': Decimal('999.00')}
 ret = models.Book.objects.all().aggregate(Min('price')) # {'price__min': Decimal('9.90')}
 ret = models.Book.objects.all().aggregate(Max('price'),Min('price')) # {'price__max': Decimal('999.00'), 'price__min': Decimal('9.90')}
 ret = models.Book.objects.all().aggregate(max=Max('price'),min=Min('price')) # {'max': Decimal('999.00'), 'min': Decimal('9.90')}
 print(ret)
 
 ## 分组 group by
 ## annotate 注释 过程中使用了分组,将聚合后的信息以注释的形式添加进了数据结果组里
 ## 统计每本书的作者个数
 ret = models.Book.objects.annotate(Count('authors')).values()
 
 ## 统计每个出版社最便宜的书的价格
 ## 方法一:
 ret = models.Publisher.objects.annotate(Min('book__price')).values()
 for i in ret:
     print(i)
 
 ## 方法二:
 ret = models.Book.objects.annotate(Min('price')).values()   # 错误写法 按照书进行分组了
 ## 按照pub、pub__name、name 三个条件做了分组
 ret = models.Book.objects.values('pub','pub__name','name').annotate(Min('price'))
 for i in ret:
     print(i)
 
 ## 统计不止一个作者的书
 ret = models.Book.objects.annotate(count=Count('authors')).filter(count__gte=2)
 for i in ret:
     print(i)
 
 ## 根据一本书的作者数量做排序
 ret = models.Book.objects.annotate(count=Count('authors')).values().order_by('-count')
 print(ret)
 
 ## 求各个作者作书的总价格
 ## 方法一
 ret = models.Author.objects.annotate(sum=Sum('books__price')).values()
 
 ## 方法二
 ret = models.Book.objects.values('authors__id','authors__name').annotate(sum=Sum('price'))
 print(ret)

15. F查询和Q查询

 # -*- coding = utf-8 -*-
 # @Time     :2021/1/17 下午3:42
 # @Author   :Asher
 # @File     :6.F查询和Q查询.py
 # @SoftWare :PyCharm
 
 import os
 import django
 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
 
 django.setup()
 from app01 import models
 from django.db.models import F,Q
 
 # ret = models.Book.objects.filter(kucun__lt=50)
 # 相当于 where sale > kucun
 ret = models.Book.objects.filter(sale__gt=F('kucun'))
 ret = models.Book.objects.filter(id__lte=3).update(sale=F('sale') * 2 - 3)
 
 ## 求id<3或id>5的数据
 ret = models.Book.objects.filter(id__lt=3,id__gt=5)# 错误写法,相当于id<3且id>5
 ret = models.Book.objects.exclude(id__gte=3,id__lte=5)# 正确写法,取id大于等于3且小于等于5的结果取反
 # 使用Q查询 使用,连接时,依旧表示且的关系,想表示或的关系需要使用|连接
 ret = models.Book.objects.filter(Q(id__lt=3)|Q(id__gt=5))
 # Q查询可嵌套
 ret = models.Book.objects.filter(Q(Q(id__lt=3)|Q(id__gt=5)),name__startswith='新')
 # 两个Q查询中间可以用&符号连接
 ret = models.Book.objects.filter(Q(Q(id__lt=3)|Q(id__gt=5))&Q(name__startswith='新'))
 
 # | 表示或  &表示与  ~表示非
 
 print(ret)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值