django 模型层(orm)05

配置测试脚本

方式1:

# 如果想单独测试django中的某个文件,需要手动配置测试脚本(在你应用下的tests.py文件中)
import os
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day55.settings")
    import django
    django.setup()
    
    # 在测试脚本搭建完毕之后,才能导入django文件进行测试
    from app01 import models

方式2:

直接新建一个任意名称的.py文件,将方式1中的配置拿过来就行了。

django ORM基本操作

增删改查

首先创建模型表:

from django.db import models
# Create your models here.
class Books(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField()

1.新增(create())

向数据库添加一条记录

# 第一种 :
book_obj = models.Books.objects.create(title = '人性的弱点',price='172.44',publish_date='2008-08-08')  # 日期格式可以直接写,也可以通过datetime模块生成一个年月日传进去

# 第二种:(利用对象的绑定方法)
book_obj = models.Books(title = '未来简史',price='399.44',publish_date='2008-08-09')
book_obj.save()
'''
返回的是对象本身
'''

2.修改(update())

修改表中某一条记录的某些字段

res=models.Books.objects.filter(id=1).filter().filter().filter()...
'''
id可以写成pk,表示任意一张表的主键字段,我们不需要记自己建表时设置的主键字段的名字,直接写成pk=? 的形式,就more你是当前表的主键了;
返回的res是一个QuerySet对象,可以无限制的调用queryset对象,表示按照当前filter查询的结果继续按照你写的条件继续往下查询;
queryset对象还可以点query查看当前结果内部对应的sql语句
'''
res=models.Books.objects.filter(id=1)
print(res.query)
'''
SELECT `app01_books`.`id`, `app01_books`.`title`, `app01_books`.`price`, `app01_books`.`publish_date` FROM `app01_books` WHERE `app01_books`.`id` = 1
'''

#方式1:
models.Books.objects.filter(id=1).update(price=88.88) # 当条件不存在时,filter空queryset对象,推荐使用filter进行查询。

# 方式2:
book_obj = models.Books.objects.get(pk=1)  # 返回的是对象本身,get在条件不存在时会报错,不推荐使用这种方法做修改操作。
book_obj.price = 88.88
book_obj.save()

3.删除(delete())

删除表中的一条记录

# 方式1:
models.Books.objects.filter(pk=3).delete()
# 方式2:
book_obj = models.Books.objects.get(pk=3)
book_obj.delete()

Django 终端打印SQL语句

如需将你的orm查询语句内部对应的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',
        },
    }
}

13条基本查询操作

all()

查询所有

res = models.Books.objects.all()  # 查询所有 ,返回的结果QuerySet对象
'''
<QuerySet [<Books: Books object>, <Books: Books object>, <Books: Books object>, <Books: Books object>, <Books: Books object>, <Books: Books object>, <Books: Books object>, <Books: Books object>]>
'''

filter()

筛选

res = models.Books.objects.filter(pk=2,title='活着')  # 返回的结果QuerySet对象,支持传多个参数 并且是and关系

get()

筛选

res = models.Books.objects.get(title='活着',price = '99.9') # 获取的是数据对象本身,查询条件不存在报错,也可以传多个参数
'''
Books object
'''

first()

取queryset中第一个数据对象 ,那么返回的就是一个普通的数据对象了

res = models.Books.objects.filter(title='活着').first()

last()

取queryset中最后一个数据对象 ,返回的就是一个普通的数据对象

res = models.Books.objects.filter(title='活着').last()

count()

统计数据的个数,返回的是数字(数据个数)

num = models.Books.objects.count()

values()

获取数据对象中指定的字段的值

# 按照指定条件查询
res = models.Books.objects.filter(pk=2).values('title','price')
print(res)  # <QuerySet [{'title': '活着', 'price': Decimal('99.90')}]>

# 查所有
res = models.Books.objects.values('title','price') # 可以传多个参数, 返回的是QuerySet对象,列表套字典
'''
<QuerySet [{'title': '三国演义', 'price': Decimal('99.99')}, {'title': '活着', 'price': Decimal('99.90')}, {'title': '许三多卖血记', 'price': Decimal('199.88')}, {'title': '平凡的世界', 'price': Decimal('166.66')}, {'title': '富国论', 'price': Decimal('888.88')}, {'title': '鬼谷子', 'price': Decimal('35.55')}, {'title': '人性的弱点', 'price': Decimal('172.44')}, {'title': '人性的弱点', 'price': Decimal('172.44')}]>
'''

