模型层(ORM语法):跟数据库打交道的

补充知识

  1. # django自带的sqlite3数据库对日期格式不是很敏感 处理的时候容易出错

测试脚本创建

  1. """
  2. 当你只是想测试django中的某一个py文件内容 那么你可以不用书写前后端交互的形式
  3. 而是直接写一个测试脚本即可
  4. 脚本代码无论是写在应用下的tests.py还是自己单独开设py文件都可以
  5. """
  6. # 测试环境的准备 去manage.py中拷贝前四行代码 然后自己再写两行
  7. import os
  8. import sys
  9. if __name__ == "__main__":
  10. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  11. import django
  12. django.setup()
  13. # 在这个代码块的下面就可以测试django里面的单个py文件了

实例:

test.py

  1. import os
  2. if __name__ == "__main__":
  3. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  4. import django
  5. django.setup()
  6. # 所有的代码都必须等待环境准备完毕之后才能书写
  7. from app01 import models # 注意这行导入不能放在main上面导入,一定要在django.setup()下方写,因为要等上面测试环境完成
  8. models.User.objects.all()

单表操作

环境准备:

数据库

Django之模型层 - 图1

单表操作数据增删改查示例

models.py

  1. from django.db import models
  2. # Create your models here.
  3. class User(models.Model):
  4. name = models.CharField(max_length=32)
  5. age = models.IntegerField()
  6. register_time = models.DateField() # 日期字段,表示年月日,models.DateTimeField()表示年月日,时分秒
  7. """
  8. 针对DateField()和DateTimeField()两个重要参数:
  9. auto_now=True:每次操作数据的时候,该字段会自动将当前时间更新
  10. auto_now_add=True:在创建数据的时候,会自动将当前创建时间记录下来,之后只要不人为的修改,那么就一直不变
  11. """
  12. def __str__(self):
  13. return self.name

test.py

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 单表操作
  10. # 增
  11. # 方法一:
  12. # res = models.User.objects.create(name="jason",age=18,register_time="2020-5-29")
  13. # print(res) # 返回值是创建对象本身
  14. # 方法二:
  15. # import datetime
  16. # ctime = datetime.datetime.now()
  17. # user_obj = models.User(name="egon",age=99,register_time=ctime) # 日期格式还支持直接传日期对象
  18. # user_obj.save()
  19. # 删
  20. # 方法一:
  21. # res = models.User.objects.filter(pk=2).delete()
  22. # print(res) # 返回值是当前命令所影响的行数
  23. # 补充,以前是用id来取,这里pk会自动查找到当前表的主键字段,指代的就是当前表的主键,用了pk之后,就不需要知道当前表的主键字段到底叫什么
  24. #
  25. # 方法二:
  26. # user_obj = models.User.objects.filter(pk=1).first()
  27. # user_obj.delete()
  28. # 修改
  29. # 方法一:
  30. # models.User.objects.filter(pk=1).update(age=22)
  31. #
  32. # 方法二:
  33. user_obj = models.User.objects.filter(pk=2).first()
  34. user_obj.age = 66
  35. user_obj.save()
  36. #
  37. """
  38. ps:
  39. user_obj = models.User.objects.get(pk=4)
  40. get方法返回的直接就是当前数据对象
  41. 但是该方法不推荐使用
  42. 一旦数据不存在该方法会直接报错
  43. 而filter则不会
  44. 所以我们还是用filter
  45. """

单表操作必知必会13条:

