django入门菜鸟教程:ORM查询操作详解

2022年9月18日11:04:59后端程序开发评论74 views字数 10334阅读模式

表查询数据准备以及测试环境搭建

1.数据库介绍

Django自带一个小型数据库:sqlite3数据库。该数据库功能十分有限,并且针对日期类型的数据兼容性差,所以我们选择用MySQL数据库。

2.mysql数据库配置

Django1.x 
	import pymysql
	pymysql.install_as_MySQLdb()
Django2.x 3.x 4.x
	pip install mysqlclient

配置文件信息:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'day59',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'CHARSET': 'utf8'
    }
}

3.自定义模型类

class User(models.Model):
    uid = models.AutoField(primary_key=True, verbose_name='编号')
    name = models.CharField(max_length=32, verbose_name='姓名')
    age = models.IntegerField(verbose_name='年龄'),
    join_time = models.DateField(auto_now_add=True)
    def __str__(self):	
        return f'数据对象:{self.name}'
	# __str__方法:对象被执行打印操作的时候自动执行,该方法需返回一个字符串,可以作为我们测试时的提示信息

主要关键字介绍:
    primary_key:指定主键 在django中 主键字段可以默认不写,如果不写,则会自动添加一个名为id的主键字段。如果写了则用我们自己定义的。
    verbose_name:字段说明,相当于是备注,方便见名知意。
    DateField:年月日
    DateTimeField:年月日时分秒
    auto_now:每次操作数据库并保存都会自动更新当前时间
    auto_now_add:只在创建数据的时候自动获取当前时间,以后除非人为改动,否则永不改变。

4.执行数据库迁移命令

模型类>>>数据表

makemigrations
migrate

5.模型层测试环境准备

方式1:在任意的空py文件中准备环境
    import os
    # Create your tests here.
    def main():
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings')
        import django
        django.setup()
        from app01 import models

    if __name__ == '__main__':
        main()

方式2:pycharm提供测试环境
	python console命令行测试环境

6.django删除表重建的实现方法

使用migrate同步数据到数据库上时遇到了一些问题,记录下来。比如设计表的时候,手贱直接删除了一张表,然后就一直无法生成表了。 或者已经删除了这张表,执行migrate时候,一直说"Table 'hello_xxx' already exists",都是血与泪的坑

手贱删除表 如果有一天你手痒了,删除了一张自己设计的表,你会发现,不管你怎么执行makemigrations和migrate都无法自动生成新的表了。 就算你删除app_name/migrations下面的0001_initial.py和其它文件,重新执行makemigrations和migrate依然无法查询生成表,提示" No migrations to apply." 就算你重新改过models.py的标名称,还是一样的,不会自动创建。

正确的方法如下:
1.先到数据库把表删掉:drop table
2.注释django中对应的Model
3.执行以下命令:
python manage.py makemigrations  
python manage.py migrate --fake
4.去掉注释重新迁移
python manage.py makemigrations  
python manage.py migrate

ORM表查询常见关键字

前排排坑:
1.当需要查询数据主键字段值时,可以直接使用pk(primary key)忽略真正的主键名字
2.在模型类中定义一个__str__方法,便于我们后续执行对象打印操作时查看方便
3.QuerySet中如果是列表套对象的形式,可以支持for循环和列表索引取值(不支持负数索引)
4.虽然QuerySet支持索引,但当其内部没有数据的时候,索引会报错,更推荐使用first()
5.create()	创建数据,可以有返回值,返回的就是当前创建的数据对象(相当于创建之后又查询一次)
5.update()	更细数据
6.delete()	删除数据
1.filter()	# 核心
筛选数据,相当于是SQL里的where,返回值是一个QuerySet(可以看成是列表套数据对象)
括号内不写查询条件,默认就是查询所有数据
括号内可以填写条件,并且可以填写多个,逗号隔开,默认是and关系
eg:
res = models.User.objects.filter()
print(res)  # <QuerySet [<User: User object (1)>, <User: User object (2)>]>