values_list()

获取数据对象中指定的字段的值

res = models.Books.objects.values_list('title','price') # 可以传多个参数, 返回的是QuerySet对象,列表套元组
'''
<QuerySet [('三国演义', Decimal('99.99')), ('活着', Decimal('99.90')), ('许三多卖血记', Decimal('199.88')), ('平凡的世界', Decimal('166.66')), ('富国论', Decimal('888.88')), ('鬼谷子', Decimal('35.55')), ('人性的弱点', Decimal('172.44')), ('人性的弱点', Decimal('172.44'))]>
'''

order_by()

按照指定的字段排序

# 升序(两种写法)
res = models.Books.objects.order_by('price') # 默认是升序,返回的是queryset对象
res1 = models.Books.objects.all().order_by('price')
# 降序
res = models.Books.objects.order_by('-price')  # 字段前面加负号
res = models.Books.objects.order_by('price').values('title','price')
print(res)  # queryset对象可以继续通过点语法操作
'''
<QuerySet [{'title': '鬼谷子', 'price': Decimal('35.55')}, {'title': '活着', 'price': Decimal('99.90')}, {'title': '三国演义', 'price': Decimal('99.99')}, {'title': '平凡的世界', 'price': Decimal('166.66')}, {'title': '人性的弱点', 'price': Decimal('172.44')}, {'title': '人性的弱点', 'price': Decimal('172.44')}, {'title': '许三多卖血记', 'price': Decimal('199.88')}, {'title': '富国论', 'price': Decimal('888.88')}]>
'''

reverse()

颠倒顺序 ,必须是有序的情况下才能颠倒

res = models.Books.objects.all().order_by('price').reverse()

'''
res = models.Books.objects.reverse().values('title','price') # 不排序的情况下返回值按照数据库的id顺序打印,没办法帮你颠倒
print(res)  

res = models.Books.objects.all().order_by('price').reverse().values('title', 'price')
print(res)
"""
<QuerySet [{'title': '富国论', 'price': Decimal('888.88')}, {'title': '许三多卖血记', 'price': Decimal('199.88')}, {'title': '人性的弱点', 'price': Decimal('172.44')}, {'title': '人性的弱点', 'price': Decimal('172.44')}, {'title': '平凡的世界', 'price': Decimal('166.66')}, {'title': '三国演义', 'price': Decimal('99.99')}, {'title': '活着', 'price': Decimal('99.90')}, {'title': '鬼谷子', 'price': Decimal('35.55')}]>
"""
'''

exclude()

排除,按照指定条件排除数据,返回的是排除掉的数据之外的其他数据

# 方式1
res = models.Books.objects.all().exclude(title='三国演义')  # queryset对象
# 方式2
res = models.Books.objects.exclude(title='三国演义')

exists()

判断查询结果是否有值 返回结果是一个布尔值

res = models.Books.objects.filter(pk=1).exists()
print(res)

distinct()

按照指定的条件去重

res = models.Books.objects.values('title','price').distinct() # 可以传入多个去重条件,把这多个条件都一样的数据剔除掉  返回的是一个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="活")  # 获取name字段包含"活"的
models.Tb1.objects.filter(name__icontains="活") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 3])  # id范围是1到3的,顾头顾尾 ,也可以写成括号的形式
res = models.Tb1.objects.filter(title__endswith='活')  # 以“活”开头的
res = models.Tb1.objects.filter(title__endswith='活')  # 以“活”结尾的

date字段:
date字段可以通过在其后加__year,__month,__day等来获取date的特点部分数据

res=models.Tb1.objects.filter(publish_date='2008-08-08') # 指定年月日

res=models.Tb1.objects.filter(publish_date__lte='2002-03-28')  # 日期小于等于2002-03-28的  也可以是lt,或者单独指定year,month,day