test.py

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 必知必会13条
  10. # 1.all() 查询所有数据
  11. # 2.filter() 带有过滤条件的查询
  12. # 3.get() 直接拿数据对象 但是条件不存在直接报错
  13. # 4.first() 拿queryset里面第一个元素
  14. # res = models.User.objects.all().first()
  15. # print(res)
  16. # 5.last()
  17. # res = models.User.objects.all().last()
  18. # print(res)
  19. # 6.values() 可以指定获取的数据字段 类似数据库select name,age from User;语句 列表套字典
  20. # res = models.User.objects.values('name','age') # <QuerySet [{'name': 'jason', 'age': 18}, {'name': 'egonPPP', 'age': 84}]>
  21. # print(res)
  22. # 7.values_list() 列表套元祖
  23. res = models.User.objects.values_list('name','age') # <QuerySet [('jason', 18), ('egonPPP', 84)]>
  24. print(res)
  25. """
  26. res = models.User.objects.values_list('name','age') # <QuerySet [('jason', 18), ('egonPPP', 84)]>
  27. print(res.query) # SELECT `app01_user`.`name`, `app01_user`.`age` FROM `app01_user`
  28. # 查看内部封装的sql语句
  29. 上述查看sql语句的方式 只能用于queryset对象
  30. 只有queryset对象才能够点击query查看内部的sql语句
  31. """
  32. # 8.distinct() 去重
  33. # res = models.User.objects.values('name','age').distinct()
  34. # print(res)
  35. """
  36. 去重一定要是一模一样的数据
  37. 如果带有主键那么肯定不一样 你在往后的查询中一定不要忽略主键
  38. """
  39. # 9.order_by()
  40. # res = models.User.objects.order_by('age') # 默认升序
  41. # res = models.User.objects.order_by('-age') # 降序
  42. #
  43. # print(res)
  44. # 10.reverse() 反转的前提是 数据已经排过序了 order_by()
  45. # res = models.User.objects.all()
  46. # res1 = models.User.objects.order_by('age').reverse()
  47. # print(res,res1)
  48. # 11.count() 统计当前数据的个数
  49. # res = models.User.objects.count()
  50. # print(res)
  51. # 12.exclude() 排除在外
  52. # res = models.User.objects.exclude(name='jason')
  53. # print(res)
  54. # 13.exists() 基本用不到因为数据本身就自带布尔值 返回的是布尔值
  55. # res = models.User.objects.filter(pk=10).exists()
  56. # print(res)

补充:查看内部sql语句的方法

  1. # 方式1
  2. res = models.User.objects.values_list('name','age') # <QuerySet [('jason', 18), ('egonPPP', 84)]>
  3. print(res.query)
  4. queryset对象才能够点击query查看内部的sql语句
  5. # 方式2:所有的sql语句都能查看
  6. # 去配置文件中配置一下即可
  7. LOGGING = {
  8. 'version': 1,
  9. 'disable_existing_loggers': False,
  10. 'handlers': {
  11. 'console':{
  12. 'level':'DEBUG',
  13. 'class':'logging.StreamHandler',
  14. },
  15. },
  16. 'loggers': {
  17. 'django.db.backends': {
  18. 'handlers': ['console'],
  19. 'propagate': True,
  20. 'level':'DEBUG',
  21. },
  22. }
  23. }

神奇的双下划线查询

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 神奇的双下划线查询
  10. # 练习题
  11. # 1、年龄大于35岁的数据
  12. # res = models.User.objects.filter(age__gt=35)
  13. # print(res)
  14. # 2、 年龄小于35岁的数据
  15. # res = models.User.objects.filter(age__lt=35)
  16. # print(res)
  17. # 大于等于 小于等于
  18. # res = models.User.objects.filter(age__gte=32)
  19. # print(res)
  20. # res = models.User.objects.filter(age__lte=32)
  21. # print(res)
  22. # 年龄是18 或者 32 或者40
  23. # res = models.User.objects.filter(age__in=[18,32,40])
  24. # print(res)
  25. # 年龄在18到40岁之间的 首尾都要
  26. # res = models.User.objects.filter(age__range=[18,40])
  27. # print(res)
  28. # 查询出名字里面含有s的数据 模糊查询 ps:默认区分大小写
  29. # res = models.User.objects.filter(name__contains='s')
  30. # print(res)
  31. #
  32. # 查询出名字里面含有p的数据 默认区分大小写
  33. # res = models.User.objects.filter(name__contains='p')
  34. # print(res)
  35. # 忽略大小写
  36. # res = models.User.objects.filter(name__icontains='p')
  37. # print(res)
  38. # res = models.User.objects.filter(name__startswith='j')
  39. # res1 = models.User.objects.filter(name__endswith='j')
  40. #
  41. # print(res,res1)
  42. # 查询出注册时间是1月的(按月份找)
  43. # res = models.User.objects.filter(register_time__month='1')
  44. # 查询出注册时间是2020年的(按年份找)
  45. # res = models.User.objects.filter(register_time__year='2020')
  46. # 查询出注册时间是29号的(按日找)
  47. res = models.User.objects.filter(register_time__day="29")
  48. print(res)

多表操作

以图书管理系统为例:

环境:

models.py

  1. from django.db import models
  2. # Create your models here.
  3. class Book(models.Model):
  4. title = models.CharField(max_length=32)
  5. price = models.DecimalField(max_digits=8,decimal_places=2)
  6. publish_date = models.DateField(auto_now_add=True)
  7. # 一对多
  8. publish = models.ForeignKey(to="Publish")
  9. # 多对多
  10. authors = models.ManyToManyField(to="Author")
  11. class Publish(models.Model):
  12. name = models.CharField(max_length=32)
  13. addr = models.CharField(max_length=64)
  14. email = models.EmailField()
  15. # varchar(254) 该字段不是给models看的,而是给后面我们会学到的校验型组件看的
  16. class Author(models.Model):
  17. name = models.CharField(max_length=32)
  18. age = models.IntegerField()
  19. # 一对一
  20. author_detail = models.OneToOneField(to="AuthorDetail")
  21. class AuthorDetail(models.Model):
  22. phone = models.BigIntegerField() # 电话号码用BigIntegerField或者直接用CharField
  23. addr = models.CharField(max_length=64)

给三给表先写点数据

Django之模型层 - 图2

一对多的外键增删改查

test.py

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 一对多的外键增删改查 以Book表为例
  10. # 增
  11. # 方法一:直接写实际字段 id,,Book表中publish_date字段可以不给,创建时会自动写入创建时间
  12. # models.Book.objects.create(title='论语',price=899.23,publish_id=1)
  13. # models.Book.objects.create(title='聊斋',price=444.23,publish_id=2)
  14. # models.Book.objects.create(title='老子',price=333.66,publish_id=1)
  15. # 方法二:虚拟字段 对象
  16. # publish_obj = models.Publish.objects.filter(pk=3).first()
  17. # models.Book.objects.create(title='红楼梦',price=666.23,publish=publish_obj)
  18. # 删
  19. # models.Publish.objects.filter(pk=1).delete() # 级联删除
  20. # 修改
  21. # models.Book.objects.filter(pk=1).update(publish_id=2)
  22. # publish_obj = models.Publish.objects.filter(pk=1).first()
  23. # models.Book.objects.filter(pk=1).update(publish=publish_obj)

多对多的外键增删改查(就是在操作两者之间的第三者关系表)

test.py

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 多对多的外键增删改查 以Book表为例
  10. # 增
  11. # 方法一:add中放主键id数字
  12. # 如何在第三张表book_authors中建立书籍和作者的绑定关系
  13. # 先整一个书籍对象
  14. # book_obj = models.Book.objects.filter(pk=3).first()
  15. # print(book_obj.authors) # 就类似于你已经到了第三张关系表了,authors是Book类里的authors属性
  16. # book_obj.authors.add(1) # 书籍id为3的书籍绑定一个主键为1 的作者
  17. # book_obj.authors.add(2, 3) # 还可以绑定多个作者
  18. # 方法二:add中放对象
  19. # book_obj = models.Book.objects.filter(pk=1).first()
  20. # author_obj = models.Author.objects.filter(pk=1).first()
  21. # author_obj1 = models.Author.objects.filter(pk=2).first()
  22. # author_obj2 = models.Author.objects.filter(pk=3).first()
  23. # book_obj.authors.add(author_obj)
  24. # book_obj.authors.add(author_obj1,author_obj2) # 还可以放作者对象
  25. """
  26. add给第三张关系表添加数据
  27. 括号内既可以传数字也可以传对象 并且都支持多个
  28. """
  29. # 删除
  30. # 方法一:
  31. # book_obj = models.Book.objects.filter(pk=1).first()
  32. # book_obj.authors.remove(2) # # 书籍id为1的书籍解除绑定一个主键为2 的作者
  33. # book_obj.authors.remove(1,3)
  34. #
  35. # 方法二:
  36. # book_obj = models.Book.objects.filter(pk=1).first()
  37. # author_obj = models.Author.objects.filter(pk=1).first()
  38. # author_obj1 = models.Author.objects.filter(pk=2).first()
  39. # author_obj2 = models.Author.objects.filter(pk=3).first()
  40. # book_obj.authors.remove(author_obj,author_obj2)
  41. """
  42. remove
  43. 括号内既可以传数字也可以传对象 并且都支持多个
  44. """
  45. # 修改
  46. # 方法一:
  47. # book_obj = models.Book.objects.filter(pk=1).first()
  48. # book_obj.authors.set([1,2]) # 括号内必须给一个可迭代对象
  49. # book_obj.authors.set([3]) # 括号内必须给一个可迭代对象
  50. # 方法二:
  51. # book_obj = models.Book.objects.filter(pk=1).first()
  52. # author_obj = models.Author.objects.filter(pk=2).first()
  53. # author_obj1 = models.Author.objects.filter(pk=3).first()
  54. # book_obj.authors.set([author_obj,author_obj1]) # 括号内必须给一个可迭代对象
  55. """
  56. set
  57. 括号内必须传一个可迭代对象,该对象内既可以数字也可以对象 并且都支持多个
  58. """
  59. # 清空
  60. # 在第三张关系表中清空某个书籍与作者的绑定关系
  61. book_obj = models.Book.objects.filter(pk=1).first()
  62. book_obj.authors.clear()
  63. """
  64. clear
  65. 括号内不要加任何参数
  66. """