2.all()
查询所有数据,返回值是一个QuerySet(可以看成是列表套对象,与filter类似)
eg:
res = models.User.objects.all()  
print(res)  # <QuerySet [<User: 数据对象:jason>, <User: 数据对象:kevin>>

3.first()
查询QuerySet中的第一个数据对象,如果是QuerySet为空则返回None,常配合filter使用
eg:
res = models.User.objects.filter().first()
print(res)  # 数据对象:jason

4.last()
查询QuerySet中的最后一个数据对象,如果是QuerySet为空则返回None,与first使用方法一致
eg:
res = models.User.objects.filter().last()
print(res)  # 数据对象:kevin  

5.get()
根据条件直接查询具体的数据对象(不用再被QuerySet包起来),但是如果对象不存在会报错,不推荐使用
eg:
res = models.User.objects.get(name='jason')
# 相当于是 models.User.objects.filter(name='jason').first()
print(res)  # 数据对象:jason

6.values()
查询指定字段,结果是QuerySet(可以看成是列表套数据字段),里边可以跟多个条件,逗号隔开
eg:
res = models.User.objects.filter(name='jason').values('uid', 'join_time')
print(res)  # <QuerySet [{'uid': 1, 'join_time': datetime.date(2022, 9, 5)}]>

7.values_list()
查询指定字段,结果是QuerySet(可以看成是列表套元组数据)
eg:
res = models.User.objects.filter(pk=1).values_list('pk', 'name')
print(res)  # <QuerySet [(1, 'jason')]>

8.order_by()
指定字段排序,默认是升序,在字段前加负号是降序,并且支持多个字段排序
eg:
res = models.User.objects.order_by('pk')	# 升序
print(res)  # <QuerySet [<User: 数据对象:jason>, <User: 数据对象:kevin>]>
res = models.User.objects.order_by('-pk')	# 降序
print(res)  # <QuerySet [<User: 数据对象:kevin>, <User: 数据对象:jason>]>

9.count()
统计orm查询之后结果集数据格式的个数
eg:
res = models.User.objects.order_by('-pk')
print(res)  # <QuerySet [<User: 数据对象:jason>, <User: 数据对象:kevin>]>
print(res.count())	# 2

10.distinct()
针对重复的数据进行去重 要求数据必须完全一致,一定要注意数据对象中的主键

11.exclude()
针对括号内的条件进行反向数据查询,结果是QuerySet(filter的反向操作)
eg:
res = models.User.objects.exclude(name='jason')
print(res)  # <QuerySet [<User: 数据对象:kevin>]>

12.reverse()
针对已经排序的结果集颠倒顺序

13.exists()
判断查询结果集是否存在 返回布尔值 但是几乎不用因为所有数据自带布尔值

14.raw()
不想写orm时使用,支持写原汁原味的SQL
还可以借助于模块
    from django.db import connection  
    cursor = connection.cursor()  
    cursor.execute("insert into hello_author(name) VALUES ('郭敬明')") 
    cursor.execute("update hello_author set name='韩寒' WHERE name='郭敬明'")  
    cursor.execute("delete from hello_author where name='韩寒'")  
    cursor.execute("select * from hello_author")  
    cursor.fetchone()  
    cursor.fetchall()

神奇的双下划线查询

其实一点也不神奇,就是SQl里的一些运算符

1.比较运算符
	字段__gt			大于
	字段__lt			小于
	字段__gte			大于等于
	字段__lte			小于等于
eg:
# 查询年龄大于18岁的用户
res = models.User.objects.filter(age__gt=18)
print(res)  # <QuerySet [<User: 数据对象:kevin>]>

2.成员运算符
	字段__in
    
3.返回查询(数字)
	字段__range  # 左右都包含
eg:
# 查询年龄在18~20之间的用户
res = models.User.objects.filter(age__range=(18, 20))
print(res)  # <QuerySet [<User: 数据对象:jaon>, <User: 数据对象:kevin>]>