res=models.Tb1.objects.filter(publish_date__gte='2002-03-28')  # 日期大于等于2002-03-28的  也可以是gt
res=models.Tb1.objects.filter(publish_date__year='2007') # 只指定年份
res=models.Tb1.objects.filter(publish_date__month='08')  # 指定月份
res=models.Tb1.objects.filter(publish_date__day='29')  # 指定某一天

 models.Tb1.objects.filter(publish_date__week_day=2)  
 models.Tb1.objects.filter(publish_date__week_day__gte=2)

表查询

建表

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)
    stored = models.IntegerField(default=1000)
    saled = models.IntegerField(default=1000)

    def __str__(self):  # 打印对象是触发,控制打印格式
        return self.title
    """
      auto_now:每次修改的数据的时候 都会自动更新修改书籍(展示最新的一次修改时间)
      auto_now_add:当数据创建出来的时候 会自动将创建时间记录下来
   """

    publish = models.ForeignKey(to='Publish')  # 一对多关系,外键字段建在查询频率较高的一方
    authors = models.ManyToManyField(to='Author') # 多对多关系,多对多字段建在查询频率较高的一方

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

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()  #  对应到数据库的表当中是varchar类型,长度是254
    author_detail = models.OneToOneField(to='AuthorDetail')  # 一对一关系,一对多字段建在查询频率较高的一方
    def __str__(self):
        return self.name

class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=64)
    def __str__(self):
        return self.addr
'''
模型表类建完以后,要使用数据库迁移命令,把表迁移到数据库当中,建表才算完毕
python3 manage.py makemigrations
python3 manage.py migrate
'''

一对多字段数据的增删改查

# 方式1:
models.Book.objects.create(title='三国演义',price=222.33,publish_id=1)  # 直接传表里面的实际字段和数据主键值  
# 方式2:
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='红楼梦',price=444.33,publish=publish_obj)  # 传虚拟字段  跟数据对象即可

# 方式1:
models.Book.objects.filter(pk=1).update(publish_id=2)
# 方式2:
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)

models.Publish.objects.filter(pk=1).delete() # 默认级联删除,级联更新,这里删除id=1的出版社,出版社id=1的图书也都跟着删除

多对多字段数据的增删改查

增(add)

book_obj = models.Book.objects.filter(pk=2).first()
# print(book_obj.publish) # Publish object
# print(book_obj.authors) # app01.Author.None  已经跨到第三张表了

# 方式1:(add支持传入多个,以逗号分开)
book_obj.authors.add(1) # 在第三张表里面给此书籍对象绑定一个author_id=1的作者  

# 方式2:
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.add(author_obj1,author_obj2)  

总结add方法 能够朝第三张关系表添加数据,即支持传数字--》add(1,2);也支持传对象,add(author_obj,author_obj1);并且两者都可以是多个。

改(set)

book_obj = models.Book.objects.filter(pk=2).first()
# 方式1:
book_obj.authors.set((1,3))
book_obj.authors.set([1,])
# 方式2:
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set((author_obj,author_obj1))

总结set方法修改多对多关系表中的数据,既可以传数字也可以传对象,但是传入的一个或多个参数必须是一个可迭代对象,即用小括号或中括号括起来,内部传入你需要的参数数量。ps: set((1,3)), set((author_obj,author_obj1))

删(remove)

book_obj = models.Book.objects.filter(pk=2).first()
# 方式1:
book_obj.authors.remove(100)
book_obj.authors.remove(1,2)
# 方式2:
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
book_obj.authors.remove(author_obj)
book_obj.authors.remove(author_obj,author_obj1)

总结remove方法,既可以传数字 也可以穿对象;并且都支持传多个 不需要迭代;ps:remove(1,2), remove(author_obj,author_obj1)

清空(clear)

删除某个数据在第三张表中的所有记录

 book_obj = models.Book.objects.filter(pk=2).first()
 book_obj.authors.clear()

跨表查询

正反向查询:
都是基于对象的跨表查询
关系字段在谁哪 由谁查相关联的表就是正向
如果关系字段不在当前表,就是反向

正向查询按字段:正向查询,当被查询出来的数据有多个的情况下,需要点.all();ps:app01.Author.None,一旦看到该结果 只需要加.all()即可

**反向查询按表名小写 + _set:**但是,在一对多,多对多的情况下,才需要加;在一对一的关系中,不需要表明小写加_set。

正向查询

1.查询书籍主键为2的出版社名称