修改单独说明:

1、

修改前

Django之模型层 - 图3

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 修改
  10. book_obj = models.Book.objects.filter(pk=1).first()
  11. book_obj.authors.set([1,2]) # 会将id为1的book,对应的作者从1,3改为1,2,会先将id为1的book对应的作者id为3的删掉,再重新建

Django之模型层 - 图4

2、

修改前

Django之模型层 - 图5

test.py

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 修改
  10. book_obj = models.Book.objects.filter(pk=1).first()
  11. book_obj.authors.set([3]) # 将id为1的book对应author_id为1,2的先删掉,然后再建一个id为1的book对应author_id为3

修改后

Django之模型层 - 图6

FOREIGNKEY中的DB_CONSTRAINT参数有什么用

  1. db_constraint 唯一约束
  2. db_constraint = True 方便查询 约束字段
  3. db_constraint = fales 不约束字段 同时也可以查询

正反向概念

  1. # 正向
  2. # 反向
  3. 外键字段在我手上那么,我查你就是正向
  4. 外键字段如果不在手上,我查你就是反向
  5. book >>> 外键字段在书那儿(正向)>>> publish
  6. publish >>> 外键字段在书那儿(反向)>>>book
  7. 一对一和多对多正反向的判断也是如此
  8. 正向查询按外键字段:eg:
  9. author_obj = models.Author.objects.filter(name='jason').first()
  10. res = author_obj.author_detail
  11. 反向查询按表名小写
  12.     _set
  13. ...
  14. eg:
  15. # publish_obj = models.Publish.objects.filter(name='东方出版社').first()
  16. # 出版社查书 反向
  17. # res = publish_obj.book_set # app01.Book.None
  18. # res = publish_obj.book_set.all()
  19. """
  20. 基于对象
  21. 反向查询的时候
  22. 当你的查询结果可以有多个的时候 就必须加_set.all()
  23. 当你的结果只有一个的时候 不需要加_set.all()
  24. """

多表查询

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

