博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Django --ORM
阅读量:7117 次
发布时间:2019-06-28

本文共 13350 字,大约阅读时间需要 44 分钟。

一、单表操作

1.1创建表

1.2,单表增删改查

二,多表操作

2.1,多表操作--创建关联表

2.2,多表操作--添加记录(一对多/多对多)

2.3,多表操作--基于对象的跨表查询(select嵌套)

2.4,多表操作--基于双下划线的跨表查询(join)

2.5,多表操作--聚合函数

2.6,多表操作--分组查询

2.7,多表操作--F查询与Q查询

2.8,(补)多表操作--列转行

2.9  (补)给查询结果增加字段extra 

3.0  (补)时区问题

一、单表操作

1.1创建表

第一步、views.py里写表对应的类,例如:

from django.db import models
class Book(models.Model):
id=models.AutoField(primary_key=True)
title=models.CharField(max_length=32)
pub_date=models.DateField()
price=models.DecimalField(max_digits=8,decimal_places=2) #9999.99
publish=models.CharField(max_length=32)
第二步、settings.py里加两条
1,加项目名称app01,pycharm里面好像可以不加
2,
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'orm',
'USER': 'root',
'PASSWORD': 'mysql8',
'HOST': '127.0.0.1',
'PORT': 3306
}
}

Django的orm支持的数据库(对PostgreSQL支持最好):

  • 'django.db.backends.postgresql'
  • 'django.db.backends.mysql'
  • 'django.db.backends.sqlite3'
  • 'django.db.backends.oracle'

DB2直接写SQL吧,可以放弃Django的orm了。

或者是DB2:要install  ibm_db and ibm_db_django

其他数据库:

'db2': {        'ENGINE'     : 'ibm_db_django',        'NAME'       : 'SAMPLE',        'USER'       : 'username',        'PASSWORD'   : 'password',        'HOST'       : '192.168.23.123',        'PORT'       : '50000',        'PCONNECT'   :  True,      #Optional property, by default it is false    }

第三步、项目文件夹下的__init__.py里加:

import pymysql
pymysql.install_as_MySQLdb()

第四步、如果Python解释器是3.4以上且Django是2.0以上还要注释掉:

..\Python37\Lib\site-packages\django\db\backends\mysql\base.py里的
if version < (1, 3, 13):
  raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)

如果报错:AttributeError: 'str' object has no attribute 'decode'

query = query.decode(errors=‘replace’)  将decode修改为encode即可

第五步、如果想打印ORM转换过程中的sql语句,需要在settings里写:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
第六步、在项目路径下顺序执行两条命令就把表生成好了
python manage.py makemigrations
python manage.py migrate

这时候除了类那张表Django还生成了其他表

1.2,单表增删改查

1、增加记录

有两种方式:
方式1:
from app01.models import Book
book1 = Book(title="python编程思想",price=100)
book1.save()
方式2:
# 执行后插入一条记录,返回这条记录的对象
book1 = Book.objects.create(title="python编程思想",price=100)

创建用户时,如果使用原生的Django-User表需要用create_user,如果用create创建的密码没有加密:

UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar_obj)

2、查询

'''

要知道方法的返回值和方法的调用者
'''
<1> Book.objects.all(): 查询所有结果,返回一个QuerySet对象,类似于list,可以遍历切片或索引访问
<2> Book.objects.filter(**kwargs): 例如filter(price=100,title='go'),返回QuerySet对象
<3> Book.objects.get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个对象
如果符合筛选条件的对象超过一个或者没有都会抛出错误。
例如get(price=100,title='go')
<4> Book.objects.exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象,返回QuerySet对象
例如exclude(price=100)查出price<>100的
----------
(5) first()/last() 取第一个或最后一个,调用者是QuerySet,返回Book对象
(6) order_by(*field): 对查询结果排序,调用者是QuerySet,例如升序order_by("id") 降序order_by("-id")可以几个字段联合排序
(7) reverse(): 对查询结果反向排序,调用者是QuerySet
(8) count(): 返回QuerySet集合中的对象数
(9) exists(): 如果QuerySet包含数据,就返回True,否则返回False,由QuerySet调用
----------
<10> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
model的实例化对象,而是一个可迭代的字典序列
例如:拿QuerySet集合中所有对象的某几个字段,返回字典组成的QuerySet,由QuerySet对象调用
Book.objects.all().values("title","price")

<11> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

(12) distinct(): 从返回结果中剔除重复纪录,配合values或values_list使用
.values(..).distinct()

单表查询之模糊查询
大于10且小于200
Book.objects.filter(price__gt=10, price__lt=200)
Book.objects.filter(price__range=[10,200])
以xx开头
Book.objects.filter(title__startwith="xx")
包含xx的
Book.objects.filter(title__contains="xx") 区分大小写
Book.objects.filter(title__icontains="xx") 不区分大小写
字段在集合内的
Book.objects.filter(price__in=[100,200,300])
年份等于xx的,date类型才有
Book.objects.filter(pub_date__year=2012)

 