book_obj = models.Book.objects.filter(pk=2).first()
print(book_obj.publish)  # 出版社对象
print(book_obj.publish.name)

2.查询书籍主键为4的作者姓名

book_obj = models.Book.objects.filter(pk=4).first()
print(book_obj.authors)  # app01.Author.None,跨入到了Author这张表中。
print(book_obj.authors.all())   # 一本书可能有多个作者,所以需要点.all()  返回的是queryset对象,列表内部是一个个的作者对象<QuerySet [<Author: Author object>, <Author: Author object>]>,这个是设置打印格式之后的显示:<QuerySet [<Author: zhang>, <Author: liu>]>,表示id=4 的书籍,有两个作者。

3.查询作者是jason的手机号码

author_obj = models.Author.objects.filter(name='zhang').first()
print(author_obj.author_detail)  # AuthorDetail object
print(author_obj.author_detail.phone)

总结:正向查询,当被查询出来的数据有多个的情况下,需要点.all();

ps:app01.Author.None,一旦看到该结果 只需要加.all()即可

反向查询

4.查询出版社是东方出版社出版过的书籍

publish_obj = models.Publish.objects.filter(name='东方出版社').first()
print(publish_obj.book_set)  # app01.Book.None
print(publish_obj.book_set.all())

5.查询作者是jason写过的书籍

author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set)  # app01.Book.None
print(author_obj.book_set.all())  # app01.Book.None

6.查询手机号是120的作者姓名

author_detail_obj = models.AuthorDetail.objects.filter(phone=120).first()
print(author_detail_obj.author)  # Author object  不需类名小写+_set,直接作者详情对象点类名(Author)小写即可 
print(author_detail_obj.author.email)  # 120qq.com

基于双下滑线的跨表查询

基于双下滑线的联表查询操作,能让两张表组成一张表,使用一条django的orm语句就能查询出想要的信息,使操作更加简洁化。

1.查询书籍pk为2的出版社名称

# 方式1:正向查询
res = models.Book.objects.filter(pk=2).values('publish__name')
print(res)  # <QuerySet [{'publish__name': '东方出版社'}]>
# 方式2:反向查询
res = models.Publish.objects.filter(book__pk=2).values('name')
print(res)  # <QuerySet [{'name': '北方出版社'}]>

2.查询书籍pk为2的作者姓名和邮箱

# 方式1:正向查询
res =models.Book.objects.filter(pk=2).values('authors__name','authors__email')
print(res)  # <QuerySet [{'authors__name': 'zhang', 'authors__email': '[email protected]'}, {'authors__name': 'liu', 'authors__email': '120qq.com'}]>  不需要像上面基本方法的跨表查询,这里不需要用点.all()

# 方式2:反向查询
res =models.Author.objects.filter(book__pk=2).values('name','email')
print(res)  # <QuerySet [{'name': 'zhang', 'email': '[email protected]'}, {'name': 'liu', 'email': '120qq.com'}]>

总结models后面点的谁 就以谁为基表

3.查询作者是egon的家庭地址

# 方式1:正向查询
res=models.Author.objects.filter(name='egon').values('author_detail__addr')
print(res)

# 方式2:反向查询
res=models.AuthorDetail.objects.filter(author__name='egon').values('addr')
print(res)

4.查询出版社是东方出版社出版过的书的名字

# 方式1:正向查询
res = models.Publish.objects.filter(name='东方出版社').values('book__title')
print(res)

# 方式2:反向查询
res = models.Book.objects.filter(publish__name='东方出版社').values('title')
print(res)

5.查询书籍pk是2的作者的手机号

# 方式1:正向查询
res=models.Book.objects.filter(pk=2).values('authors__author_detail__phone')
print(res)

# 方式2:反向查询
res=models.Author.objects.filter(book__pk=2).values('author_detail__phone')
print(res)

聚合函数

1.关键字 aggregate

2.还需要导入模块
from django.db.models import Max, Min, Sum, Avg, Count

