python学习笔记-django的ORM

一、ORM了解

ORM即对象关系映射,使开发者可以使用面向对象的方式操作数据库,而不需要直接编写复杂的SQL语句。

django数据库中,表与表的关联,可分为一对一,一对多,多对多
django中常用的一对多,创建时用外键关联。一对一是用unque关键字,多对多可以理解是一对多,外加关联表。

django默认支持sqlite,mysql,oracle,postgresql数据库

1、django默认支持sqlite的数据库,默认自动sqlite的数据库驱动,
引擎名称:django.db.backends.sqlite3

mysql:引擎名称:django.db.backends.mysql

2、mysql驱动程序
MySQLdb(mysql python)
mysqlclient
MySQL
PyMySQL(纯python的mysql驱动程序)

3、在django的项目中会默认使用sqlite数据库,在settings里有如下设置

image

 使用sqlite数据库时没有配置数据库名,创建的表是在默认的数据库,这种情况只限sqlite数据库。mysql数据库要指定数据库名

DATABASWS={
'default':{
'ENGINE':'django.db.backends.mysql',
'NAME':'Django_ORM',
'USER':'root',
'PASSWORD':'123456',
'HOST':'', #数据库主机,默认为localhost
'PORT':'3306'
}
}

4、创建数据表实例
class Book(models.Model):
name=models.CharField(max_length=20)
price=models.IntegerField()
pub_date=models.DateField()

5、执行创建表的命令

python manage.py makemigrations

python manage.py migrate

备注:使用原生的SQL和使用ORM的差别
1、初学者使用ORM,熟练的更了解查询性能,用原生SQL多,原生SQL能处理复制场景
2、ORM数据库搬迁时方便,不需要做太多改动。

二、ORM对单表的增删改查

 案例使用mysql数据库,配置setting文件后。创建表,model文件编辑如下

from django.db import models

#Create your models here.
class Book(models.Model):
    name=models.CharField(max_length=20)
    price=models.IntegerField()
    pub_date=models.DateField()
    publish=models.ForeignKey("Publish",on_delete=models.CASCADE) #on__delete没有会报错
    authors=models.ManyToManyField("Author")

    def __str__(self):
        return self.name


class Publish(models.Model):
    name=models.CharField(max_length=32)
    city=models.CharField(max_length=32)

    def __str__(self):
        return self.name

