理论:初识ORM

6.1 模型类字段

6.1.1 模型类字段类型及特殊属性

在模型层类中的字段即是数据库中表的字段,表的字段设计非常重要

每一个字段都是Field基类的一个实例(Field类用来建立字段与数据库之间的映射)

模型字段定义不能以下划线结尾

django 会根据在模型类中定义的字段属性来确定以下几点工作

  • 数据库中使用的数据类型

  • 模型类对应的表单类渲染时使用的表单类型及HTML部件

  • 必填字段等最低限度的验证要求检查,包括admin界面下自动生成的表单

BooleanField

True/False 字段,默认值为 None

BooleanField(**options)
  • 表单类型:CheckboxInput<input type='checkbox' ...>

CharField

字符串字段

CharField(max_length=None)
# max_length 设置最大的字符数长度限制
  • 表单类型:TextInput<input type="text" ...>

DateField

datetime.date实例表示的日期

DateField(auto_now=False, auto_now_add=False,**options)
'''
auto_now: 该值为 True 时,每次在保存数据对象时,自动设置该字段为当前时间,也可以理解为自动更新最后一次修改时间
auto_now_add: 该值为 True 时,该字段设置在第一次数据对象创建时,可以记录当前字段创建的时间值
'''
  • 注意:避免矛盾,auto_nowauto_now_adddefault不能同时出现,一个字段属性只能有其中一条设置,当设置了auto_now,或auto_now_add时,也会让该字段默认具有blank=True(字段可以为空)属性

  • 表单类型:TextInput<input type="text" ...>

DatetimeField

datetime.datetime实例表示的日期和时间

DatetimeField(auto_now=False, auto_now_add=False,**options)
# auto_now_add: 创时使用当前时间 注册时间
# auto_now: 修改时自动更新为当前时间 用户上次登陆,修改ip
# 和 DateField 具有相同的字段属性

DecimalField

Decimal实例表示的十进制浮点数类型

DecimalField(max_digits=None,decimal_places=None, **options)
'''
max_digits: 位数总数,包括小数点后的位数,必须大于 decimal_places 参数
decimal_places: 小数点后的数字数量,精度
'''
  • 表单类型:TextInput<input type="text" ...>

FloatField

使用float实例来表示的浮点数

FloatField(**options)
  • 表单类型:TextInput<input type="text" ...>

IntegerField

一个整数,范围由-21474836482147483647

IntegerField(**options)

6.2 字段属性

6.2.1 字段通用属性

null

如果该值为True,将在数据库中将控制存储为NULL

字符串字段CharFieldTextField要避免使用null,因为空值字符串将存储空字符串(""),而不是null值。

对于字符串类型的数据字段,大多数情况下,django使用空字符串代表空值


blank

如果该值为True,则在验证时该字段值可以为空;

null为数据库存储层面可以为空,而blank为表单验证层面可以填写空值


choices

一个二元组的列表或元组;

元组中第一个值为真正在数据库中存储的值,第二个值为该选项的描述

该值一旦被设定,表单样式会显示选择框,而不是标准的文本框,选择框内的选项为choices中的元组

class TestTable(models.Model):
    CHAR_CHOICE = [
        ('H',"非常苦难"),
        ('M',"中等难度"),
        ('S',"非常简单"),
    ]
    choicechar = modesl.CharField(max_length=1,choices=CHAR_CHOICE)
  • choices字段也支持分类的写法

CHAR_CHOICE = [
    ('A',
        (
            ('H',"Hard"),
        )
    ),
    ('B',
        (
            ('M',"Medium"),
        )
    ),
    …
]

分类的名称作为元组中的第一个值,元组的第二个值为该分类下的一个新的二元组序列数据


db_column

数据库中用来表示该字段的名称,如果未指定,那么 Django 将会使用Field名作为字段名


db_index

当该值为True时,为该字段创建索引


default

该字段默认值,可以是一个值或是一个回调函数

当是一个函数对象时,在创建新对象时,函数调用


primary_key

设置该值为True时,该字段成为模型的主键字段,一个模型类同时只能有一个主键

如果一个表中不存在任意一个设置好的主键字段,django 会自动设置一个自增AutoField字段来充当主键,该值可以用pkid方式获取。主键的设置还意味着,null=Falseunique=True


unique

如果该值为True,代表这个数据在当前的表中有唯一值

这个字段还会在模型层验证存储的数据是否唯一

unique 的设置也意味着当前字段具备唯一索引的创建

  • 注意ManyToManyFieldOneToOneFieldFileField字段不可以使用该属性


verbose_name

对于字段的一个可读性更高的名称

如果没有设置该值,字段名中的下换线转换成空格,作为当前字段的数据库中名称

6.3 模型类元选项

6.3.1 模型元选项

在模型类的Meta类中,可以提供一系列的元选项,可以方便对该模型类进行属性设置或约束等