练习题:

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 正向
  10. # 子查询(基于对象的跨表查询)
  11. # 1.查询书籍主键为1的出版社
  12. # book_obj = models.Book.objects.filter(pk=1).first()
  13. # # 书查出版社 正向
  14. # res = book_obj.publish # 通过一对一多的外键publish,book_obj.publish得到书籍主键为1的出版社对象
  15. # # print(res)
  16. # print(res.name)
  17. # print(res.addr)
  18. # 2.查询书籍主键为2的作者
  19. # book_obj = models.Book.objects.filter(pk=2).first()
  20. # # 书查作者 正向
  21. # # res = book_obj.authors # app01.Author.None
  22. # res = book_obj.authors.all() # <QuerySet [<Author: Author object>, <Author: Author object>]>
  23. # print(res)
  24. """
  25. 在书写orm语句的时候跟写sql语句一样的
  26. 不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点
  27. 正向什么时候需要加.all()
  28. 当你的结果可能有多个的时候就需要加.all()
  29. 如果是一个则直接拿到数据对象
  30. book_obj.publish
  31. book_obj.authors.all()
  32. author_obj.author_detail
  33. """
  34. # 3.查询作者jason的电话号码
  35. # author_obj = models.Author.objects.filter(name='jason').first()
  36. # res = author_obj.author_detail
  37. # print(res)
  38. # print(res.phone)
  39. # print(res.addr)
  40. # 反向查
  41. # 4.查询出版社是东方出版社出版的书
  42. # publish_obj = models.Publish.objects.filter(name='东方出版社').first()
  43. # 出版社查书 反向
  44. # res = publish_obj.book_set # app01.Book.None
  45. # res = publish_obj.book_set.all()
  46. # print(res)
  47. # 5.查询作者是jason写过的书
  48. # author_obj = models.Author.objects.filter(name='jason').first()
  49. # 作者查书 反向
  50. # res = author_obj.book_set # app01.Book.None
  51. # res = author_obj.book_set.all()
  52. # print(res)
  53. # 6.查询手机号是110的作者姓名
  54. # author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
  55. # res = author_detail_obj.author # jason <class 'app01.models.Author'>
  56. # print(res.name)
  57. """
  58. 基于对象
  59. 反向查询的时候
  60. 当你的查询结果可以有多个的时候 就必须加_set.all()
  61. 当你的结果只有一个的时候 不需要加_set.all()
  62. """

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

  1. # 基于双下划线的跨表查询
  2. # 1.查询jason的手机号和作者姓名
  3. # res = models.Author.objects.filter(name='jason').values('author_detail__phone','name') # values()支持正反向概念 # # author_detail__phone:正向利用author_detail字段跨到作者详情表,之后拿phone
  4. # print(res)
  5. # 反向
  6. # res = models.AuthorDetail.objects.filter(author__name='jason') # filter同样支持正反向的概念,拿作者姓名是jason的作者详情
  7. # res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__name')
  8. # print(res) # # 总结: # # 利用正反向的概念,正向使用使用外键字段,反向使用表名小写
  9. # 2.查询书籍主键为1的出版社名称和书的名称
  10. # res = models.Book.objects.filter(pk=1).values('title','publish__name')
  11. # print(res)
  12. # 反向
  13. # res = models.Publish.objects.filter(book__id=1).values('name','book__title')
  14. # print(res)
  15. # 3.查询书籍主键为1的作者姓名
  16. # res = models.Book.objects.filter(pk=1).values('authors__name')
  17. # print(res)
  18. # 反向
  19. # res = models.Author.objects.filter(book__id=1).values('name')
  20. # print(res)
  21. # 查询书籍主键是1的作者的手机号
  22. # book author authordetail
  23. # res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone') # authors__author_detail__phone:book通过外键字段authors进入作者表,再通过作者表中author_detail外键字段进入作者详情表
  24. # print(res)
  25. """
  26. 你只要掌握了正反向的概念
  27. 以及双下划线
  28. 那么你就可以无限制的跨表
  29. """

聚合查询(聚合函数的使用)

  1. # 聚合查询,orm要使用聚合函数要先导入模块,通常情况下聚合查询都是配合分组一起使用的# 如果不想分组但是还想使用聚合函数那么就需要使用aggregate()方法 示例:models.模型类名.objects.aggregate()
  2. """
  3. 只要是跟数据库相关的
  4. 基本都在django.db.models里面
  5. 如果上述没有那么应该在django.db里面
  6. """
  7. from django.db.models import Max,Min,Sum,Avg,Count

简单示例:

test.py

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 聚合查询,orm要使用聚合函数要先导入模块,一般都是结合分组使用,如果想在不使用分组的情况下使用聚合函数,要使用aggregate()
  10. """
  11. 只要是跟数据库相关的
  12. 基本都在django.db.models里面
  13. 如果上述没有那么应该在django.db里面
  14. """
  15. from django.db.models import Max,Min,Sum,Avg,Count
  16. # 1、统计所有书的平均价格
  17. # res = models.Book.objects.aggregate(Avg("price"))
  18. # print(res)
  19. # 2、上述方法一次性使用
  20. res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Count('pk'), Avg('price'))
  21. print(res)

分组查询

  1. """
  2. MySQL分组查询都有哪些特点
  3. 分组之后默认只能获取到分组的依据 组内其他字段都无法直接获取了
  4. 严格模式
  5. ONLY_FULL_GROUP_BY
  6. """

简单示例:

test.py

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 分组查询,分组查询关键字annotate() 类似MySQL中group by
  10. from django.db.models import Max,Min,Sum,Avg,Count
  11. # 1、统计每一本书的作者个数
  12. # res = models.Book.objects.annotate() # models后面点什么 就是按什么分组
  13. # res = models.Book.objects.annotate(author_num=Count("authors__pk")).values("title","author_num")
  14. # res1 = models.Book.objects.annotate(author_num=Count("authors")).values("title", "author_num")
  15. # # author_num=Count("authors__id") author_num是我们自己定义的别名 用来存储聚合函数统计出来的每本书对应的作者个数
  16. # # Count("authors__id") 括号里面(书查作者正向)可以使用外键字段名__字段,也可以直接写外键字段(因为orm帮我们简化了)
  17. # # res = models.Book.objects.annotate(author_num=Count("authors")).values("title","author_num")
  18. # print(res,res1)
  19. # 2、统计每个出版社卖的最便宜的书的价格(作业:复习原生SQL语句 写出来)
  20. # res = models.Publish.objects.annotate(min_price=Min("book__price")).values("name","min_price")
  21. # print(res)
  22. # 3、统计不止一个作者的图书
  23. # res = models.Book.objects.annotate(author=Count("authors")).filter(author__gt=1).values("title","author")
  24. # print(res)
  25. """
  26. 补充:
  27. 只要你的orm语句得出的结果还是一个queryset对象
  28. 那么它就可以继续无限制的点queryset对象封装的方法
  29. """
  30. # 4、查询每个作者出的书的总价格
  31. # res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
  32. # print(res)
  33. """
  34. 如果我想按照指定的字段分组该如何处理呢?
  35. 如果annotate前面出现values那么就会按照values括号里分组
  36. models.Book.objects.values('price').annotate()
  37. 后续BBS作业会使用
  38. 你们的机器上如果出现分组查询报错的情况
  39. 你需要修改数据库严格模式
  40. """

F与Q查询

F查询

示例:

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # F查询
  10. """
  11. 能够帮助你直接获取到表中某个字段对应的数据
  12. """
  13. from django.db.models import F # F查询需要导入F
  14. # 1、查询卖出数大于库存数的书籍
  15. # res = models.Book.objects.filter(maichu__gt=F('kucun'))
  16. # print(res)
  17. # 2.将所有书籍的价格提升500块
  18. models.Book.objects.update(price=F('price') + 500)
  19. # 3.将所有书的名称后面加上爆款两个字
  20. """
  21. 在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
  22. """
  23. from django.db.models.functions import Concat
  24. from django.db.models import Value
  25. models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
  26. # models.Book.objects.update(title=F('title') + '爆款') # 所有的名称会全部变成空白

Q查询

  1. """filter括号内多个参数默认是and关系"""
  2. # 如果想改成or或这非not关系需要用到Q查询

示例:

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # Q查询
  10. """
  11. 能够帮助你直接获取到表中某个字段对应的数据
  12. """
  13. from django.db.models import Q # Q查询需要导入Q
  14. # 1、查询卖出数大于100或者价格小于600的书籍
  15. """filter括号内多个参数是and关系"""
  16. from django.db.models import Q
  17. res = models.Book.objects.filter(Q(maichu__gt=100),Q(price__lt=600)) # Q包裹逗号分割 还是and关系
  18. # res = models.Book.objects.filter(Q(maichu__gt=100)|Q(price__lt=600)) # | or关系
  19. # res = models.Book.objects.filter(~Q(maichu__gt=100)|Q(price__lt=600)) # ~ not关系
  20. print(res)

Q查询高阶

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # Q查询
  10. """
  11. 能够帮助你直接获取到表中某个字段对应的数据
  12. """
  13. from django.db.models import Q # Q查询需要导入Q
  14. # Q的高阶用法 能够将查询条件的左边也变成字符串的形式
  15. q = Q()
  16. q.children.append(('maichu__gt', 100))
  17. q.children.append(('price__lt', 600))
  18. res = models.Book.objects.filter(q) # filter括号内也支持直接放q对象,默认还是and关系
  19. print(res)
  20. """"
  21. 改成or关系
  22. q = Q()
  23. q.connector = 'or' # 指定为or
  24. q.children.append(('maichu__gt', 100))
  25. q.children.append(('price__lt', 600))
  26. res = models.Book.objects.filter(q)
  27. print(res)
  28. """

Django中如何开启事务

  1. """
  2. 事务
  3. ACID
  4. 原子性
  5. 不可分割的最小单位
  6. 一致性
  7. 跟原子性是相辅相成
  8. 隔离性
  9. 事务之间互相不干扰
  10. 持久性
  11. 事务一旦确认永久生效
  12. 事务的回滚
  13. rollback
  14. 事务的确认
  15. commit
  16. """

