day64——orm单表操作/多表操作

模型层

orm单表操作

测试脚本

当你只需要对某一个py文件内容进行测试的时候,不用书写前后端交互的形式,而是直接写一个测试脚本,在脚本中导入需要测试的py文件进行测试即可。

我们在创建应用的时候,应用文件夹下会有个test.py文件,但并不是进到该文件中就能够进行测试看,还需要在文件内准备一下测试环境,当然也可以自己创建一个测试脚本文件然后在配置测试环境也是可以的。

测试环境准备:

# 去manage.py文件中拷贝前四行代码,然后自己书写两行
import os 
#import sys  # sys模块没有用到可以将这一行导入代码删除
if __name__ == "__main__":
    os.environ.setdefault(("DJANGO_SETTINGS_MODULE", "day64.settings")
    import django  # 这行导入必需写在"__main__"下面
    django.setup()                      
    # 在这个代码块下就可以测试django里面的任意一个py文件了                                

创建模型表:

# models.py
class User(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    register_time = models.DateField()

    def __str__(self):
        return f'对象:{self.name}'
    """
    为了让测试结果更更加清晰,可以在User模型类中定义一个__str__方法,对象被打印	   时自动触发"""

针对DateField/DateTimeField字段有两个重要的参数

  • auto_now:每次操作数据的时候,该条数据的DateField/DateTimeField字段的值就会跟新成当前操作的时间,多应用与日志当中
  • auto_now_add:只记录创建数据的时间,之后不会自动修改修改,多用于记录用户的注册时间

基本的增删改操作

 django.setup()
 from app01 import models

# 增(两种方式)
res = models.User.objects.create(name='jason',age=18,register_time='2002-1-20')  # 可以指定日期的格式
print(res)

import datetime
ctime = datetime.datetime.now()
res = models.User(name='egon',age=28,ctime)  # 注册时间还可以一个日期对象,自动记录创建数据的时间
print(res)
res.save()


# 删(两种方式)
res = models.User.objects.filter(pk=2).delete()
print(res)

res = modles.User.objects.filter(pk=2)
print(res)
res.delete()
# 补充
"""
pk会自动查找当前表的主键字段,pk=2查找User表主键字段值为2的数据;
用了pk之后,就不需要知道表主键字段叫什么名字,推荐使用"""


# 改(两种方式)
res = modls.User.objects.filter(pk=1).update(name='JASON')
print(res)

res = models.User.objects.filter(pk-1)
res.name = 'JASON'
print(res)

查询必知必会13条

# 1 .all()  查所有数据

# 2 .filter()  带有过滤条件的查询,类似于sql语句中的where

# 3 .get()  直接拿数据对象,但不推荐使用,条件不存在会报错
 res = models.User.objects.filter(pk=1).get()
    
# 4 .first()  拿queryset里面的第一个元素
res = models.User.objects.all().first()

# 5 .last()  拿最后一个元素
res= models.User.objects.all().last()

# 6 .values()  可以指定查询的字段,类似于select name, age from ...
res = models.User.objects.values('name','age')
print(res)
"""
[{'name': 'jason', 'age': 18}, {'name': 'egonPPP', 'age': 84}]
可以理解为列表套字典,字段名作为字典的key,对应的值作为value"""

# 7 .values_list()  ~~
res = models.User.objects.values_list('name','age')
print(res)
"""
[('jason', 18), ('egonPPP', 84)]
可以理解为列表套元祖的形式"""

# 8 .distinct()  去重
"""
去重一定要是一模一样的数据,数据的主键肯定不同,
意味着需要先将主键排除在外,再进行去重"""
res = modles.User.objects.values('name','age','register_time').distinct()

# 9 .order_by()  分组
res = models.User.objects.order_by('age')  # 默认是升序
res = models.User.objects.orser_by('-age') # 字段名前加负号改为降序

# 10 .reverse() 数据反转,数据必须是排过序的才能反转,跟在order_by之后使用
res = models.User.objects.order_by('age').reverse()

# 11 .count()  统计当前数据的个数
res = models.User.objects.count()
print(res) # 2

# 12 .exclude()  # 排除满足条件的,其他全获取
res = models.User.objects.exclude(age='18')

# 13 .exists()  判断数据是否存在,几乎用不到,因为数据本身自带布尔值。
res = models.User.objects.exists(name='jason')

查询内部sql语句的方式

  • 方式一:.qurey

    res = models.User.objects.values_list('name','age')  # QuerySet对象
    print(res.query)
    # 前提条件必须是QuerySet对象才能.query进行查询
    
  • 方式二:settings.py配置文件中增加配置

    # 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',
            },
        }
    }
    

    orm操作数据时,自动打印内部sql 语句。

好用的双下滑线查询

数据准备:

id name age register_time
1 jason 20 1999-08-11
2 tank 30 2020-05-01
3 egon 40 2020-03-20
4 alex 50 2020-06-24
5 JaSon 20 1999-08-11
  • 查询年龄大于30岁的数据__gt

    res = models.User.objects.filter(age__gt=30)
    print(res)
    """
    <QuerySet [<User: 对象:egon>, <User: 对象:alex>]>"""
    
  • 查询年龄小于30岁的数据__lt

    res = models.User.objects.filter(age__lt=30)
    print(res)
    """
    <QuerySet [<User: 对象:jason>, <User: 对象:jaSon>]>"""
    
  • 查询年龄大于等于30岁的数据__gte

    res = models.User.objects.filter(age__gte=30)
    print(res)
    """
    <QuerySet [<User: 对象:tank>, <User: 对象:egon>, <User: 对象:alex>]>"""
    
  • 查询年龄小于等于30岁的数据__lte

    res = models.User.objects.filter(age__lte=30)
    print(res)
    """
    <QuerySet [<User: 对象:jason>, <User: 对象:tank>, <User: 对象:jaSon>]>"""
    
  • 查询年龄是20、30或者40岁的数据__in

    res = models.User.objects.filter(age__in=[20,30,40])
    print(res)
    """
    <QuerySet [<User: 对象:jason>, <User: 对象:tank>, <User: 对象:egon>, <User: 对象:jaSon>]>"""
    
  • 查询年龄在20岁与40岁之间的数据,包含30岁和40岁首位都要__range

    res = models.User.objects.filter(age__range=[30,40])
    print(res)
    """
    <QuerySet [<User: 对象:tank>, <User: 对象:egon>]>"""
    
  • 查询名字中带有小写字母s的数据,模糊查询__contains,默认区分大小写

    res = models.User.objects.filter(name__contains='s')
    print(res)
    """
    <QuerySet [<User: 对象:jason>]>"""
    
  • 查询名字中带字母S/s的数据,模糊查询__icontains,忽略大小写

    res = models.User.objects.filter(name__icontains='s')
    print(res)
    """
    <QuerySet [<User: 对象:jason>, <User: 对象:jaSon>]>"""
    
  • 查询名字以字母e开头的数据__startswith

    res = models.User.objects.filter(name__startswith='e')
    print(res)
    """
    <QuerySet [<User: 对象:egon>]>"""
    
  • 查询名字以字母x结尾的数据__endswith

    res = models.User.objects.filter(name__endswith='x')
    print(res)
    """
    <QuerySet [<User: 对象:alex>]>"""
    
  • 查询注册年份是2020年的数据、查询注册月份是5月的数据、查询20号注册的数据:register_time__year__month__day

    res = models.User.objects.filter(register_time__year='2020')
    print(res)
    res1 = models.User.objects.filter(register_time__month='5')
    print(res1)
    res2 = models.User.objects.filter(register_time__day='20')
    print(res2)
    """
    <QuerySet [<User: 对象:tank>, <User: 对象:egon>, <User: 对象:alex>]>
    <QuerySet [<User: 对象:tank>]>
    <QuerySet [<User: 对象:egon>]>"""
    

orm多表操作

前期准备:

# models.py
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)
    publish = models.ForeignKey(to='Publish')
    author = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    email = models.EmailField()  # 本质是varchar(254)  该字段不是给models看的,而是给后面我们学到的校验性组件看的


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail')


class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=32)

手动给出版社表、作者表、作者详情表录入准备数据

  • 出版社数据

    id name addr email
    1 东方出版社 东方 111@qq.com
    2 北方出版社 北方 222@qq.com
  • 作者表数据

    id name age author_detail_id
    1 jason 18 1
    2 egon 19 2
    3 tank 20 3
  • 作者详情表数据

    id phone addr
    1 123 芜湖
    2 124 山东
    3 125 惠州

一对多外键增删改

  • # 方式一
    models.Book.objects.create(title='论语',price=899.23,publish_id=1)
    # publish_id为book表中实际存在的字段,直接赋值数字
    
    # 方式二
    publish_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.create(title='红楼梦',price=666.23,publish=publish_obj)
    """
    # publish为book表中的虚拟字段,可以放查询到的出版社对象,
    创建数据后publish_id后会自动到出版社对象的id值"""
    
  • # 方式一
    models.Publish.objects.filter(pk=1).delete()
    # 级联删除,删除一条出版社数据后,book表中该出版社出版的书也会跟着删除
    
    # 方式二
    publish_obj = models.Publish.objects.filter(pk=1).delete()
    publish_obj.delete() # 级联删除
    
  • #方式一
    models.Book.objects.filter(pk=1).update(publish_id=2)  # 直接修改字段的值 级联更新
    
    # 方式二
    publish_obj = models.Publish.objects.filter(pk=1).first()
    models.Book.objects.filter(pk=1).update(publish=publish_obj)
    """
    publish为book表中的虚拟字段,可以放查询到的出版社对象,
    publish_id后会自动修改成出版社对象的id值"""
    
    