class TestTable(models.Model):
    …
    class Meta:
        ordering = [Fields]
        …

abstract

代表当前模型类为抽象基类,不会创建真正的数据表,只是为了其他模型类继承使用

abstract = True

app_label

当模型类被定义在了其他 app 下,这个属性用来描述当前表属于哪个 app 应用

app_label = "MyApp"

db_table

当前模型类所对应的表名,未设置时,django 默认将表名与 app 名由下划线组成,作为表名

需要注意这个表名为真实在数据库中所使用的,所以该元选项的使用应在数据表创建之前

如果在表已经存在的情况下去修改,会导致数据库内表与模型类表名不一致而查找不到报错


ordering

当前表中的数据存储时的排序规则,这是一个字段名的字符串,可以是一个列表或元组;

每一个字符串前可以使用"-"来倒序排序,使用"?"随机排序

ordering 排序规则的添加,也会增加数据库的开销

ordering = ['-birthday', 'age']
#先按照 birthday 倒序排序,再按照 age 字段进行排序。

verbose_name

一般设置该表展示时所用的名称,名称被自动处理为复数,字符串后加一个"s"


verbose_name_plural

verbose_name功能相同,但是不会自动在字符串后加"s"以表复数 设置表的复数名称


6.4 ORM增删改查

6.4.1 ORM的增删改查

在进行模型操作的学习之前,可以先创建一个测试的数据库模型类,如下所示

class Person(models.Model):
    name = models.CharField(max_length=10,verbose_name="姓名")
    age = models.IntegerField(verbose_name="年龄")

创建对象

django 自带了一个数据库测试的 shell 工具,这是一个非常方便可以让我们对 django 代码进行测试的环境

可以直接通过python manage.py shell命令行管理工具来打开

实例 save 创建数据

通过模型类的关键词参数实例化一个对象来进行数据的创建

>>> from app.models import Person
>>> p1 = Person(name='张三',age=15)
>>> p1.save()

以上的代码,在为字段赋予值之后,通过实例的 save 函数进行该数据的保存 在数据库底层执行了SQL语句中的insert操作,并且,在我们显示调用save之前,django不会访问数据库,实例数据只存在于内存中

  • 注意save函数没有返回值

create 方法创建数据

>>> P1 = Person.objects.create(name='李四',age=20)

这条语句创建一条数据,并且返回一个数据在内存中的实例 P1,之后可以通过这个实例字段 P1 对数据库中该条数据进行修改或删除操作 create方法一步到位,save方式可以慢悠悠的赋予字段值,最后赋予结束再 save


查找对象

接下来,我们将通过模型类中的管理器进行数据的查询;

管理器(Manager)是每一个模型类所具有的,默认名为objects,模型类通过模型类调用orm数据接口,其实就是在对数据表进行操作

注意,具体的某一条数据无法访问这个管理器

  • all()

获取一个表中的所有数据,返回QuerySet数据对象

all_person = Person.objects.all()

  • filter(**kwargs)

返回一个包含数据对象的集合,满足参数中所给的条件

res = Person.objects.all().filter(age__lt=16)
res = Person.objects.filter(age__lt=16)

我们在查询过程中,除了直接使用字段属性进行验证 还可以在字段名之后使用双下化线来标明更加详细的字段筛选条件(在下一节会有详细的字段筛选条件介绍),也叫做链式过滤

这也是为什么表单类字段不可以以下换线结尾的原因


  • exclude(**kwargs)

返回一个包含数据对象的集合,数据为不满足参数中所给的条件

filter()查询会始终返回一个结果集,哪怕只有一个数据,但是有些时候,我们对于一些在数据表中的唯一数据进行查询时,可以使用更加合适的get方法


  • get(**kwargs)

获取唯一单条数据

get 获取数据只会返回一条匹配的结果,获取的数据只能在数据库中有一条

如果返回多个结果,会引发MultipleObjectsReturned异常;如果没有任何匹配到的结果也会引发DoesNotExist异常

Person.objects.get(pk=1)

  • order_by(*field)

默认情况下,数据表使用模型类中的 Meta 中指定的 ordering 选项进行排序 也可以通过使用 order_by 函数进行查询结果的排序

Person.objects.order_by('age')
Person.objects.all().order_by('-age')

  • count()

返回数据库中对应字段的个数,并且该函数永远不会引发异常

models.Person.objects.filter(age=20).count()
Person.objects.count()

使用count函数时,还需要对数据表进行迭代访问 *注意:所以有时使用已生产好的结果集,通过len函数获取长度,这种方式效率会更高;count方法的调用会导致额外的数据库查询


  • values(*fields)

返回一个查询集结果,但是迭代访问时返回的是字典,而不是数据实例对象

models.Person.objects.all().values()
models.Person.objects.values()

链式过滤条件

  • contains

大小写敏感的匹配查询,也是 like,注意转换后查询条件的两侧都有%

