Django里的ORM操作

ORM

  ORM框架的目的是不再使用SQL语句,让用户使用对象类和对象的方式和这个框架内部的方法来进行数据库操作,对于ORM框架来说,把类和对象转换成SQL语句.
优点:
 1.ORM提供了对数据库的映射,不用直接编写SQL代码,只需要操作对象就能对数据库操作数据
 2.让软件开发人员专注于业务逻辑的处理,提高了开发的效率
缺点:
 1.ORM的缺点是会在一定程度上牺牲程序的执行效率
 2.ORM的操作是有限的,也就是ORM定义好的操作是可以完成的
对应关系:
 类  --> 数据表
 对象 --> 数据行
 属性 --> 字段

ORM表模型

一对一(one-to-one)

实质就是在主外键的关系基础上,给外键加了一个unique=Ture的属性,方法OneToOnefiled()

一对多(one-to-many)

什么时候需要外键
  一张表里的数据会在其它多个表中被使用,这个数据就会被称为外键
在Django中使用外键
publisher = models.Foreignkey(to='类名')
ORM在数据库中创建该字段的时候会默认在后面加_id
在ORM中使用外键
book_obj.publisher --> 表示的是和书关联的出版社对象(ORM帮我们封装的)
book_obj.publisher_id --> 表示数据库中真正保存的字段(int)

多对多(many-to-many)

语法:book = models.ManyToManyField(to='book')
自动创建第三张表:实质就是创建了两个foreign_key

什么是QuerySet

从数据库中查询出来的结果一般是一个集合,这个集合叫做QuerySet。
注意:
(1)QuerySet是可以迭代的
(2)支持切片
(3)可以用len()和count()函数测量数量。
(4)可以通过list()函数强行变成列表。

ORM的增删查改

类名.objects.all()           --> 查找数据库中该表的所有数据
类名.objects.filter(条件)    --> 根据给定的条件取数据库中查找,返回的是列表
类名.objects.get(条件)       --> 根据条件找到一个对象(找到多个或者找不到都报错),返回的是对象

多对多的查询:
author_obj.book.all() ---> 返回一个书籍的列表

类名.objects.create()

多对多的增加:
对象.book.add(id1, id2) --> 多对多添加关联的数据

类名.objects.filter(条件).delete()
类名.objects.get(条件).delete()

多对多的删除:
对象.book.clear()
补充Django2.0以上的版本,外键需要手动设置级联操作 on_delete=models.CASCADE

obj = 类名.objects.get(条件)
obj.属性 = ‘新值’
obj.save()
类名.objects.filter(条件).update(字段=‘新值’)

多对多的修改:
author_obj.book.set([id1, id2]) --> 多对多的设置关联的数据

外键的增删查改

Book.objects.all()          --> 返回的是一个列表(QuerySet)
Book.objects.filter()       --> 返回的是一个列表(QuerySet)
Book.objects.get()          --> 返回的是一个具体书籍对象
Book.objects.filter().delete()
Book.objects.get().delete()
Book.objects.create(title='', publisher='出版社对象')
Book.objects.create(title='', publisher_id='出版社id值')

(1) 基于对象的修改

book_obj = Book.objects.get(id='id值')
book_obj.title = '书名'
book_obj.publisher_id = '出版社id值'
book_obj.save()

(2) 基于QuerySet的修改

Book.objects.filter(id='id值').update(title='值',publisher_id='值')

ORM常用字段和参数

常用字段

AutoField

int自增列,必须填入参数primary_key=Ture.当model中如果没有自增列的时候会自动创建

IntegerField

一个整数类型,范围在-2147483648 to 2147483647,只有10位不能表示手机号。

CharField

字符类型,必须提供max_length参数, max_length表示字符长度。

DataField

日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例。

Data TimeField

日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例

DecimalField