示例:

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # 目前你只需要掌握Django中如何简单的开启事务
  10. # 事务 需要导入transaction
  11. from django.db import transaction
  12. try:
  13. with transaction.atomic():
  14. # sql1
  15. # sql2
  16. ...
  17. # 在with代码快内书写的所有orm操作都是属于同一个事务
  18. except Exception as e:
  19. print(e)
  20. print('执行其他操作')

数据库设计范式

当一个关系中的所有分类都是不可再分的数据项时,该关系是规范化的。不可再分的数据项,即不存在组合数据项和多项数据项。一个低一级的关系模式,通过模式分解可以转换为若干高一级范式的关系模式的集合,这个过程就叫规范化。二维数据表可以分为5级范式为1NF、2NF、3NF、4NF、5NF。第一范式满足最低的要求条件,第五范式满足最高要求的条件。

第一范式条件:必须不包含重复组的关系,即每一列都是不可拆分的原子项。

如以下表存在可再分项(高级职称),所以不满足第一范式

Django之模型层 - 图7

非规范化转换为规范化的第一范式方法很简单,将表分别从横向、纵向展开即可。将高级职称横向展开即可以得到满足第一范式的表结构。

Django之模型层 - 图8

第二范式条件:关系模式必须满足第一范式,并且所有非主属性都完全依赖于主码。注意,符合第二范式的关系模型可能还存在数据冗余、更新异常等问题。

举例如关系模型(职工号,姓名,职称,项目号,项目名称)中,职工号->姓名,职工号->职称,而项目号->项目名称。显然依赖关系不满足第二范式,常用的解决办法是差分表格,比如拆分为职工信息表和项目信息表。

第三范式的条件:关系模型满足第二范式,所有非主属性对任何候选关键字都不存在传递依赖。即每个属性都跟主键有直接关系而不是间接关系,像:a—>b—>c。一般数据库设计中,一般要求达到3NF,第四第五较少涉及。

比如Student表(学号,姓名,年龄,性别,所在院校,院校地址,院校电话)这样一个表结构,就存在上述关系。 学号—> 所在院校 —> (院校地址,院校电话)。我们应该拆开来,如下:

(学号,姓名,年龄,性别,所在院校)—(所在院校,院校地址,院校电话)

ORM中常用字段以及参数

  1. AutoField
  2. 主键字段 primary_key=True
  3. CharField varchar
  4. verbose_name 字段的注释
  5. max_length 长度
  6. IntegerField int
  7. BigIntegerField bigint
  8. DecimalField
  9. max_digits=8
  10. decimal_places=2
  11. EmailFiled varchar(254)
  12. DateField date
  13. DateTimeField datetime
  14. auto_now:每次修改数据的时候都会自动更新当前时间
  15. auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了
  16. BooleanField(Field) - 布尔值类型
  17. 该字段传布尔值(False/True) 数据库里面存0/1
  18. TextField(Field) - 文本类型
  19. 该字段可以用来存大段内容(文章、博客...) 没有字数限制
  20. 后面的bbs作业 文章字段用的就是TextField
  21. FileField(Field) - 字符类型
  22. upload_to = "/data"
  23. 给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中
  24. /data/a.txt
  25. 后面bbs作业也会涉及
  26. # 更多字段
  27. 直接参考博客:https://www.cnblogs.com/Dominic-Ji/p/9203990.html

自定义字段

  1. # django除了给你提供了很多字段类型之外 还支持你自定义字段

自定义类型示例:

models.py

  1. # 自定义字段需要让类继承feild
  2. class MyCharField(models.Field):
  3. def __init__(self, max_length, *args, **kwargs):
  4. self.max_length = max_length
  5. # 调用父类的init方法
  6. super().__init__(max_length=max_length, *args, **kwargs) # 一定要是关键字的形式传入
  7. def db_type(self, connection):
  8. """
  9. 返回真正的数据类型及各种约束条件
  10. """
  11. return 'char(%s)' % self.max_length
  12. # 自定义字段使用
  13. class Book(models.Model):
  14. title = models.CharField(max_length=32)
  15. price = models.DecimalField(max_digits=8,decimal_places=2)
  16. publish_date = models.DateField(auto_now_add=True)
  17. # 自定义字段
  18. myfield = MyCharField(max_length=16)