Person.objects.filter(name__contains='好')
  • icontains

大小写不敏感的匹配查询

Person.objects.filter(name_icontains='好')
  • range

在某个范围内进行查询

Person.objects.filter(id_range=(1,6))
  • in

查询条件是否在给定的范围内,用小括号和中括号都可以

Person.objects.filter(id_in=(1,6))
Person.objects.filter(id_in=[1,6])
  • exact

如果在查询过程中,没有提供查询类型(没有双下划线),那么查询类型就会被默认指定为exact,这是一种严格查找的方式,用来在数据库中查找和查询时的关键词参数完全一致的内容

Person.objects.filter(account='root')
Person.objects.filter(account__exact='root')

  • iexact

忽略大小写的匹配

Person.objects.filter(account__iexact='root')
#匹配到的结果可能是 Root,ROot,ROOt,ROOT

  • startswith、endswith

分别匹配开头和结尾,区分大小写

Person.objects.filter(passwd__startswith='admin')
# 匹配以 admin 开头的数据

  • istartswith、iendswith

分别匹配开头和结尾,忽略大小写

Person.objects.filter(passwd__istartswith='admin')
匹配以不区分大小写的字符串 admin 为开头的数据

  • gte

大于或等于

Person.objects.filter(reg_data__gte=datetime.date.today)

  • lte

小于或等于

Person.objects.filter(reg_data__lte=datetime.date.today)

修改对象

获取到对应的数据实例之后,通过.的方式访问数据实例中的属性,进行数据的字段修改,修改之后通过save方法对实例进行入库的保存

p = models.Person.objects.get(pk=1)
p.age = 21
p.save()

对过滤出的结果链式调用update()函数,这样的修改,类似批量修改,update函数会返回成功修改的个数

models.Person.objects.filter(age__gt=100).update(age=25)
# 将所有年纪小于 100 的人的年纪改为 20

删除对象

对于普通的单表数据删除,获取到数据实例对象后调用内置的delete()函数即可

models.Person.objects.get(pk=1).delete()
  • 注意:删除一条数据之后,默认占有的主键 ID 值并不会被下一个新插入的值所占用

比如 1,2,3,4;删除掉 3 之后,剩下:1,2,4;下一个值存储时,id 是 5,3 不会被复用

6.4.2 ORM的惰性查询

注意:创建结果集的过程不涉及任何数据库的操作,查询工作是惰性的,在上面的查询方式中,查询代码不会实际访问数据库,只有查询集在真正使用时,django 才会访问数据库

6.4.3 ORM的Q、F查询

属性 VS 属性

之前的查询都是对象的属性与常量值比较,两个属性怎么比较呢?

  • 使用F对象,被定义在django.db.models中。

  • 可以在F对象上使用算数运算。

语法如下:

from django.db.models import F
​
​
F('属性名')  # 注意:属性名是字符串形式

1. 查询阅读量大于评论量的图书

Book.objects.filter(bread__gt=F('bcomment'))
Users.objects.filter(age__lt=F('id'))

2. 查询阅读量是评论量2倍的图书

Book.objects.filter(bread=F('bcomment')*2)

3. 查询阅读量比评论量多100的图书

Book.objects.filter(bread=F('bcomment')+100)

逻辑运算

  • 多个过滤器逐个调用表示逻辑与and关系,同sql语句中where部分的and关键字。

  • 如果需要实现逻辑或or的查询,需要使用Q对象结合逻辑运算符Q对象被义在django.db.models中。

  • Q对象可以使用&|连接,&表示逻辑与,|表示逻辑或。

语法如下:

from django.db.models import Q
​
Q(属性名__运算符=值)  # 注意: 属性名不是字符串,不能加引号

1 查询id阅读量大于20且id小于3的图书

Book.objects.filter(bcomment__gt=20).filter(id__lt=3)  # 多个过滤器连续操作
​
Book.objects.filter( Q(bcomment__gt=20) & Q(id__lt=3) )  # 使用Q对象和逻辑运算符

2 查询阅读量大于20,或编号小于3的图书

Book.objects.filter( Q(bcomment__gt=20) | Q(id__lt=3) )  # 只能使用Q对象

6.4.4 QuerySet是什么

Django的ORM中存在查询集的概念。

查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。

当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表):

  • all():返回所有数据。

  • filter():返回满足条件的数据。

  • exclude():返回满足条件之外的数据。

  • order_by():对结果进行排序。

对查询集可以再次调用过滤器进行过滤,如

BookInfo.objects.filter(bread__gt=30).order_by('bpub_date')

也就意味着查询集可以含有零个、一个或多个过滤器。过滤器基于所给的参数限制查询的结果。

从SQL的角度讲,查询集与select语句等价,过滤器像where、limit、order by子句。

判断某一个查询集中是否有数据

  • exists():判断查询集中是否有数据,如果有则返回True,没有则返回False。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值