class Author(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField(default=20)

    def __str__(self):
        return self.name
models
安装驱动,django2.0以上版本用的是mysqlclient 用pip安装
在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错
on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值

 执行同步建表命令时,可能出现报错,根据提示调整(可能是驱动问题或mysql版本问题),本次执行没有报错

1、表数据增加

方式一
b=Book(name='python',price=99,author='steven',pub_date='2017-12-12')
b.save()

方式二
Book.objects.create(name='python',price=99,author='steven',pub_date='2017-12-12')
create可以传字典对象,参数**dic,可以批量创建,写循环方式创建

2、表数据修改

方式一
Book.objects.filter(author='yuan').update(price=999)
方式二
b=Book.objects.get(author='oldboy') #这里的get拿到的是一个实例对象,filter拿到的是Queryset对象
b.price=100
b.save()

3、表数据删除

Book.objects.filter(author='yuan').delete()

4、表数据查询

book_list=Book.objects.all()
book_list=Book.objects.all()[::2] #切片
book_list=Book.objects.filter()
book_list=Book.objects.first() #first、last、get取到的都是一个实例对象
book_list=Book.objects.last()
book_list=Book.objects.get() #只能取出一条记录时,才不报错

其他常用方法

exclude() 排除之外的

对查询结果进一步处理
book_list=Book.objects.filter(author='yuan').value('name') #value拿某个具体字段,字典的集合。(可以传多个字段条件,如name,price)
book_list=Book.objects.filter(author='yuan').value_list('name') #返回的是一个元组序列

distinct() 去重
book_list=Book.objects.all().value('name').distinct()
count() 计数
order_by(*field) 对查询结果排序
reverse() 反向排序
exists() 是否存在

万能的__,(模糊查询)
查price>50的
book_list=Book.objects.filter(price__gt=50).values('name','price') #__后可跟更多字母条件,gt是大于,lt是小于。
查name包含P
book_list=Book.objects.filter(name__icontains='P').values('name','price') #包含P的,不区分大小写的数据

示例:路由urls和调用的操作方法views

    path(r'data/',views.index),
    path(r'create/',views.create),
    path(r'modify/',views.modify),
    path(r'delete/',views.delete),
    path(r"querydb/",views.querydb)
def create(req):

    #方式一
    # p=Publish(name="南方出版社",city="广州")
    # p.save()

    # #方式二
    # Publish.objects.create(name="新华出版社",city="重庆")

    aut=[{"name":"张飞","age":19},{"name":"李四","age":30},{"name":"李白","age":35}]
    for a in aut:
        Author.objects.create(**a)

    return HttpResponse("新增成功")

def modify(req):

    #方式一
    # Author.objects.filter(name="张三").update(age=20)

    #方式二
    a=Author.objects.get(name="张飞")
    a.age=26
    a.save()

    return HttpResponse("修改成功")

def delete(req):
    Author.objects.filter(name="李四").delete()
    return HttpResponse("删除成功")


def querydb(req):
    ret=Author.objects.all()    #<QuerySet [<Author: 张三>, <Author: 张飞>, <Author: 李白>]>

    ret=Author.objects.filter(name="李白")#<QuerySet [<Author: 李白>]>
    ret = Author.objects.first()    #张三
    ret=Author.objects.last()   #李白
    ret = Author.objects.get(age=35)    #李白
    ret = Author.objects.exclude(age=35)    #<QuerySet [<Author: 张三>, <Author: 张飞>]>
    ret = Author.objects.all().count()  #3
    ret = Author.objects.all().order_by("age").reverse()    #<QuerySet [<Author: 李白>, <Author: 张飞>, <Author: 张三>]>
    ret = Author.objects.filter(name="李白").exists() #True

    #模糊查询
    ret=Author.objects.filter(age__gt=32)#<查询年龄大于32的作者。QuerySet [<Author: 李白>]>
    ret = Author.objects.filter(age__gt=32).values("name","age") #values设置显示的字段范围。<QuerySet [{'name': '李白', 'age': 35}]>
    ret = Author.objects.filter(name__icontains="张").values("name", "age") #查询名字包含”张“的作者,<QuerySet [{'name': '张三', 'age': 20}, {'name': '张飞', 'age': 26}]>


    print(ret)
    return HttpResponse("success")
views.py

查询数据库验证表数据操作结果正确

image

三、多表操作

 1、一对多

新增数据

#方式一:# publish_id=1
Book.objects.create(name="linux运维", price=77, pub_date="2026-08-12", publish_id=1)

#方式二:
publish_obj = Publish.objects.filter(name="人民出版社")[0]
Book.objects.create(name="linux基础", price=55, pub_date="2026-08-13", publish=publish_obj)

image

查询数据

#方式一
pub_obj = Publish.objects.filter(name="人民出版社")[0]
ret = Book.objects.filter(publish=pub_obj).values("name", "price")
print(ret)
#方式二
#先找到publish对象
pub_obj = Publish.objects.filter(name="人民出版社")[0]
#通过pub_obj找到关联的book对象的集合
print(pub_obj.book_set.all().values("name", "price"))

 image

注意:建表时,一对多,外键一定是建在多的一方(这里的book)

Django会自动在创建表的时候,把外键的名称后加id。如book的外键publish,创建的表字段是publish_id

 

#方式三
#查出版社是人民出版社的书籍名称和价格
ret = Book.objects.filter(publish__name = "人民出版社").values("name", "price")
print(ret)
#查书籍名称是python的出版社名字
ret2 = Publish.objects.filter(book__name="python").values("name")
print('ret2:',ret2)
#查书籍名称是python的出版社名字
ret3 = Book.objects.filter(name="python").values("publish__name")
print("ret3:",ret3)
#查北京出版社出的书
ret4 = Book.objects.filter(publish__city="北京").values("name")
print("ret4:",ret4)
#查询2026年上半年出版了书籍的出版社名称
ret5 = Book.objects.filter(pub_date__lt="2026-07-01", pub_date__gt="2026-01-01").values("publish__name")
print("ret5:",ret5)
return HttpResponse("success")

image

 2、多对多

2.1、创建多对多关系

authors=models.ManyToManyField("Author") 

*在Book写字段方式创建,python自动创建第三张关联表

image

 2.2、创建多对多映射,给指定书籍新增所有作者关联

def manytomany(req):
    #增加多对多关系映射,给书籍增加所有作者
    book_obj = Book.objects.get(id=4)
    author_objs = Author.objects.all()
    book_obj.authors.add(*author_objs)

    return HttpResponse("success")

执行结果,正确创建了记录,查看第三张表

image

 只增加一个作者

    #增加一个作者
    book_obj = Book.objects.get(id=3)
    author_obj = Author.objects.get(id=5)
    book_obj.authors.add(author_obj)

执行结果,创建了一条记录

image

 2.3、多对多查询

    #正向查找
    book_obj = Book.objects.get(id=4)
    print(book_obj.authors.all())
    print(type(book_obj.authors.all()))

    #反向查找
    author_obj = Author.objects.get(id=5)
    print(author_obj.book_set.all())

结果

image

 2.4、删除多对多

    #删除多对多关系
    book_obj = Book.objects.get(id=4)
    book_obj.authors.remove(6) #删除authors_id=2的关系

    author_objs = Author.objects.all() 
    book_obj.authors.remove(*author_objs)   #删除书籍所有的作者关联

结果

image

 注意:

多对多没有直接赋值的方式创建多对多关系
多对多第三张表是自动创建的
创建的多对多关系有重复时会去掉重复

2.5、自动创建和手动创建第三张表的对比  

不用ManyToMany自己手动创建第三张表时(Book_Author)
这时用直接赋值创建
Book_Author.objects.create(book_id=2,author_id=3)
查询(比较麻烦,不常用)
obj=Book.objects.get(id=2)
print(obj.book_author_set.all()[0].author())

示例:(自己创建第三张表)查询作者是bob的书名,价格
ret=Book.objects.filter(book_author__author__name='bob').values("name","price")

示例2:(ManyToMany)时查询相同数据
ret2=Book.objects.filter(author__name="bob").values("name","price")

四、聚合查询和分组查询

1、聚合查询

示例:

def aggrequery(req):
    from django.db.models import Avg,Min,Sum,Max,Count
    #单表:
    ret = Book.objects.all().aggregate(Avg("price"))
    ret2 = Book.objects.all().aggregate(Sum("price"))
    print("书籍平均价格",ret,",价格之和",ret2)

    #多对多场景查询
    #查询关羽写的书,总的价格
    ret3 = Book.objects.filter(authors__name='关羽').aggregate(Sum("price"))
    # 结果格式是字典{"price_sum":128},可以自己设置键的名称
    ret4 = Book.objects.filter(authors__name='关羽').aggregate(yuchang_money=Sum("price"))
    print("关羽写的书总价格:",ret3)
    print(ret4)

    return HttpResponse("success")

结果:

image

 2、分组查询

示例:

    #分组查询
    #查询每个作者写的书,价格之和
    ret5 = Book.objects.values("authors__name").annotate(Sum("price"))
    #查询每个出版社最低的图书价格
    ret6 = Publish.objects.values("name").annotate(Min("book__price"))
    print(ret5)
    print(ret6)

结果:

image

结果存在一条作者名是none的记录,是因为有的书籍在多对多关系表中没有关联作者。

3、查询中的计算(F查询和Q查询)

 示例:

    #算术运算和逻辑运算
    #使用逗号标识多个条件与查询 (示例get没查询到符合条件的结果时,运行报错了)
    ret7=Book.objects.get(name="西游记", price=50)
    print(ret7)

    #把所有书的价格加10,使用F查询
    from django.db.models import F, Q
    Book.objects.all().update(price=F("price") + 10)

    #使用多个条件进行或查询,使用Q查询
    ret8=Book.objects.filter(Q(price=65) | Q(name="春秋"))
    print(ret8)
    #使用~进行非运算查询
    ret9=Book.objects.filter(Q(price=65) | ~Q(name="春秋"))
    print(ret9)

    #扩展:关键字和Q混合用
    ret10 = Book.objects.filter(Q(name__contains="p"))
    ret11 = Book.objects.filter(Q(name="p"), price=60)  # Q查询语句要写在前面
    print(ret10)
    print(ret11)

结果:

image

 数据表

image

 4、总结

【ORM的querySet集合对象的特性】

1、QuerySet是在用到的时候才去数据库查询,惰性机制。(提高网站性能)
用到的场景,if for print语句.会查数据库
2、Django给queryset数据类型加了缓存,执行一次后,再次使用不会重复查询数据库,从缓存中读取
可以通过执行两次for循环,检查sql数据打印次数
3、exists()会查数据库,但不会把数据放在缓存

if ret.exits():
    print("OK")

4、当queryset非常大时,会让缓存引发问题,极大消耗内存
解决方法:查询结果使用iterater()

ret=ret.iterator() #变成迭代器对象
for i in ret:
    print(i.name)

使用exists()和iterator()可以优化程序对内存的使用,不过,它们不会生成queryset cache。结果不能重复使用,下次使用要重新查数据库,造成额外的数据库查询。

posted @ 2026-09-24 16:52  子不语332  阅读(3)  评论(0)    收藏  举报