多对多外键增删改

  • """
    多对多外键操作,就相对于在操作第三张关系表,
    但是第三张关系表是orm自动给我们创建的,句点符无法直接调用,
    我们可以间接的操作这张表"""
    
    # 给id为1的书籍增加作者
    book_obj = models.Book.objects.filter(pk=1).first()  # 查询出书籍对象
    book_obj.author
    """
    书籍对象点虚拟字段(建表关系的外键字段)就类似于已经到了第三张关系表中"""
    # 方式一,括号内放作者id
    book_obj.author.add(1) # 给id为1的书籍绑定一个主键为1的作者
    book_obj.author.add(2,3) # 支持放多个id绑定,给书籍绑定多个作者
    
    # 方式二,括号内放查询到的作者对象,也支持放多个
    author_obj1 = models.Author.objects.filter(pk=2).first()
    author_obj2 = models.Author.objects.filter(pk=3).first()
    book_obj.author.add(author_obj1,author_obj2)
    

    add给第三张关系表添加数据,括号内既可以传数字也可以传对象 并且都支持多个 。

  • # 方式一
    book_obj.author.remove(2) # 级联删除
    book_obj.author.remove(1,3)
    
    # 方式二
    author_obj = models.Author.objects.filter(pk=2).first()
    author_obj1 = models.Author.objects.filter(pk=3).first()
    book_obj.author.remove(author_obj,author_obj1)
    

    remove删除数据的时候,括号内既可以传数字也可以传对象 并且都支持多个

  • 修改

    # 方式一
    book_obj.authors.set([1,2])  # 括号内必须给一个可迭代对象
    book_obj.authors.set([3])  # 支持多个
    
    # 方式二
    author_obj = models.Author.objects.filter(pk=2).first()
    author_obj1 = models.Author.objects.filter(pk=3).first()
    ook_obj.author.set([author_obj,author_obj1])  
    
    # 清空
    # 在第三张关系表中清空某个书籍与作者的绑定关系
    book_obj.author.clear()
    # clear括号内不要加任何参数
       
    

    set括号内必须传一个可迭代对象,该对象内既可以是数字也可以是对象,并且都支持多个。

正方向的概念

就是看查询的时候,外键字段在哪一方

假设表A和表B是多对多关系的两张表,外键建在表A哪儿

​ 正向:由表A查表B为正向,外键在表A手上

​ 反向:由表B查表A为反向,外键不再表B手上

"""
  book——>外键字段在书那儿(正向)——>publish
  publishk——>外键字段在书那儿(反向)——>book"""

一对一和多对多正反向的判断也是如此,判断好方向后,正反向查询数据的方法是:

​ 正向查询点字段、反向查询点表名小写;正向查结果可为多加.all()、反向查询结果可为多加_set.all()

多表查询

子查询(基于对象的跨表查询)

正向:

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

book_obj = models.Book.objects.filter(pk=1).first()
res=book_obj.publish  # 正向查询点字段,结果唯一不用加.all()
print(res.name)

2.查询书籍主键为1的作者

book_obj = models.Book.objects.filter(pk=1).first()
res =book_obj.author.all()  # 正向查询结果可为多个加.all()
print(res.name)

3.查询作者jason的电话号码

author_obj = models.Author.objects.filter(name='jason').first()
res = author_obj.author_detail  # 正向查询结果唯一不用加.all()
print(res.phone)

反向:

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

publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set.all()  # 反向查询点表名小写,结果可以为多个加_set.all()
print(res.title)

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

author_obj = models.Author.objects.filter(name='jason').first()
res = author_obj.book_set.all()  # 反向查询结果可以为多个加_set.all()
print(res.title)

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

author_detail_obj = models.AuthorDetail.objects.filter(phone='123').first()
res = author_detail_obj.author  # 反向查询点表名小写,结果唯一什么都不加
print(res.name)

联表查询(基于双下滑线的跨表查询)

上述子查询运用的是句点符实现了正向/反向查询,而在filter()内和values()内也支持正反向的操作,values内取值用__xxx,下面用几个练习题具体来展示

1.查询姓名为jason的作者的手机号和年龄

# 方式一:values()内跨表
res = models.Author.objects.filter(name='jason').values('name','author_detail__phone')
# 'author_detail__phone':'author_detail'跨到作者详情表,'__phone'取手机号
print(res)

# 方式二:filter()内跨表
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age')
# author__name='jason':拿作者姓名是jason的作者详情,'author__age':跨到作者表,__age取年龄
print(res)

下面几道题用到的方法相同。

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

# 方式一
res = models.Book.objects.filter(pk=1).values('title','publish__name')
print(res)

# 方式二
res = models.Publish.objects.filter(book__id=1).values('book__title','name')
print(res)

3.查询书籍主键为1的作者姓名

# 方式一
res = models.Book.objects.filter(pk=1).values('title','author__name')
print(res)

# 方式二
models.Author.objects.filter(book__id=1).values('book__title','name')
print(res)

4 查询书籍主键是1的作者的手机号

涉及到三张表(Book/Author/AuthorDetail)

# values()内跨表
res = models.Book.objects.filter(pk=1).values('author__author_detail__phone')
# 'author__author_detail__phone':'author'由书籍表跨到作者表,'__author_detail'由作者表跨到作者详情表,'__phone'取电话号码
print(res)

"""
只要掌握了正反向的概念和下划线的使用就可以无限制的跨表查询"""
posted @ 2020-06-01 00:08  风起千寻  阅读(81)  评论(0编辑  收藏  举报