4.模糊查询
	字段__contains		不忽略大小写
	字段__icontains		忽略大小写
eg:
# 查询名字中含有 j 的用户
res = models.User.objects.filter(name__icontains='j')
print(res)  # <QuerySet [<User: 数据对象:jaon>]>
# 查询名字中含有 J 的用户
res = models.User.objects.filter(name__contains='J')
print(res)  # <QuerySet []>	

5.日期处理
    字段__year
    字段__onth
    字段__day
eg:
res = models.User.objects.filter(join_time__day=6)
print(res)  # <QuerySet [<User: 数据对象:kevin>]>    

查看ORM底层的SQL语句

方式1

如果是QuerySet对象,那么可以直接.query查看SQL语句
eg:
res = models.User.objects.filter(join_time__day=6).query
print(res)  # <QuerySet [<User: 数据对象:kevin>]>

# SELECT `app01_user`.`uid`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`join_time` FROM `app01_user` WHERE EXTRACT(DAY FROM `app01_user`.`join_time`) = 6

方式2

配置文件配置	打印所有的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外键创建

先创建四张关系表

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)  # 总共八位小数点后两位
    join_time = models.DateField(auto_now_add=True)
    # 书对应出版社 一对多关系
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    # 书对应作者 多对多关系
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    # 作者对应作者详情表 一对一关系
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)


class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    address = models.CharField(max_length=255)

一对多

外键字段创在多的一方 models.Foreignkey()

publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
# 不需要在外键字段后面手动添加_id后缀,django会自动添加_id后缀
# 一对多和一对一关系需要在后面加 on_delete=models.CASCADE

多对多

ORM中有三种创建多对多字段的方式 models.ManyToManyField()
    方式1:直接在查询频率较高的表中填写字段即可 自动创建第三张关系表
    方式2:自己创建第三张关系表
    方式3:自己创建第三张关系表 但是还要orm做多对多字段做关联
authors = models.ManyToManyField(to='Author')
# to='被关联表的名字'

一对一

ORM中创建在查询频率较高的表中 models.OneToOneField()

author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
# 不需要在外键字段后面手动添加_id后缀,django会自动添加_id后缀
# 一对多和一对一关系需要在后面加 on_delete=models.CASCADE
django1.x 针对 models.ForeignKey() 和 models.OneToOneField() 不需要添加on_delete
django2.x 3.x 则需要添加该参数

ORM外键数据增删改查

一对多:
# 方式1:直接给实际字段添加关联数据值  publish_id = 1
models.Book.objects.create(title='java高并发', price=1288.88, publish_id=1)
# 方式2:根据pk=2获取到出版社对象,间接使用外键虚拟字段添加数据对象  publish=publish_obj
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='爬虫入门到入狱', price=2222.22, publish=publish_obj)

一对一:
# 方式1:直接给实际字段添加关联数据值 author_detail_id=5
models.Author.objects.create(name='oscar', age=33, author_detail_id=5)
# 方式2:根据pk=6获取到作者详情对象,间接使用外键虚拟字段创建数据对象  author_detail=author_detail_obj
author_detail_obj = models.AuthorDetail.objects.filter(pk=6).first()
models.Author.objects.create(name='zhangsan', age=50, author_detail=author_detail_obj)

多对多:
# 方式1
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.add(3, 4)
# 方式2:
book_obj = models.Book.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj1, author_obj2)
# 操作类型
add()		添加数据	括号内既可以填写数字,也可以填写数据对象,支持多个
remove()	删除数据	括号内既可以填写数字,也可以填写数据对象,支持多个
    book_obj = models.Book.objects.filter(pk=2).first()
    book_obj.authors.remove(2, 3)
set()		修改数据	括号内必须是可迭代对象
    book_obj.authors.set([2, ])
clear()		清空指定数据	括号内不需要任何参数
    book_obj.authors.clear()

正反向概念

