ORM(上)

内容概要

  • 模型层之前期准备工作
  • ORM常用关键字
  • ORM执行SQL语句
  • 神奇的双下划线查询
  • ORM外键字段的创建
  • 外键字段数据的增删改查
  • 多表查询(基于对象的跨表查询、基于双下划线的跨表查询)

模型层之前期准备工作

  1. 自带的sqlite3数据库对时间字段不敏感 有时候会展示错乱 素以我们习惯切换成常见的数据库比如MySQL django orm并不会自动帮你创建库 所以需要自己提前准备好

  2. 单独测试django某个功能层

    默认不允许单独测试某个py文件 如果想要测试某个py文件(能单独测试也就 models.py了)

    • 测试环境1: pycharm提供的python console

    • 测试环境2:自己搭建(自带的test或者自己创建)

      1. 拷贝manage.py前四行

        import os
        import sys # 用不到可以删掉
        
        
        def main():
            os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoday05.settings')
        
      2. 自己在加两行

        import django
        django.setup()
        
      3. django orm底层还是SQL语句 我们是可以查看的

        如果我们手上是一个QuerySet对象 那么可以直接点query查看

        如果想查看所有orm底层的SQL语句也可以在配置文件添加日志记录

        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常用关键字

  1. create

        res = models.User.objects.create(name='jason', age=18)
        print(res)  # 用户对象jason
    

  2. filter() 里面不写入条件 则查所有 写了就可以当成(where后面的条件 ,多个条件是 and关系)

        # res = models.User.objects.filter()  # 如果什么都不写则查所有
        # res 是一个 QuerySet 里面可以看成列表套字典
    
        # print(res)  # <QuerySet [<User: 用户对象jason>, <User: 用户对象jason>, <User: 用户对象kevin>, <User: 用户对象oscar>, <User: 用户对象lili>, <User: 用户对象tom>]>
        res = models.User.objects.filter(name='jason')
        print(res  # <QuerySet [<User: 用户对象jason>]>
    

  3. updateQuerySet的方法不是对象的方法

    models.User.objects.filter(name='jason').update(name='weiwei')
    # 如果不加筛选条件 则 修改全部
    res = models.User.objects.filter()
    print(res)  
    # <QuerySet [<User: 用户对象weiwei>, <User: 用户对象kevin>, <User: 用户对象oscar>, <User: 用户对象lili>, <User: 用户对象tom>]>
    

  4. delete也是QuerySet的方法不是对象的方法

        models.User.objects.filter(name='tom').delete()
        # 如果不加筛选条件 则 删除全部
        res = models.User.objects.filter()
        print(res)
    

  5. first()filter()的方法取它的第一个值

    res = models.User.objects.filter().first()
    res1 = models.User.objects.filter()[0]
    print(res)
    print(res1)
    

    这样看虽然一样 ,但是取一个不存在的索引取值直接报错

    而first()则返回None

        res = models.User.objects.filter(id=100).first()
        print(res)
    

    res1 = models.User.objects.filter(id=100)[0]
    print(res1)
    

    而我们 那安排需求没有实现,我们也不希望报错

  6. last()filter()的最后一个值

    res = models.User.objects.filter().last()
    print(res)
    

  7. all()filter()一样但是语义不同 all()一看就知道是获取全部数据,filter()是筛选条件

    res = models.User.objects.all().values('name')
    print(res)
    

  8. values()根据指定的字段获取数据

        res = models.User.objects.filter().values('name')
        # 如果不写限制条件则 返回每条数据的全部
        # 如果写了限制条件则 返回每条数据的这个字段
        print(res)
    

    可以看到这个QuerySet里面是列表套字典 字典的键是限制的字段名值是对应的值

  9. values_list() 也是根据指定的字段获取数据

    res = models.User.objects.filter().values_list('name')
    print(res)

​ 获取的是列表套元组

  1. distinct() 去重 数据一定要一模一样,一定要不主键字段排除在外,有主键字段在根本去重不了

    res = models.User.objects.filter().values('name')
    print(res)
    res = models.User.objects.filter().values('name').distinct()
    print(res)
    

    res = models.User.objects.filter()
    print(res)
    res = models.User.objects.filter().distinct()
    print(res)
    

  2. order_by() 根据指定条件排序 默认是升序

    res = models.User.objects.filter().values('name', 'age')
    print(res)
    print('-------------------------------------')
    res = models.User.objects.filter().order_by('age').values('name', 'age')
    print(res)
    

    如果想降序,则把条件前面加符号

    res = models.User.objects.filter().values('name', 'age')
    print(res)
    print('-------------------------------------')
    res = models.User.objects.filter().order_by('-age').values('name', 'age')
    print(res)
    

  3. get() 根据条件筛选数据并直接获取到数据对象,一旦条件不存在会直接报错 不建议使用

    res = models.User.objects.get(pk=100)
    print(res)
    

```python
res = models.User.objects.filter(pk=100)
print(res)
```

![](https://img2023.cnblogs.com/blog/2490064/202212/2490064-20221214215242321-72923446.png)


而 `filter`不会
  1. exclude()取反操作

    res = models.User.objects.filter().values('name', 'age')
    print(res)
    res = models.User.objects.filter().values('name', 'age').exclude(age=18)
    print(res)
    

  1. reverse() 颠倒顺序(被操作的对象必须是已经排过序的才可以)

    res = models.User.objects.filter().values('name', 'age')
    print(res)
    print('--------------------')
    res = models.User.objects.filter().values('name', 'age').reverse()
    print(res)
    print('--------------------')
    res = res = models.User.objects.filter().values('name','age').order_by('age')
    print(res)
    print('--------------------')
    res = models.User.objects.filter().values('name','age').order_by('age').reverse()
    print(res)
    

ORM执行SQL语句

有时候ORM的操作效率可能偏低 我们自己编写SQL的

方式1:

res = models.User.objects.raw('select * from app01_user;')
print(list(res))

只能执行查询,如果要执行增删改还得需要别的执行sql方法

方式2:

    import pymysql
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='db6',
                           charset='utf8',autocommit=True)
    cursor = conn.cursor()
    cursor.execute('insert into app01_user(name, age) values("lili", 18);')
    cursor.execute('select * from app01_user')
    res = cursor.fetchone()
    for i in res:
        print(i)

方式3:

from django.db import connection
cursor = connection.cursor()
cursor.execute('insert into app01_user(name, age) values("kevin", 19)')
cursor.execute('select * from app01_user')
res = cursor.fetchall()
for i in res:
    print(i)

from django.db import connection 是连接一个数据库的

from django.db import connections  可以连接多个数据库

神奇的双下划线查询

只要是QuerySet对象都可以无限制的点QuerySet对象的方法

  1. __gt大于

    res = models.User.objects.filter(age__gt=18).values('name', 'age')
    print(res)
    大于等于
    res=models.User.objects.filter(age__gte=18).values('name','age')
    print(res)
    

  2. __lt小于

    res = models.User.objects.filter(age__lt=38).values('name','age')
    print(res)
    小于等于
    res = models.User.objects.filter(age__lte=38).values('name', 'age')
    print(res)
    

  3. 查询年龄是18或者28,38的数据

    res = models.User.objects.filter(age__in=(18,28,38)).values('name','age')
    print(res)
    

  4. 查询年龄在18到38范围之内的

    res = models.User.objects.filter(age__range=(18,38)).values('name','age')
    print(res)
    

  5. 查询名字含有字母j的数据

    res = models.User.objects.filter(name__contains='j').values('name','age')
    print(res)
    

    大小写模糊的

    res = models.User.objects.filter(name__icontains='j').values('name','age')
    print(res)
    

  6. 查询注册年份是2022的数据

    res = models.User.objects.filter(register__year=2022).values('name','register')
    print(res)
    

ORM外键字段的创建

在MySQL中的表关系有
一对多
外键创建在多的一方
多对多
外键创建在第三张表
一对一
外键创建在热数据的一方 热数据就是访问频率高的一方
没有关系
  1. 创建基础表(书籍表,出版社表,作者表,作者详情表)

    class Book(models.Model):
        """书的表 """
        name = models.CharField(max_length=32, verbose_name='书名')
        price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格')
        # 书跟出版社 一本书的版权只能有一个出版社 出版社可以有多本书
        # 外键创建在多的一方  这一对多必须要有 on_delete 要不然报错
        publish = models.ForeignKey(to='publish', on_delete=True, verbose_name='外键 出版社')
        # 书跟作者  一本书可以有多个作者  一个作者可以写多本书
        # 多对多 orm会在自动创建第三张表 你也可以自己创建第三张表
        authors = models.ManyToManyField(to='Author')
    
    
    class Publish(models.Model):
        """ 出版社 """
        name = models.CharField(max_length=32, verbose_name='出版社名称')
        addr = models.CharField(max_length=64, verbose_name='出版社地址')
    
    
    class Author(models.Model):
        """作者表"""
        name = models.CharField(max_length=32, verbose_name='名字')
    
        # 作者跟作者详情表 是一对一的关系
        # 外键可以创建在 任意一个表中
        author_detail = models.OneToOneField(to='AuthorDetail', on_delete=True, verbose_name='外键 作者详情')
    
    
    class AuthorDetail(models.Model):
        phon = models.CharField(max_length=32, verbose_name='手机号')
        age = models.IntegerField(verbose_name='年龄')
    
  2. 确认外键关系

    一对多 ORM与MySQL一致 外键字段建立在多的一方

    多对多 ORM与MySQL有更多的变化

    1. 外键字段可以直接建立在某张表中(查询频率较高的)

      内部会自动创建第三张表关系

    2. 自己创建第三张表关系并创建外键字段

    一对一 ORM与MySQL一致 外键字段建在查询较高的一方

  3. ORM创建

    针对一对多和一对一同步到表中会自动加_id后缀

    自动创建第三张表

外键字段相关操作

针对一对多 插入数据直接填写表中字段

models.Book.objects.create(name='sanguo',price=888.88,publish_id=2)
    models.Book.objects.create(name='shitouji',price=888.88,publish_id=3)
    models.Book.objects.create(name='xiyouji',price=888.88,publish_id=4)
    models.Book.objects.create(name='shuixuzhuan',price=888.88,publish_id=2)

# 针对一对多 插入数据也可以填写表中的类中字段名
# publish_obj = models.Publish.objects.filter(pk=1).first()
# models.Book.objects.create(title='水浒传', price=555.66, publish=publish_obj)
"""针对多对多的关系绑定"""
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.add(2)

book_obj.authors.add(2,3)
"""如果关系已有则不管,"""

author_obj = models.Author.objects.filter(pk=1).first()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.add(author_obj)
可以放一个数字 也可以放一个作者对象

修改关系

book_obj = models.Book.objects.filter(pk=2).first()
book_obj.authors.set((1, 2))  # 里面必须是一个参数

author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set((author_obj1, author_obj2)) # 也可以放一个对象

删除关系

author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.remove(author_obj1, author_obj2)

清空关系

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

add 增加关系
对象.外键.add(可以是被关联对象也可以是int类型)
remove
对象.外键.remove(可以是被关联对象也可以是int类型)
set
对象.外键.set((可以是被关联对象,也可以是int类型))必须在容器里
clear()
对象.外键.clear()

ORM跨表查询

"""
复习MySQL跨表查询的思路
子查询
	分布操作:将一条SQL语句用括号括起来当做另外一条SQL语句的条件
连表操作
	先整合多张表之后基于单表查询即可
	inner join  内连接
	left  join  左连接
	ringht join 右连接
"""
正反向查询的概念(重要)
	正向查询
     由外键字段所在的表数据查询关联的表数据 正向
   反向查询
	 没有外键字段的表数据查询关联的表数据  反向
    
ps:正反向的核心就看外键字段在不在当前数据所在的表中

ORM跨表查询的口诀(重要)
	正向查询按外键字段
	反向查询按表名小写

基于对象的跨表查询

1.查询主键为2的书籍对应的出版社名称
"""
先获取主键为2的书籍对象
书籍到 出版社  外键字段在 数据 正向 点外键字段
"""
book_obj = models.Book.objects.filter(pk=2).first()
# book_obj.publish 这样一点就到了 publish的这张表了
print(book_obj.publish.name)
2.查询主键为4的书籍对应的作者姓名
"""
先获取主键为4的数据对象 
书籍找作者 外键在书籍 正向  点外键
"""
book_obj = models.Book.objects.filter(pk=4).first()
print(book_obj.authors)
# app01.Author.None 碰到这种不要怕这就离成功就差一步了
print(book_obj.authors.all())  # 获取里面的数据
print(book_obj.authors.all().first().name)  # 那里面对象的name

# 3.查询jason的电话号码
"""
    先获取jason对象 然后去 作者详情表拿电话号码

    """
# author_obj = models.Author.objects.filter(name='jason').first()
# print(author_obj.author_detail.phon)
# 4.查询BeiFang出版过的书籍
"""
    先获取BeiFang的对象 
    然后从出版社 到 书记 外键 在数据 反向  反向表名小写
    """
# publish_obj = models.Publish.objects.filter(name='BeiFang').first()
# print(publish_obj.book_set)  # app01.Book.None
# """看到 app01.Book.None"""
# print(publish_obj.book_set.all().values('name'))

# 5.查询jason写过的书籍
"""
    先获取json对象 
    然后从 作者到书籍 主键在 书籍 反向 
    反向点书名小写
    """
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set)
print(author_obj.book_set.all().values('name'))
"""
    app01.Book.None
    <QuerySet [{'name': 'shitouji'}, {'name': 'xiyouji'}, 			{'name': 'shuixuzhuan'}]>
    """