小数字段DecimalField(max_digits=5, decimal_places=2),max_digits定义小数总长度,decimal_places`定义小数位数

其它字段

BigAutoField(AutoField)
        - bigint自增列,必须填入参数 primary_key=True

        注:当model中如果没有自增列,则自动会创建一个列名为id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自动创建一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整数 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767
 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

    BooleanField(Field)
        - 布尔值类型

    NullBooleanField(Field):
        - 可以为空的布尔值
  TextField(Field)
        - 文本类型

    EmailField(CharField):
        - 字符串类型,Django Admin以及ModelForm中提供验证机制

    IPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

    GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"

    URLField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证 URL

    SlugField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

    CommaSeparatedIntegerField(CharField)
        - 字符串类型,格式必须为逗号分割的数字

    UUIDField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
        - 参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          允许文件
                allow_folders=False,       允许文件夹

    FileField(Field)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
            width_field=None,   上传图片的高度保存的数据库字段名(字符串)
            height_field=None   上传图片的宽度保存的数据库字段名(字符串)
 TimeField(DateTimeCheckMixin, Field)
        - 时间格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

    FloatField(Field)
        - 浮点型

    DecimalField(Field)
        - 10进制小数
        - 参数:
            max_digits,小数总长度
            decimal_places,小数位长度

    BinaryField(Field)
        - 二进制类型

自定义字段

class UnsignedIntegerField(models.IntegerField):
    def db_type(self, connection):
        return 'integer UNSIGNED'

自定义char类型字段:

class FixedCharField(models.Field):
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        super().__init__(max_length=max_length, *args, **kwargs)
        self.length = max_length

    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为length指定的值
        """
        return 'char(%s)' % self.length


class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用上面自定义的char类型的字段
    cname = FixedCharField(max_length=25)

常用的字段参数

null 数据库中字段是否可以为空
db_column 数据库中字段的列名
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引

DatetimeField和Datefield独有:

auto_now_add --> 当前数据的创建时间
auto_now --> 当前数据的最后修改时间

带choice参数的字段

get_字段名_display()

建表的元信息

class Meta:
db_table = '表名'
unique_together = (('ip', 'port'),)
index_together = (("pub_date", "deadline"),)

必知必会13条

  1. 返回QuerySet列表类型的
    1. filter():筛选条件匹配的对象
    2. all():查询所有的结果
    3. exclude():筛选出与条件不匹配的对象
    4. order_by():对查询的结果排序
    5. reverse():对查询的结果反向排序
    6. distinct():从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)
    7. values(): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
    8. values_list() :它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
    2. 返回具体对象
    1. get():返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
    2. first():返回第一条记录
    3. last():返回最后一条记录
    3. 返回布尔值
    1. exists():如果QuerySet包含数据,就返回True,否则返回False
    4. 返回数字的
    1. count():返回数据库中匹配查询(QuerySet)的对象数量。

神奇的下划线(单表查询)

models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
 
models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
 
models.Tb1.objects.filter(name__contains="ven")  # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
 
models.Tb1.objects.filter(id__range=[1, 3])      # id范围是1到3的,等价于SQL的bettwen and
 
类似的还有:startswith,istartswith, endswith, iendswith 

date字段还可以:
models.Class.objects.filter(first_day__year=2017)

ORM关联查询

基于对象的查询

正向查询

对象.关联字段

反向查询

1.默认不设置related_name属性
(1)查找的对象是多个的时候(一对多或者多对多)
publisher_obj.book_set.all()
(2)查找一个对象时(一对一)
author_info_obj.autjor.name
2.设置related_name=‘books’属性
publisher_obj.books.all()

基于QuerySet的查询

正向查询

Book.objects.filter(id=1).values_list(‘publisher__name’)

反向查询

1.默认不设置related_name属性,默认就用类名的小写
Publisher.objects.filter(id=1).values_list(‘book__price’) 2.设置related_name属性Publisher.objects.filter(id=1).values_list(‘books__price’)

聚合查询和分组查询

聚合

Django在django.db.models中提供了许多聚合函数,aggregate()是Queryset的一个终止子句,意思是它返回一个包含键值对的字典不能使用values()和value_list()
聚合函数:

from django.db.models import Avg,Sum,Max,Min,Count:

假如要求一个表中某个数值列的平均数

from django.db.models import Avg,Sum,Max,Min,Count:
表名.objects.all().aggregate(Avg('列名'))

也可以指定一个名字

表名.objects.all().aggregate(昵称=Avg('列名'))

也可以使用多个聚合函数

表名.objects.all().aggregate(Avg('列名'),Max('列名'))

分组

假设现在有一张公司职员表:


image

我们使用原生SQL语句,按照部分分组求平均工资:

select dept,AVG(salary) from employee group by dept;

ORM查询:

from django.db.models import Avg
Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")

连表查询的分组:


image

SQL查询:

select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;

ORM查询:

from django.db.models import Avg
models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")

F查询和 Q查询

F查询

当我们需要进行两个字段的值作比较的时候我们使用F()来做比较.F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
查询评论数大于收藏数的书籍

from django.db.models import F
models.Book.objects.filter(commnet_num__gt=F('keep_num'))

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)

修改操作也可以使用F函数,比如将每一本书的价格提高30元

models.Book.objects.all().update(price=F("price")+30)

如果要修改char字段咋办?

如:把所有书名后面加上(第一版)

>>> from django.db.models.functions import Concat
>>> from django.db.models import Value
>>> models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))

Q查询

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象。
你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。
示例:查询作者名字是小仙女并且不是2018年出版的书的书名

models.Book.objects.filter(Q(author__name="小仙女") & ~Q(publish_date__year=2018)).values_list("title")

查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。

例如:查询出版年份是2017或2018,书名中带物语的所有书。

 models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="物语")

在Django终端打印SQL语句

在Django项目的settings.py文件中,在最后复制粘贴如下代码:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • 1 ORM常用操作 1.1 概念 对象关系映射(英语:Object Relational Mapping,简称OR...
    Spareribs阅读 651评论 0 6
  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,919评论 2 89
  • 看@面相与命运的微博已经两年了也买过一套入门面相书来看。“以面识人,以面识己”已经成为了我习惯的一种思维方式。当然...
    丁叮妹儿Nikki阅读 2,256评论 3 7
  • 姓名:刘明正 公司:青州市华松塑业有限公司 【日精进打卡第357天,始于20180420今天是20190421】。...
    LMZ_4d79阅读 219评论 0 0
  • 身边的历史 | 家之脉 父亲的勤劳,母亲的努力,爷爷的节俭,是我们家几代人传承不断的脉。(四年级 唐子涵) 父亲的...
    Fenny_官阅读 195评论 0 4