正反向的概念的核心就在于外键字段在谁手上

正向查询:通过书查出版社,外键字段在书籍表中 反向查询:通过出版社查书,外键字段不在出版社表中

ORM跨表查询口诀>>>:正向查询按外键字段,反向查询按表名小写

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

1.查询主键为1的书籍对应的出版社(书>>>出版社)

# 1.1 先拿到书籍对象
book_obj = models.Book.objects.filter(pk=1).filter().first()
# 1.2 以对象为基准 思考正反向(书查出版社,外键在书表中,正向查询)
print(book_obj.publish)  # 出版社对象:北方出版社

2.查询主键为2的书籍对应的作者(书>>>作者)

# 2.1 先拿到书籍对象
book_obj = models.Book.objects.filter(pk=2).first()
# 2.2 以对象为基准 判断正反向(书查作者 多对多 外键在书籍表中 正向)
print(book_obj.authors)  # app01.Author.None
print(book_obj.authors.all())  # <QuerySet [<Author: 作者对象:jason>, <Author: 作者对象:kevin>]>

因为查询结果对象有多个,所以book_obj.authors返回的是app01.Author.None。要想获取完整的作者列表,需要用 ll()方法: book_obj.authors.all()

3.查询jason的电话(作者详情)

author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.author_detail.phone)  # 110

4.查询南方出版社出版的书籍

# 4.1 先拿到出版社对象
publish_obj = models.Publish.objects.filter(name='南方出版社').first()
# 4.2 由出版社到书籍 判断正反向(一对多 外键不在出版社表中 反向)
# print(publish_obj.book)  # AttributeError: 'Publish' object has no attribute 'book'
print(publish_obj.book_set)  # app01.Book.None
print(publish_obj.book_set.all())  # <QuerySet [<Book: 书籍对象:C语言程序设计>]>

由于一个出版社对应多个书籍,所以用句点符点书籍时应该加上_set
由于查询结果有多条,所以book_set后面应该.all()

5.查询jason写过的书

author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set.all())  # <QuerySet [<Book: 书籍对象:java高并发>, <Book: 书籍对象:爬虫入门到入狱>]>

6.查询电话是110的作者

author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(author_detail_obj.author)  # 作者对象:jason

这里由于是一对一关系,一个作者对应一个作者详情,查询结果仅有一个,所以不用加_set.all()

基于双下划线的跨表查询(连表操作)

# 1.查询主键为1的书籍对应的出版社名称及书名

res = models.Book.objects.filter(pk=1).values('publish__name', 'title')
print(res)  # <QuerySet [{'publish__name': '北方出版社', 'title': 'java高并发'}]>
# 2.查询主键为3的书籍对应的作者姓名及书名

res = models.Book.objects.filter(pk=2).values('authors__name', 'title')
print(res)  # <QuerySet [{'authors__name': 'jason', 'title': '爬虫入门到入狱'}, {'authors__name': 'kevin', 'title': '爬虫入门到入狱'}]>
# 3.查询jason的作者的电话号码和地址

res = models.Author.objects.filter(name='jason').values('author_detail__phone', 'author_detail__address')
print(res)  # <QuerySet [{'author_detail__phone': 110, 'author_detail__address': '芜湖'}]>
# 4.查询南方出版社出版的书籍名称和价格

res = models.Publish.objects.filter(name='南方出版社').values('book__title', 'book__price')
print(res)  # <QuerySet [{'book__title': 'C语言程序设计', 'book__price': Decimal('222.22')}]>
# 5.查询jason写过的书的名称和日期

res = models.Author.objects.filter(name='jason').values('book__title', 'book__join_time')
print(res)
# 6.查询电话是110的作者姓名和年龄

res = models.AuthorDetail.objects.filter(phone=110).values('author__name', 'author__age')
print(res)  # <QuerySet [{'author__name': 'jason', 'author__age': 19}]>

作者:爱新觉罗吉星
来源:稀土掘金

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/27823.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定