基于双下划线的跨表查询

就是把上面的操作两行变为一行

# 1.查询主键为2的书籍对应的出版社名称
"""
先获取书籍对象
然后看正反向
书籍到出版社 外键在书籍  正向
"""
# res = models.Book.objects.filter(pk=2).values('外键__外键的字段')
res = models.Book.objects.filter(pk=2).values('publish__name', 'name')
print(res)

# 2.查询主键为4的书籍对应的作者姓名
"""
书籍到作者 正向
"""
res = models.Book.objects.filter(pk=4).values('authors__name')
print(res)
# 3.查询jason的电话号码
res = models.Author.objects.filter(name='jason').values('author_detail__phon')
print(res)
# 5.查询jason写过的书籍名称
"""
反向  表名小写
"""
res = models.Author.objects.filter(name='jason').values('book__name')
print(res)
# 6.查询电话号码是110的作者姓名
res = models.AuthorDetail.objects.filter(phon=110).values('author__name')
print(res)

ps:当关系是多的时候需要加_set

进阶操作

    # 1.查询主键为2的书籍对应的出版社名称
    """
    反着写 先写 搞出版社对象
    出版社到书籍反向 反向表名小写
    """
    res = models.Publish.objects.filter(book__pk=2).values('name')
    # print(res)

    # 2.查询主键为4的书籍对应的作者姓名
    res = models.Author.objects.filter(book__pk=4).values('name')
    # print(res)

    # 3.查询jason的电话号码
    res = models.AuthorDetail.objects.filter(author__name='jason').values('phon')
    # print(res)

    # 4.查询BeiFang出版社出版过的书籍名称和价格
    # res = models.Book.objects.filter(publish__name='BeiFang').values('name', 'price')
    # print(res)

    # 5.查询jason写过的书籍名称
    res = models.Book.objects.filter(authors__name='jason').values('name')
    print(res)

    # 6.查询电话号码是110的作者姓名
    res = models.Author.objects.filter(author_detail__phon=110).values('name')
    print(res)
main()
posted @ 2022-12-15 22:01  可否  阅读(18)  评论(0编辑  收藏  举报