一、单表操作
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 modelsclass 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 pymysqlpymysql.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 makemigrationspython manage.py migrate这时候除了类那张表Django还生成了其他表
1.2,单表增删改查
1、增加记录
有两种方式:方式1:from app01.models import Bookbook1 = 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且小于200Book.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): passClass 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 Customername = Customer.objects.filter(order__num="dd123").values("name") # Customer INNER JOIN Order
多对多:
订单表(Order)/商品表(Comm)class Comm(models.Model): passClass 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 depQuerySet = 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 FBook.objects.filter(comm_num__gt=F("read_num"))给所有价格+1Book.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