删:对QuerySet进行操作,先查出来再删,返回删除的条数

QuerySet.delete()

改:对QuerySet进行操作

QuerySet.update(title="php")

二,多表操作

2.1,多表操作--创建关联表

一对一:(在有外键的表里加。数据库:authorDetail_id字段,关联表AuthorDetail的主键id,能使用on_delete属性)

authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)    或者

authorDetail=models.ForeignKey(to="AuthorDetail",to_field="id",on_delete=models.CASCADE,unique=True)

一对多:(在有外键的表里加。数据库:customer_id字段,关联表Customer的主键id,能使用on_delete属性)

customer = models.ForeignKey(to="Customer",to_field="id",on_delete=models.CASCADE)

多对多:(随便在哪张表里加。生成新当前表和Order的关系表:xx2orders,只有三个字段,不能使用on_delete属性,是级联删除) 

orders = models.ManyToManyField(to="Order")

或者自己加第三张表(推荐,能使用on_delete属性):最好能给多对多关系添加联合约束

好处1,可以对某个外键设置on_delete,这样可以灵活配置通过一张表能级联删除,通过另一张表不能进行级联删除

好处2,可以设置联合主键,或者联合约束

class Order_To_Commodity(models.Model):     id = models.AutoField(primary_key=True)  # 主键     order = models.ForeignKey(to="Order", to_field="id", on_delete=models.CASCADE)     commodity = models.ForeignKey(to="Commodity", to_field="id", on_delete=models.CASCADE)     comm_quantity = models.IntegerField()

on_delete字段含义:(以下都无法进行级联更新)

on_delete=models.CASCADE, # 无关联数据的,可删除;有关联数据的,可删除,是级联删除

on_delete=models.DO_NOTHING, # 无关联数据的,可删除;有关联数据的,不可删除跟PROTECT一样报错
on_delete=models.PROTECT, # 无关联数据的,可删除;有关联数据的,不可删除并引发错误ProtectedError

# models.ForeignKey('关联表', on_delete=models.SET_NULL, blank=True, null=True)

on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空,一对一同理)

# models.ForeignKey('关联表', on_delete=models.SET_DEFAULT, default='默认值')

on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值,一对一同理)

on_delete=models.SET, # 删除关联数据,

a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

----------set可执行对象的使用------

**官方案例**

def get_sentinel_user():
return get_user_model().objects.get_or_create(username='deleted')[0]

class MyModel(models.Model):

user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET(get_sentinel_user),
)
---------------------

2.2,多表操作--添加记录(一对多/多对多)

一对多添加记录

例如:客户表-Customer 订单表-Order

客户表:添加方法跟单表添加方法一样。

订单表:
方式1:
Order.objects.Create(addr='静安路12号',customer_id=1)
order_obj.customer # 可以访问,返回这个订单的客户对象;只有一个
方式2:
customer_obj=Customer.objects.filter(id=1).first()
order_obj = Order.object.Create(addr='静安路12号',customer=customer_obj)
order_obj.customer # 可以访问,返回这个订单的客户对象;只有一个

多对多添加记录
订单表(Order)/商品表(Comm)/(订单商品关系表Order_Comm)

给订单绑定2个商品

apple = Comm.objects.get(name="苹果")
pear = Comm.objects.get(name="梨子")
order1 = Order.object.get(order_num="2324232342")

order1.comms.add(apple, pear)

order1.comms.add(1, 2)
order1.comms.add(*[1, 2])

给订单解除商品

order1 = Order.object.get(order_num="2324232342") #先查出来
order1.comms.remove(1,2)
order1.comms.remove(*[1, 2])
order1.comms.clear() # 全解除

获取订单的所有商品

order1.comms.all() # QuerySet对象

 ---示例

def add(request):     G_title=request.POST.get('title')#-------值为:python书本     G_publish=request.POST.get('publish') #-------值为:1    G_authors_list=request.POST.getlist('authors')#-------值为:[3,7]      publish_obj=Publish.objects.get(id=G_publish)#查找Publish表对应id的obj       authors_obj_list=Author.objects.filter(id__in=G_authors_list)#查找Author表对应id的多个obj     title       #-------普通字段    publish     #-------一对多外键    authors     #-------多对多外键     book_obj=Book.objects.create(title=G_title,publish=publish_obj)#添加普通和一对多外键的值         #添加多对多外键的值    方式一    book_obj.authors.add(*authors_obj_list)    方式二    for obj in authors_obj_list:        book_obj.authors.add(obj)     return redirect('/index/')

 多对多--反向修改:

# 修改外键字段publish_id,赋值外键ID就行    Book.objects.filter(id=id).update(title=title, unit_price=unit_price, publish_date=pub_date,publish_id=publish_id)    # 获取当前要被添加书籍的作者    authors_obj_now = Author.objects.filter(id__in=authors_id)    # 获取之前被添加书籍的作者    authors_obj_pre = Author.objects.filter(books__id=id)    # 给之前的所有作者解绑当前书籍(id是书籍的主键)    for author in authors_obj_pre:        author.books.remove(id)    # 给现在的所有作者绑定当前书籍    for author in authors_obj_now:        author.books.add(id)

 

2.3,多表操作--基于对象的跨表查询(select嵌套)

----------

A-B
外键在A表中
正向查询:A---->B 按字段
反向查询:B---->A 按表名:表名小写_set
----------

一对多:

客户表(Customer)/ 订单表(Order)
正向查询:查订单"dd123"的客户名称
c = Order.objects.filter(order_num="dd123").first()
name = c.name
反向查询:查客户"张三"的订单
c = Customer.objects.filter(name="张三").first()
orders = c.order_set.all() #QuerySet

多对多:
订单表(Order)/商品表(Comm)
class Comm(models.Model):
pass
Class Order(models.Model):
comms = models.ManyToManyField(to="Order")

正向查询:Order--->comms obj.comms.all()

反向查询:Comm--->orders obj.order_set.all()

正向查询:查订单"dd123"的所有的商品

o = Order.objects.filter(order_num="dd123").first()
comms = o.comms.all() #所有的商品
反向查询:查买了商品"苹果"的所有订单
c = Comm.objects.filter(name="苹果").first()
orders = c.order_set.all()

一对一:

农户表(Farmer) / 农户详情表(FarmerDetail)
class Farmer(models.Model):
farmerdetail = models.OneToOneField(to="FarmerDetail",on_delete=models.CASCADE)
Class FarmerDetail(models.Model):
pass
正向查询:obj.farmerdetail
反向查询:obj.farmer

 

2.4,多表操作--基于双下划线的跨表查询(join)

正向查询按字段,反向查询按表名告诉orm引擎join哪张表。不区分一对多还是多对多还是一对一

举例:

一对多:
查订单"dd123"的客户名称。有两种方式 (customer是字段名,order是表名)
name = Order.objects.filter(num="dd123").values("customer__name") # Order INNER JOIN Customer
name = Customer.objects.filter(order__num="dd123").values("name") # Customer INNER JOIN Order

 

多对多:

订单表(Order)/商品表(Comm)
class Comm(models.Model):
pass
Class Order(models.Model):
comms = models.ManyToManyField(to="Order")

查订单"dd123"的所有的商品名称,两种方式

Order.objects.filter(num="dd123").values(comms__name) 或者
Comm.objects.filter(order_num="dd123").values(name)

 

一对一:

农户表(Farmer) / 农户详情表(FarmerDetail)
class Farmer(models.Model):
farmerdetail = models.OneToOneField(to="FarmerDetail",on_delete=models.CASCADE)
Class FarmerDetail(models.Model):
pass
查农户"张三"的详情
Farmer.objects.filter(name="张三").values(farmerdetail__tel)
FarmerDetail.objects.filter(farmer_name="张三").values("tel")

连续跨表查询:
例如:手机号以110开头的作者出版过的所有书籍名称以及出版社名称
Book.objects.filter(authors__authordetail__telephone__startswith="110").values("title","publish__name")
或者
Author.objects.filter(authordetail__telephone__startswith="110").values("book__title","book__publish__name")

2.5,多表操作--聚合函数

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

Book.objects.all().aggregate(Avg("price")) #{'avg_price':151}

2.6,多表操作--分组查询

单表的分组查询

例如:查询每一个部门的名称以及员工的平均薪水
# select dep,Avg(salary) from emp group by dep
QuerySet = Emp.objects.values("dep").annotate(avg_salary=Avg("salary"))
Emp.objects.all().annotate(avg_salary=Avg("salary")) # 按所有字段分组,跟按主键分组是一样的
总结:
单表.objects.values("group by的分组字段").annotate(聚合函数("统计字段"))
在单表分组下,按主键进行group by没有意义

跨表分组查询
Book表与Publish表是一对多关系。书-出版社
示例1:查询每一个出版社的名称以及出版的书籍个数
Book.objects.filter("publish__name").annotate(c=Count("title"))
或者
Publish.objects.values("id").annotate(c=Count("book__title")).values("name","c")

示例2:查询每一个作者的名字以及出版过的书籍的最高价格

Book表(authors)与Author表是多对多关系
Author.objects.values("id").annotate(m=Max("book__price")).values("name","m")

总结:

每一个后表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段))
每一个后表模型.objects.annotate(聚合函数(关联表__统计字段))

示例3:查询每一个书籍的名称以及对于的作者个数

Book.objects.values("pk").annotate(c=Count("authors__id")).values("title","c")

2.7,多表操作--F查询与Q查询

临时加字段:在类里加属性。要给默认值才行。

例如:select * from Book where comm_num > read_num
这种情况需要引用F函数
from django.db.models import F
Book.objects.filter(comm_num__gt=F("read_num"))
给所有价格+1
Book.objects.all().update(price=F("price")+1)

查询名字=红楼梦 或者 价格=101的书籍
Book.objects.filter(title="红楼梦", price=101) #这是且的关系
Book.objects.filter(Q(title="红楼梦")|Q(price=101)) #或的关系
Book.objects.filter(~Q(title="红楼梦")) #非的关系

2.8,(补)多表操作--列转行

例如:一本书有多个作者,按图书ID分组,每组的author列转行,用逗号分隔

先添加一个方法Concat:

from django.db.models import Aggregate, CharField class Concat(Aggregate):     """ORM用来分组显示其他字段 相当于group_concat"""     function = 'GROUP_CONCAT'     template = '%(function)s(%(distinct)s%(expressions)s)'     def __init__(self, expression, distinct=False, **extra):         super(Concat, self).__init__(             expression,             distinct='DISTINCT ' if distinct else '',             output_field=CharField(),             **extra) 查询:
books = Book.objects.values("id","title","unit_price","publish_date","publish__pub_name").annotate(auth_name=Concat("author__auth_name")) print(books)

2.9  (补)给查询结果增加字段extra

假设:表table中有字段dt类型为datetime类型

有这样的SQL语句,select dt, date_format(dt,"%Y-%m-%d") from table (把datetime转换为date到新的视图字段)

Django orm如何实现呢?这就要用到extra了

  extra(select = None, where = None, params=None, tables=None, order_by = None, select_params = None)

  有些情况下,Django的查询语法难以简单的表达复杂的where子句,对于这种情况,Django提供了extra() 

  extra()的作用是对QuerySet的结果集进行修改。extra中的参数不是都必须的,但是至少要用一个。

  *参数之select: select参数可以让你在select从句中添加其他字段信息,它应该是一个字典,存放着属性名到SQL从句的映射。

  字典的value是一个SQL语句,用这个语句遍历queryResult得到结果放在字典的key(字段)里

  queryResult = models.Article.objects.extra( select = {'is_recent' : "create_time > '2017-09-05' "})

  结果集中每个Entry对象都有一个额外的属性is_recent, 它是一个布尔值,表示Article对象的create_time是否晚于2017-09-05

  上面可以这样修改:

    table.objects.extra( select = {"create_date" : "date_format(create_time,'%%Y-%%m-%%d')"} ).values("dt", "d")

 也可以这样用(官方文档):

  from django.db.models.functions import TruncMonth

  Sales.objects

        .annotate(month = TruncMonth('timestamp'))  # Truncate to month and add to select list

        .values('month')                      # Group by month

        .annotate(c = Count('id'))             # Select the count of the grouping

        .values('month', 'c')            # select month , c  

 

3.0  (补)时区问题

settings.py里:

1、数据库和前端显示时区不一致:

TIME_ZONE = 'Asia/Shanghai'

2、orm中datetime字段的create_time__month字段不好使:

USE_TZ = False
posted on
2019-04-15 00:34 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/staff/p/10708429.html

你可能感兴趣的文章
Jvm(57),类加载器----初次认识加载器
查看>>
poj1053
查看>>
【关键帧提取】基于运动信息(续3)
查看>>
Intellij Idea @Autowired取消提示
查看>>
不再联系
查看>>
VC++中遇到的各种数据类型BSTR、LPSTR、LPWSTR、CString、VARIANT、COleVariant 、_variant_t、CComBSTR...
查看>>
background-size background-positon合并的写法
查看>>
linux创建进程和等待进程退出
查看>>
Android手机终端使用Curl命令
查看>>
前端进击的巨人(八):浅谈函数防抖与节流
查看>>
后端开发面经
查看>>
使用Envoy 作Sidecar Proxy的微服务模式-3.分布式追踪
查看>>
焦虑、不安
查看>>
this是什么以及如何判断它
查看>>
【Go】string 优化误区及建议
查看>>
Perseus-BERT——业内性能极致优化的BERT训练方案【阿里云弹性人工智能】
查看>>
酷狗音乐快速转换MP3格式的方法
查看>>
原生JS 实现复杂对象深拷贝(对象值包含函数)
查看>>
优化体系结构 - 算法外置优化计算结构
查看>>
你应该知道的数据库数据类型及其设计原则
查看>>