from django.db.models import Max, Min, Sum, Count, Avg
 # 1.筛选出价格最高的书籍的
 res = models.Book.objects.aggregate(mr = Max('price'))
 print(res)  # {'mr': Decimal('222.99')}
    
 # 2.求书籍总价格
  res = models.Book.objects.aggregate(sm = Sum('price'))
  print(res)  # {'sm': Decimal('600.58')}

  # 3.求书籍平均价格
  res = models.Book.objects.aggregate(av = Avg('price'))
  print(res)   # {'av': 150.145}
  # 4.一起使用
  res=models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('price'),Avg('price'))
  print(res)
'''
{'price__max': Decimal('222.99'), 'price__min': Decimal('88.80'), 'price__sum': Decimal('600.58'), 'price__count': 4, 'price__avg': 150.145}
'''

分组查询

1.关键字 annotate
2.借助于聚合函数
from django.db.models import Max, Min, Sum, Avg, Count

1.统计每一本书的作者个数 书名 和对应的作者人数

res=models.Book.objects.annotate(author_num=Count('authors__id')).values('title','author_num')
print(res)

2.统计出每个出版社卖的最便宜的书的价格 出版社的名字 价格

res=models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)

3.统计不止一个作者的图书

res=models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num')
print(res)

4.查询各个作者出的书的总价格 作者名字 总价格

res=models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)

F 查询与Q查询

F查询

  1. 查询库存数大于卖出数的书籍
from django.db.models import F,Q
res = models.Book.objects.filter(kun_cun__gt = F('mai_cun')).values('title')  # 后面的条件是来自于数据库的其他字段值
print(res)  # <QuerySet [{'title': '天龙八部'}]>

2.将所有书的价格上涨100块

models.Book.objects.all().update(price=F('price') + 100)

3.将所有书的名称后面全部加上 "爆款" 后缀

from django.db.models.functions import Concat
from django.db.models import Value
ret3 = models.Book.objects.update(title=Concat(F('title'), Value('新款')))

Q查询

1.查询一下书籍名称是三国演义 或者 库存数是500的书籍

from django.db.models import Q
res = models.Book.objects.filter(title='三国演义',kun_cun=500)  # and关系
res = models.Book.objects.filter(title='三国演义',kun_cun=500)  # and关系
res = models.Book.objects.filter(Q(title='三国演义'),Q(kun_cun=500))# Q包裹之后逗号还是and关系
res = models.Book.objects.filter(Q(title='三国演义') | Q(kun_cun=500))  #  |就是or的关系
res = models.Book.objects.filter(~Q(title='三国演义')|Q(kun_cun=500))  # ~就是not关系
print(res)

Q对象高级用法

q = Q()
q.connector = 'or'  # 默认是and  可以改成or
q.children.append(('title','三国演义'))
q.children.append(('kun_cun__gt',500))
res = models.Book.objects.filter(~q)  # 取反
print(res)

from django.db import transaction

with transaction.atomic():
    # 在缩进的代码中书写数据库操作
    # 该缩进内的所有代码 都是一个事务
    pass

orm字段及参数

orm字段及参数
    CharField     varchar
    IntegerField   int
    BigIntegerField   bigint
    EmailField    varchar(254)
    DateField
    DateTimeField
    
    auto_now:每次修改数据的时候 都会自动将当前修改时间更新上去  实时更新
    auto_now_add:在创建数据的时候 会将当前时间自动记录 之后不会自动修改  除非你人为修改
        
     AutoField     auto_increment   

     BooleanField    布尔值  
        该字段在存储的时候 你只需要传布尔值True或False
         它会自动存成1/0
        
     TextField  专门用来存大段文本

     FileField  专门用来文件路径   '/etc/data/a.txt'   
        upload_to = '/etc/data'
        给该字段传值的时候 直接传文件对象
        会自动将文件对象保存到upload_to后面指定的文件路径中
        然后将路径保存到数据库
        
    DecimalField(Field)
    - 10进制小数
    - 参数:
        max_digits,小数总长度
        decimal_places,小数位长度

自定义char字段

# 如何自定义字段类型
class MyCharField(models.Field):
    def __init__(self,max_length,*args,**kwargs):
        self.max_length = max_length
        # 重新调用父类的方法
        super().__init__(max_length=max_length,*args,**kwargs)

     def db_type(self, connection):
         return 'char(%s)'%self.max_length

猜你喜欢

转载自www.cnblogs.com/zhangchaocoming/p/11955422.html
今日推荐