外键字段及参数

  1. unique=True
  2. ForeignKey(unique=True) ===> OneToOneField()
  3. # 你在用前面字段ForeignKey创建一对一 orm会有一个提示信息 orm推荐你使用后者但是前者也能用
  4. db_index
  5. 如果db_index=True 则代表着为此字段设置索引
  6. (复习索引是什么)
  7. to_field
  8. 设置要关联的表的字段 默认不写关联的就是另外一张的主键字段
  9. on_delete
  10. 当删除关联表中的数据时,当前表与其关联的行的行为。
  11. """
  12. django2.X及以上版本 需要你自己指定外键字段的级联更新级联删除
  13. """

数据库查询优化

  1. onlydefer
  2. select_relatedprefetch_related

orm语句的特点

  1. # orm语句的特点:
  2. 惰性查询
  3. 如果你仅仅只是书写了orm语句 在后面根本没有用到该语句所查询出来的参数
  4. 那么orm会自动识别 直接不执行

only和defer

test.py

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # orm语句特点:惰性查询
  10. # res = models.Book.objects.all()
  11. # print(res) # 要用数据了才会走数据库
  12. # only与defer
  13. # 1、想要获取书籍表中所有书的名字
  14. # 原先方法,拿到QuerySet对象(列表套字典)
  15. # res = models.Book.objects.values('title')
  16. # print(res)
  17. # for d in res:
  18. # print(d.get('title'))
  19. # 2、实现获取到的是一个数据对象 然后点title就能够拿到书名 并且没有其他字段
  20. # 使用only
  21. # res = models.Book.objects.only('title')
  22. # res = models.Book.objects.all()
  23. # print(res) # <QuerySet [<Book: 三国演义爆款>, <Book: 红楼梦爆款>, <Book: 论语爆款>, <Book: 聊斋爆款>, <Book: 老子爆款>]>
  24. for i in res:
  25. print(i.title) # 点击only括号内的字段 不会走数据库
  26. # print(i.price) # 点击only括号内没有的字段 会重新走数据库查询(因为其对象本身只有title属性)而all方法的对象本身就有了所有属性所以不需要走数据库了
  27. # 3、对象除了没有title属性之外其他的都有
  28. # res = models.Book.objects.defer('title') # 对象除了没有title属性之外其他的都有
  29. # # print(res)
  30. # for i in res:
  31. # print(i.price) # 点击only括号内没有的字段 不会走数据库查询
  32. # print(i.title) # 点击only括号内的字段 会重新走数据库查询
  33. """
  34. defer与only刚好相反
  35. defer括号内放的字段不在查询出来的对象里面 查询该字段需要重新走数据
  36. 而如果查询的是非括号内的字段 则不需要走数据库了
  37. """

select_related与prefetch_related

select_related ==》 inner join

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # select_related与prefetch_related 跟跨表操作有关
  10. # 查询每本书的出版社名称
  11. # 原先方法
  12. # res = models.Book.objects.all()
  13. # for i in res:
  14. # print(i.publish.name) # 每循环一次就要走一次数据库查询
  15. # 优化查询select_related
  16. res = models.Book.objects.select_related("publish") # INNER JOIN
  17. for i in res:
  18. print(i.publish.name)
  19. """
  20. select_related内部直接先将book与publish连起来 然后一次性将大表里面的所有数据
  21. 全部封装给查询出来的对象
  22. 这个时候对象无论是点击book表的数据还是publish的数据都无需再走数据库查询了
  23. ps:select_related括号内只能放外键字段 一对多 一对一
  24. 多对多也不行 ps2:select_related括号内可以放多个外键字段,eg: select_related(外键字段1__外键字段2__外键字段3__...)
  25. """

prefetch_related 子查询

示例:

  1. from django.test import TestCase
  2. # Create your tests here.
  3. import os
  4. if __name__ == "__main__":
  5. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day64.settings")
  6. import django
  7. django.setup()
  8. from app01 import models
  9. # prefetch_related
  10. res = models.Book.objects.prefetch_related("publish") # 子查询
  11. """
  12. prefetch_related该方法内部其实就是子查询
  13. 将子查询查询出来的所有结果也给你封装到对象中
  14. 给你的感觉好像也是一次性搞定的
  15. """
  16. for i in res:
  17. print(i.publish.name)