• Django框架05 /orm单表操作


    Django框架05 /orm单表操作

    1. orm使用流程

    • 配置mysql

      # django 连接mysql,settings配置文件中
      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.mysql',
              'NAME': 'orm02',
              'USER':'root',
              'PASSWORD':'123',
              'HOST':'127.0.0.1',
              'PORT':3306,
          }
      }
      
    • 用pymysql替换mysqldb

      # 项目文件夹下的init文件中写上下面内容,用pymysql替换mysqldb
      import pymysql
      pymysql.install_as_MySQLdb()
      
    • models文件中创建一个类

      class UserInfo(models.Model):
          id = models.AutoField(primary_key=True)
          name = models.CharField(max_length=10)
          bday = models.DateField()
          checked = models.BooleanField()
      
      BooleanField()   # 不能设置空值,只能设置默认值
      
    • 执行数据库同步指令

      python manage.py makemigrations
      python manage.py migrate
      
      # 执行数据库同步指令,添加字段的时候别忘了,该字段不能为空,所有要么给默认值,要么设置它允许为空 null=True
      
    • 创建记录(实例一个对象,调用save方法)

      from app01 import models
      
      def query(request):
          # 创建一条记录,增
          new_obj = models.UserInfo(
              id=2,
              name='张三',
              bday='2019-09-27',
              checked=1,
          )
          new_obj.save()  
          # 翻译成sql语句,然后调用pymysql,发送给服务端  
          # insert into app01_userinfo values(2,'张三','2019-09-27',1)
      
          return HttpResponse('xxx')
      
    • orm语句执行流程

      orm语句 -- sql -- 调用pymysql客户端发送sql -- mysql服务端接收到指令并执行

    2. orm字段

    • CharField

      - 字符串字段, 用于较短的字符串.
      - CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.
      
    • IntegerField

      用于保存一个整数.
      
    • DecimalField

      一个浮点数. 必须 提供两个参数:
      
      参数                    描述
      max_digits             总位数(不包括小数点和符号)
      decimal_places         小数位数
      
      # 举例
      # 要保存最大值为 999 (小数点后保存2位),要这样定义字段:
      models.DecimalField(..., max_digits=5, decimal_places=2)
      
      # 要保存最大值一百万(小数点后保存10位)的话,要这样定义:
      models.DecimalField(..., max_digits=17, decimal_places=10)
      # max_digits大于等于17就能存储百万以上的数了
      
    • BooleanField

      用 checkbox 来表示此类字段,用来存储布尔值
      
    • TextField

      一个容量很大的文本字段
      
    • EmailField

      判断输入的是不是一个合法的email字段,不接受 maxlength 参数.
      
    • DateField

      一个日期字段, 共有下列额外的可选参数:
      Argument        描述
      auto_now        当对象被保存时(更新或者添加都行),自动将该字段的值设置为当前时间.
      			    通常用于表示 "last-modified" 时间戳.
      auto_now_add    当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间.
      
    • DateTimeField

      一个日期时间字段. 类似 DateField 支持同样的附加选项.
      
    • AutoField

      一个 IntegerField, 添加记录时它会自动增长. 通常不需要直接使用这个字段;
      自定义一个主键:my_id=models.AutoField(primary_key=True)
      如果不指定主键的话,系统会自动添加一个主键字段到model.
      
    • URLField

      用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在
      即URL是否被有效装入且没有返回404响应
      
    • FileField

      一个文件上传字段.
      
      要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime 
      
      
      注意:
      在一个 model 中使用 FileField 或 ImageField 需要以下步骤:
      1、在settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件.
      (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对
      WEB服务器用户帐号是可写的.
      2、在model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django
      使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT).
      
    • ImageField

      它有两个可选参数:height_field和width_field,
      类似 FileField, 不过要校验上传对象是否是一个合法图片.
      如果提供这两个参数,则图片将按提供的高度和宽度规格保存. 
      

    3. orm参数

    • null

      如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
      
    • blank

      如果为True,该字段允许不填。默认为False。
      要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
      如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
      
    • default

      字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
      
    • primary_key

      如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
      Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
      否则没必要设置任何一个字段的primary_key=True。
      
    • unique

      如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
      
    • choices

      由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。
      
    • auto_now_add

      配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
      
    • auto_now

      配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
      
    • DatetimeField、DateField、TimeField这个三个时间字段,都可以设置auto_now_add、auto_now属性。

    • 时间问题:auto_now_add/auto_now

      models.UserInfo.objects.create(
              name='张三',
              bday=current_date,   # 直接插入时间没有时区问题
              checked=0
          )
      
      now = models.DateTimeField(auto_now_add=True,null=True)
      # 如果自动来插入时间,就会有时区的问题,auto_now_add创建记录时自动添加当前创建记录时的时间,存在时区问题
      
      # 解决方法:
          # settings配置文件中将USE_TZ的值改为False
          # USE_TZ = True
          USE_TZ = False  # 告诉mysql存储时间时按照当地时间来存,不要用utc时间
      # 使用pycharm的数据库客户端的时候,时区问题要注意
      

    4. orm单表简单增/删/改

    • 增:

      # 方式1:
          new_obj = models.UserInfo(
              id=2,
              name='张三',
              bday='2019-09-27',
              checked=1,
          )
          new_obj.save() 
          
      # 方式2:
      	# ret 是创建的新的记录的model对象
      	ret = models.UserInfo.objects.create(
              name='张三',
              bday='2019-08-07',
              checked=0
          )
      
          print(ret)    # UserInfo object
          print(ret.name)    # '张三'
          print(ret.bday)    # '2019-08-07'
      
    • 批量增加/bulk_create

      book_list = []
      for i in range(10):
          bk_obj = models.Book(
              name='张三%s'%i,
              addr='北京%s'%i
          )
          book_list.append(bk_obj)
      
      models.Book.objects.bulk_create(book_list) # 批量插入,速度快
      
    • # 简单查询:filter() 
      # 结果是queryset类型的数据里面是一个个的model对象,类似于列表
      
      models.UserInfo.objects.filter(id=6).delete()    # queryset对象调用
      models.UserInfo.objects.filter(id=6)[0].delete()   # model对象调用
      
    • # 方式1:update
          models.UserInfo.objects.filter(id=2).update(
              name='李四',
              checked = 0,
          )
          
          # 错误示例,model对象不能调用update方法
          models.UserInfo.objects.filter(id=2)[0].update(
              name='李四',
              checked = 0,
          )
      # 方式2 
          ret = models.UserInfo.objects.filter(id=2)[0]
          ret.name = '李四'
          ret.checked = 1
          ret.save()
          
      
      nowtime = models.DateTimeField(auto_now=True,null=True)
      # 注意:
      # 更新时的auto_now参数
      # 更新记录时,自动更新时间,创建新纪录时也会帮你自动添加创建时的时间,但是在更新时只有使用方式2的save方法才能自动更新时间,有缺陷
      

    5. orm单表查询

    5.1 查询api

    1. all()

      • 查询所有结果,结果是queryset类型
      ret = models.Books.objects.all()
      print(ret)
      
      < QuerySet[ < Books: Books object >, < Books: Books object >] >
      
    2. filter(**kwargs) -- 条件查询

      • 包含与所给筛选条件相匹配的对象,结果也是queryset类型
      • 如果没有写查询条件会获取所有数据,queryset类型的数据还能够继续调用fitler方法
      • 查询条件不能匹配到数据时,不会报错,返回一个空的queryset,<QuerySet []>
      ret = models.Books.objects.filter(name='linux')
      print(ret)
      <QuerySet [<Books: Books object>, <Books: Books object>]>
      
      #多条件查询用,隔开--是and的关系
      ret = models.Books.objects.filter(name='linux',price=113)
      print(ret)
      <QuerySet [<Books: Books object>, <Books: Books object>]>
      
    3. get(kwargs) ***

      • 返回与所给筛选条件相匹配的对象,不是queryset类型,是model对象
      • 返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。
      ret = models.Books.objects.get(id=5)
      print(ret)
      Books object
      
      # 报错原因:
      # 1. 查不到数据会报错 :Book matching query does not exist.
      # 2. 超过一个就报错 :returned more than one Book -- it returned 13!
      
    4. exclude(**kwargs) -- 排除

      • 排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作,用这个exclude,返回值是queryset类型
      • model对象和queryset对象都可以调用
      Book.objects.exclude(id=6)   # 返回id不等于6的所有的对象
      Book.objects.all().exclude(id=6)   # 在queryset基础上调用
      
      # model对象调用:
      ret = models.Books.objects.exclude(name='python')
      print(ret)
      
      <QuerySet [<Books: Books object>, <Books: Books object>, <Books: Books object>]>
      
      # queryset对象调用:
      ret = models.Books.objects.all().exclude(name='python')
      print(ret)
      
      <QuerySet [<Books: Books object>, <Books: Books object>, <Books: Books object>]>
      
    5. order_by(*field) -- 排序

      • queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型
      • 两种方式改变升序和降序:
        • 1.在传入fields参数的时候,在参数前加-代表降序,不加代表升序.
        • 2.在fields的后面,再加上方法进行改变, asc( )代表升序 ,desc( )代表降序.
      • 切忌:
        • 不要在一个order_by后又加一个order_by,后面的会覆盖掉之前的查询结果.
      升序排序:
      models.Book.objects.all().order_by('price')  # 默认是按照price升序排列
      
      降序排序:
      models.Book.objects.all().order_by('-price')  # 按照字段降序排列,就写个负号就行了
      
      多条件排序:
      models.Book.objects.all().order_by('price','id')  # 是多条件排序,按照price进行升序,price相同的数据,按照id进行升序
      
    6. reverse() -- 反转

      • queryset类型的数据来调用,返回值还是queryset类型
      • 对查询结果反向排序,只适用于已经默认排序或者使用order_by排序过后的查询集,如果未排序,则没有效果.
    7. count() -- 计数、统计返回结果的数量

      • queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量
    8. first()

      • queryset类型的数据来调用,得到的都是model对象,返回第一条记录
      Book.objects.all()[0] = Book.objects.all().first()
      # 得到的都是model对象,不是queryset
      
    9. last()

      • queryset类型的数据来调用,返回最后一条记录
    10. exists() -- 判断返回结果是否有数据

      • queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False,效率高
      models.Book.objects.all().exists()   # exists() 括号内不能放数据
      
      # 翻译成的sql是SELECT (1) AS `a` FROM `app01_book` LIMIT 1,
      # 就是通过limit 1,取一条来看看是不是有数据
      
    11. values(*field)

      • queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet
      • model的实例化对象来调用,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。
      # 示例一:
      ret = models.Books.objects.all().values('name')
      print(ret)
      
      <QuerySet [{'name': 'python'}, {'name': 'linux'},  {'name': 'Go'}]>
      
      # 示例二:
      ret = models.Books.objects.values('name')
      print(ret)
      
      <QuerySet [{'name': 'python'}, {'name': 'python'}, {'name': 'linux'}, {'name': 'python'}, {'name': 'linux'}, {'name': 'Go'}]>
      # 调用values或者values_list的对象是objects控制器,那么返回所有数据
      
    12. values_list(*field)

      • 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
      ret = models.Books.objects.all().values_list('name')
      print(ret)
      <QuerySet [('python',), ('linux',), ('Go',)]>
      
    13. distinct() -- 去重、配置values和values_list来使用

      • distinct()括号里不能加去重的条件
      • values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录
      ret = models.Books.objects.values('name').distinct()
      print(ret)
      
      <QuerySet [{'name': 'python'}, {'name': 'linux'}, {'name': 'Go'}]>
      
      # 错误示例:
      ret = models.Books.objects.all().distinct()
      print(ret)
      <QuerySet [<Books: python>, <Books: python>, <Books: linux>, <Books: python>, <Books: linux>, <Books: Go>]>
      
      # 注意:
      # 不在values()、values_list()后面使用没有意义,因为要是所有的内容全相同才会去重
      

    5.2 基于双下划线的模糊查询 / filter双下划线查询

    1. 在里边:__in

      Book.objects.filter(price__in=[100,200,300]) 
      # price值等于这三个里面的任意一个的对象
      
    2. 大于:__gt

      Book.objects.filter(price__gt=100)   # 大于
      
    3. 大于等于:__gte

      Book.objects.filter(price__gte=100)
      # 大于等于,别写price>100,这种参数不支持
      
    4. 小于:__lt

      Book.objects.filter(price__lt=100)   # 小于
      
    5. 小于等于:__lte

      Book.objects.filter(price__lte=100)   # 小于
      # 小于等于,别写price>100,这种参数不支持
      
    6. 包含:__contains

      Book.objects.filter(title__contains="python")
      # title值中包含python的
      
    7. 包含,不区分大小写:__icontains

      Book.objects.filter(title__icontains="python")
      # 不区分大小写
      
    8. 以什么开头:__startswith

      Book.objects.filter(title__startswith="py") 
      # 以什么开头
      
    9. 以什么开头,不区分大小写:__istartswith

      Book.objects.filter(title__istartswith="py") 
      # 不区分大小写
      
    10. 日期相关

      # 筛选日期
      Book.objects.filter(pub_date='2012-9-12')
      
      # 筛选年份
      Book.objects.filter(pub_date__year=2012)
      # 筛选大于该年份的
      Book.objects.filter(pub_date__ year_gt='2018')  # 大于2018年的、2018数字类型也可以
      
      # 筛选某年某月
      Book.objects.filter(pub_date__year='2019' ,pub_ date__ month='8') 
      
      # 筛选某年某月某日
      Book.objects.filter(pub_date__ year='2019' ,pub_date__month='8',pub_date__day='01')
      
    11. 判断是否为空

      models.Book.objects.filter(publish_date__isnull=True) 
      # 这个字段值为空的那些数据
      

    总结:

    1. 新增字段(必须设置可以为空或者设置默认值)

    2. UTC时间格式格林尼治时间

    3. model对象不能调用update方法

    4. USE_TZ = False

    5. orm整体流程代码示例

      orm > settings.py

      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.mysql',
              'NAME': 'orm1',
              'HOST':'127.0.0.1',
              'PORT':3306,
              'USER':'root',
              'PASSWORD':'123'
          }
      }
      
      # 设置mysql默认使用当地时间
      USE_TZ = False
      
      # 不是跨时区的应用,不需要考虑时区问题,就将这个值改为False
      # mysql是对时区不敏感,django往mysql里面出数据的时候
      # 如果这里的值为True,那么将让mysql强制使用UTC时间
      # 那么存储时间后,当查询的时候,就会发现,时间晚了8小时
      

      orm1 > __init__.py

      import pymysql
      pymysql.install_as_MySQLdb()
      

      models.py

      from django.db import models
      
      class Userinfo(models.Model):
          id = models.AutoField(primary_key=True)
          name = models.CharField(max_length=16)
          register_time = models.DateField(null=True)  
          # auto_now_add=True  修改记录不会改变,只会在添加数据的时候添加时间
          update_time = models.DateTimeField(auto_now=True,null=True) 
          # auto_now=True,  修改记录用方式一/update不会改变、用方式二/ret.save()会更新
          checked = models.BooleanField(default=0)
      

      views.py

      from django.shortcuts import render,HttpResponse
      from app01 import models
      from datetime import datetime
      
      def query(request):
      # 创建记录
      # 方式一
      current_time = datetime.now()
      new_obj = models.Userinfo(
          id = 1,
          name = 'ZHANGSAN'
      )
      new_obj.save()
      
      # 方式二
      ret =models.Userinfo.objects.create(
          name='ZHANGSAN',
          register_time='2019-8-8',
          update_time='2019-8-10 12:12:12',
          checked=1,
      
      )
      ret = models.Userinfo.objects.filter(name='ZHANGSAN')
      print(ret)
      
      
      # 删除
      ret = models.Userinfo.objects.filter(id=7)[0].delete()
      print(ret)
      # 将符合条件的第一条记录删除
      
      ret = models.Userinfo.objects.filter(id=7).delete()
      print(ret)
      # 将符合条件的所有记录删除
      # 注意:filter搜索出来的是queryset类型的列表:queryset<[对象1,对象2,对象3]>
      
      # 修改
      # 方式一
      models.Userinfo.objects.filter(id=1).update(
          name='ZHANGSAN',
          checked=1,
      	register_time = '2019-1-1',
      	update_time = '2020-06-03',
      )
      
      # 方式二
      ret = models.Userinfo.objects.filter(id=1)[0]
      ret.name='LISI'
      ret.save()
      return HttpResponse('OK')
      # 注意:model对象不能调用update()方法
      
    6. 单表查询返回models对象的方法

      1.get(**kwargs)
      2.create(**kwargs)   
      3.count()
        # 该方法返回与之匹配的数据库中对象的个数,并且有一个最大的特点,永远不会返回异常
      4.first()
      5.last() 
      6.get_or_create(defaults=None,**kwargs) 
        # 如果存在则返回查找的对象,如果不存在则创建一个新的对象并且保存.  
      7.update_or_create(defaults= None,** kwargs)
        # 该方法查找defaults中的对象,如果找到则更新数值,如果没有,则创建对象. 
      
    7. 单表查询返回queryset对象的方法

      1.all()
      2.filter(**kwargs)
      3.exclude(**kwargs)
      4.order_by( *fields)
      5.reverse()
      6.distinct()
      7.values(*fields,)
      8.values_list(*fields,)
      
  • 相关阅读:
    luogu P1353 [USACO08JAN]跑步Running
    bzoj 2002: [Hnoi2010]Bounce 弹飞绵羊
    [USACO3.2]Sweet Butter
    [SDOI2009]Elaxia的路线
    [USACO5.4]Telecowmunication
    [洛谷1681]最大正方形II
    [清华集训2014]奇数国
    [洛谷2814]家谱
    [洛谷1868]饥饿的奶牛
    [HNOI2003]激光炸弹
  • 原文地址:https://www.cnblogs.com/liubing8/p/11609334.html
Copyright © 2020-2023  润新知