第五十九天 Djando之模型层

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

1.django自带一个sqlite3小型数据库
	该数据库功能非常有限 并且针对日期类型的数据兼容性很差

2.django切换MySQL数据
django1.X
	import pymysql
	pymysql.install_as_MySQLdb()
django2.X 3.X 4.X
	pip install mysqlclient
"""在服务器上启动Django的时候可能需要修改DBG=True"""

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)
"""
日期字段重要参数
	auto_now:每次操作数据并保存都会自动更新当前时间
	auto_now_add:只在创建数据的那一刻自动获取当前时间 之后如果不人为更改则不变
verbose_name='编号' 的作用类似于注释
"""

4.执行数据库迁移命令(模型类>>>表)
	makemigrations
	migrate

5.模型层测试环境准备
# 原因:我们想要在别的文件中from app01 import models print(models.User.objects.filter())结果没法产出东西
方式1:在任意空的py文件中准备环境
先去manage.py拷贝前面四行有字的代码(不包含注释哦)
最后两行的启动脚本也是需要的
中间还得import django 以及django.setup()
import os
import sys
def main():
	os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject9.settings')
	import django
	django.setup()

if __name__ == '__main__':
    main()

下面是使用范例
from django.test import TestCase
import os
def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject9.settings')
    import django
    django.setup()
    from app01 import models
    print(models.User.objects.filter())
if __name__ == '__main__':
    main()
# 注意import django和from app01 import models得写进自定义函数main当中

方式2:pycharm提供测试环境
	python console命令行测试环境
# 也就是左下角的Python Console

二、ORM常见查询关键字

1.当需要查询数据主键字段值的时候 可以使用pk忽略掉数据字段真正的名字
2.在模型类中可以定义一个__str__方法 便于后续数据对象被打印展示的是查看方便
3.Queryset中如果是列表套对象那么直接for循环和索引取值但是索引不支持负数
4.虽然queryset支持索引但是当queryset没有数据的时候索引会报错 推荐使用first
5.下面的前几个是经常使用的

1.create()  update()  delete()
创建数据 返回值就是当前创建的数据对象,因为是对象所以可以查出对应的值
ps:还可以利用类实例化对象然后调用save方法创建
print(res.pk)可以直接获取主键的值!!!!!!!!!!!!!!!!!!!!
import os
import sys
def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject9.settings')
    import django
    django.setup()
    from app01 import models
    res = models.User.objects.create(name='jason', age=18)
    print(res.name)
    print(res.join_time)
    print(res.pk)
if __name__ == '__main__':
    main()
-------------------------------------------------------全部代码只写一遍下面的就是部分重点代码了
创建对象的方法之一
user_obj = models.User.create(name='oscar', age=66)
print(user_obj)  # create不仅创建了对象,也将数据对象返回
创建对象的方法之二
实例化产生对象的方法,总共有两步
user_obj = models.User(name='oscar', age=66)  # 实例化
user_obj.save()

models.User.objects.update(name='jojo')
models.User.objects.delete()
# 注意千万不能这么干,update和delete在前面没有限制条件的情况下可能把所有的东西都删除了!!!!!!!!!!!!!
models.User.objects.filter(pk=1).update(name='jojo')
models.User.objects.filter(pk=1).delete()
前面加上filter()的限制条件即可

2.filter()
筛选数据 返回值是一个QuerySet(可以看成是列表套数据对象)
	1.括号内不写查询条件 默认就是查询所有
	2.括号内可以填写条件 并且支持多个 逗号隔开 默认是and关系
# 使用单个条件进行查询
user_obj = models.User.objects.filter()
    print(user_obj)

# 在模型类中可以定义一个__str__方法 便于后续数据对象被打印展示的是查看方便
from django.db import models
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.DateTimeField(auto_now_add=True)
    def __str__(self):
        """对象被执行的时候打印(print,页面展示,数据查询)操作的时候自动触发"""
        return f'对象:{self.name}'
---------------------------------------------------------------------------
添加前在test中print(models.User.objects.filter())
<QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>, <User: User object (4)>, <User: User object (5)>, <User: User object (6)>, <User: User object (7)>]>
添加后对象会变成你编写的返回的东西
<QuerySet [<User: 对象:jason>, <User: 对象:tony>, <User: 对象:oscar>, <User: 对象:kevin>, <User: 对象:Jojo>, <User: 对象:dio>, <User: 对象:acdc>]>


# 使用多个条件进行查询
user_obj = models.User.objects.filter(name='oscar',age=66)
    print(user_obj)

3.all()
查询所有数据 返回值是一个QuerySet(可以看成是列表套数据对象,所以可以进行索引取值)
user_obj = models.User.objects.all()
    for i in user_obj:
        print(i)
这个列表套对象只能进行正数索引取值,负数不行
user_obj[0]
user_obj = models.User.objects.all().first()
user_obj = models.User.objects.all().last()

4.QuerySet对象取值
user_obj = models.User.objects.filter(name='oscar')取到的是一个QuerySet对象,无法直接取出里面的数据对象
user_obj[0] 可以取出数据对象,但是当user_obj因为上面名字填错了,值为空的时候会报错,就不推荐使用了
user_obj.first() 不仅可以取出数据对象,还可以在user_obj为空的时候输出None

5.first()
	获取Queryset中第一个数据对象 如果为空则返回None
6.last()
	获取Queryset中最后一个数据对象 如果为空则返回None

7.get()
直接根据条件查询具体的数据对象
# 他能够直接查出具体的数据对象,输出的结果和first()一样,但是条件不存在直接报错 不推荐使用!!!!!!!!!!!!!!!!!!!!
user_obj = models.User.objects.get(pk=100)
print(user_obj)

8.values()
指定查询字段 结果是Queryset(可以看成是列表套字典数据)
user_obj = models.User.objects.values('name', 'age')
输出结果
<QuerySet [{'name': 'jason', 'age': 18}, {'name': 'jason', 'age': 18}, {'name': 'oscar', 'age': 66}]>
里面的值可以使用first()或者使用for循环来进行取用
user_obj = models.User.objects.all().values('name', 'age')
这样写更好一点

9.values_list()
指定查询字段 结果是Queryset(可以看成是列表套元组数据)
user_obj = models.User.objects.all().values_list('name', 'join_time')
输出结果
<QuerySet [('jason', datetime.datetime(2023, 12, 15, 10, 54, 33, 920435, tzinfo=datetime.timezone.utc)), ('jason', datetime.datetime(2023, 12, 15, 10, 55, 32, 123103, tzinfo=datetime.timezone.utc))]

10.order_by()
指定字段排序 默认是升序 在字段前加负号则为降序 并且支持多个字段排序
user_obj = models.User.objects.order_by('age')
默认输出结果为升序
<QuerySet [<User: 对象:kevin>, <User: 对象:jason>, <User: 对象:tony>, <User: 对象:oscar>]>
可以按照多个字段进行排序
user_obj = models.User.objects.order_by('age','join_time')
也可以进行降序排序  # 字段名称前面加上负号即可!!!!!!!!!!!!!!!!!!!!!
user_obj = models.User.objects.order_by('-age')

11.count()
统计orm查询之后结果集中的数据格式
user_obj = models.User.objects.all().count()

12.distinct()
针对重复的数据集进行去重 一定要注意数据对象中的主键  # 去重的条件很苛刻
user_obj = models.User.objects.all().distinct()
# 这里你会发现就算是内容一样的数据也会因为主键字段的不一致而无法去重!!!!!!!!!
user_obj = models.User.objects.all().distinct('name', 'join_time')
依照上面的方法才有可能去重成功

13.exclude()
针对括号内的条件取反进行数据查询 QuerySet(可以看成是列表套数据对象)
user_obj = models.User.objects.exclude(pk=1)
查询主键不是1的数据

14.reverse()
# 针对已经排了序的结果集做颠倒!!!!!!!!!!!!!!!!!!!!!!
user_obj = models.User.objects.all().reverse()
你会发现无法进行颠倒
user_obj = models.User.objects.all().order_by('age').reverse()
先用order_by排序之后才行

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

16.raw()
执行SQL语句
res = models.User.objects.raw('select * from app01_user')
print(res)  # <RawQuerySet: select * from app01_user>
输出的是一个原生的query对象
print(list(res))  # [<User: 对象:jason>, <User: 对象:tony>, <User: 对象:oscar>, <User: 对象:kevin>]
用类似于for的方法就能进行取值

17.自定义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()

三、神奇的双下划线的查询

1.比较运算符
字段__gt					大于(greater than)
	res = models.User.objects.filter(age__gt=20)
字段__lt					小于(less than)
	res = models.User.objects.filter(age__lt=30)
字段__gte					大于等于(greater than and equal)
	res = models.User.objects.filter(age__gte=25)
字段__lte					小于等于(less than and equal)
	res = models.User.objects.filter(age__lte=25)

2.成员运算符
字段__in
	res = models.User.objects.filter(name__in=['dio', 'acdc'])
	# 找出存在列表中的元素的内容

3.范围查询(数字)
字段__range
res = models.User.objects.filter(age__range=(18, 30))
# 这里好像是前后都会被取到

4.模糊查询
字段__contains			不忽略大小写
	res = models.User.objects.filter(name__contains='j')
字段__icontains			忽略大小写(ignore)
	res = models.User.objects.filter(name__icontains='j')

5.日期处理
字段__year
res = models.User.objects.filter(join_time__year=2019)
字段__month
res = models.User.objects.filter(join_time__month=12)
字段__day
res = models.User.objects.filter(join_time__month=16)
# 这里加不加引号好像并不影响识别2019  ‘2019’

四、查看ORM底层SQL语句

1.方式1
如果是Queryset对象 那么可以直接点query查看SQL语句
上面一节的方法大多都可以使用query
res = models.User.objects.filter(join_time__year='2019')
print(res.query)

2.方式2
如果是Queryset对象 那么可以直接点query查看SQL语句
user_obj = models.User.objects.get(pk=1)的输出结果就不是Queryset
# 将下面的内容复制到settings.py文件夹即可,后序在print()的时候能够自动输出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外键字段创建

一对多
ORM中外键字段建在多的一方 models.ForeignKey()
	会自动添加_id后缀
多对多
ORM中有三种创建多对多字段的方式 models.ManyToManyField()
	方式1:直接在查询频率较高的表中填写字段即可 自动创建第三张关系表
	方式2:自己创建第三张关系表
	方式3:自己创建第三张关系表 但是还是要orm多对多字段做关联
一对一
ORM中外键字段建在查询频率较高的表中 models.OneToOneField()
	会自动添加_id后缀
"""
1.建表先把表的框架搭建出来,把主要的字段先写出来,编辑字段类型
2.之后再建立外键字段
"""
class Book
	title
	price
	publish_time

class Publish
	name
	email

class Author(models.Model):
	name
	age
	author_detail

class AuthorDetail(models.Model):
	phone
	addr

书多出版社一
书多作者多
作者一细节一

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_time = models.DateTimeField(auto_now=True)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')
    def __str__(self):
        """对象被执行的时候打印(print)操作的时候自动触发"""
        return f'对象:{self.title}'


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.IntegerField()
    addr = models.CharField(max_length=32)

六、外键字段数据操作

1.ForeignKey
models.ForeignKey(to='Publish', on_delete=models.CASCADE)
方式1直接给实际字段添加关联数据值	publish_id = 1
	models.Book.objects.create(title='python从入门到入土', price=99.88, publish_id=1)
	# 注意 publish_id=1必须在对应的Publish表中有东西才行不然会报错的!!!!!!!!!!!!!!!
方式2间接使用外键虚拟字段添加数据对象 publish=publish_obj
	publish_obj = models.Publish.objects.filter(pk=1).first()
	models.Book.objects.create(title='python放弃', price=100.00, publish=publish_obj)
	# 注意models.Publish.objects.filter(pk=1).first()中得使用.first()去取值
	# 注意这里的 publish=publish_obj 不同于 publish_id=1 意思是拿到出版社对象的主键值,去出版社对象里面找到外键字段实例化!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


2.ManyToMany
models.ManyToManyField(to='Author')
add()
添加数据  括号内即可以填写数字值也可以填写数据对象 支持多个
"""
下面的解释很重要,不理解可能没法添加数据!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
"""

2.1.首先我们使用
class Book(models.Model):
    author = models.ManyToManyField(to='Author')
# 这里本质上是创建了第三张表,用来记录多对多的关系!!!!!!!!!!!!!!!!!

2.2上面对于数据的操作本质上是基于model中的对象,但是这张表是系统帮着创建的,所以我们没法直接像上面一样调用
book_obj = models.Book.objects.filter(pk=1).first()  # 这里先取出书籍对象
book_obj.author  # 这里对应的就是app01_book_author,可以进入第三张表中了
book_obj.author.add(1)  # 将id为1的书和1号作家绑定
book_obj.author.add(1,2)  # 因为是多对多所以,一本书可以绑定多个作者

2.3这里还可以使用作者对象进行添加(下面的两种方法原理同上)
book_obj = models.Book.objects.filter(pk=3).first()
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj.author.add(author_obj1)
book_obj.author.add(author_obj1,author_obj2)

2.4进行第三张表的关系删除
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.remove(1)
# 这里移除了book(pk=1)与作者1的关系
book_obj.author.remove(2,3)  # 可以同时移除多个的
book_obj.author.remove(author_obj1, author_obj2)  # 添加对象的功能也同上所述

2.5进行关系的修改
# 当发现关系绑定错误的时候可以进行修改,注意后面的括号里不能直接放数据,得是个迭代器对象!!!!!!!!!!
book_obj.author.set([2,])
book_obj.author.set([2,3])
book_obj.author.set([author_obj1, author_obj2])

2.6进行关系的删除
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.author.clear()
# 删除了所有与(pk=3)的书的关系


3.models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
方式1直接给实际字段添加关联数据值	author_detail_id = 1
方式2间接使用外键虚拟字段添加数据对象	author_detail=authorDetail_obj
# 使用方法和上面的ForeignKey一样

七、正反向概念

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

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

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

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

# 对model进行修改以后能够更加清楚的看清print对象以后得内容
from django.db import models
from django.db import models
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_time = models.DateTimeField(auto_now=True)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')
    def __str__(self):
        return '书籍对象:%s' % self.title
class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()
    def __str__(self):
        return '出版社对象:%s' % self.name
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
    def __str__(self):
        return '作者对象:%s' % self.name
class AuthorDetail(models.Model):
    phone = models.IntegerField()
    addr = models.CharField(max_length=32)
    def __str__(self):
        return '作者详情对象:%s' % str(self.phone)
首先我们先提出需求
1.查询主键为1的书籍对应的出版社(书>>>出版社)
2.查询主键为3的书籍对应的作者(书>>>作者)
3.查询jason的作者详情
4.查询南方出版社出版的书籍
5.查询jason写过的书
6.查询电话是110的作者

# 无论如何第一步都是先确认要找的对象,注意后面加.first()才能取出来对象

解
1.1.先根据条件查询数据对象(先查书籍对象)
book_obj = models.Book.objects.filter(pk=1).first()
1.2.以对象为基准 思考正反向概念(书查出版社 外键字段在书表中 所以是正向查询)
直接利用对象去点出方法,书中的外键字段就是出版社
print(book_obj.publish)  # 这里就拿到了出版社对象
print(book_obj.publish.name)  # 对象的名字一类的东西可以直接用.号来进行获取
# 这上面的内容本质就是一个子查询


2.1.先根据条件查询数据对象(先查书籍对象)
book_obj = models.Book.objects.filter(pk=3).first()
2.2.以对象为基准 思考正反向概念(书查作者 外键字段在书表中 所以是正向查询)
print(book_obj.authors)  # app01.Author.None
print(book_obj.authors.all())  # 因为一本书可以对应多个作者对象,所以得用all()来进行取值
"""一般来说当发现是None的时候都可以试试all()"""

3.1.先根据条件查询数据对象
author_obj = models.Author.objects.filter(name='jason').first()
3.2.以对象为基准 思考正反向概念
print(author_obj.author_detail)

'''基于对象的反向跨表查询'''
4.1.先拿出版社对象
publish_obj = models.Publish.objects.filter(name='南方出版社').first()
4.2.思考正反向(外键字段在书籍中,所以是反向的)
print(publish_obj.book)  # 因为是逆向查询且结果为多个所以要在后面加上_set
print(publish_obj.book_set)  # app01.Book.None
print(publish_obj.book_set.all())  # 因为一个出版社会有多本书,所以得加all()

5.1.先拿作者对象
author_obj = models.Author.objects.filter(name='jason').first()
5.2.思考正反向
print(author_obj.book)
print(author_obj.book_set)  # app01.Book.None
print(author_obj.book_set.all())

6.1.先拿作者详情对象
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
6.2.思考正反向
print(author_detail_obj.author)
"""
1.注意后面不加.first()是取不出来值的!!!!!!!!!!!!!
2.注意出来的值只有一个的情况下就算是逆向查询也不用加_set,_set.all()在逆向查询出多个结果的时候同时出现!!!!!!!!!!!!!!
"""

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

'''基于对象的正向跨表查询'''
1.查询主键为1的书籍对应的出版社名称及书名
2.查询主键为3的书籍对应的作者姓名及书名
3.查询jason的作者的电话号码和地址
4.查询南方出版社出版的书籍名称和价格
5.查询jason写过的书的名称和日期
6.查询电话是110的作者姓名和年龄
7.查询主键为1的书籍对应的作者电话号码  #涉及到了三张表的使用

1.查询主键为1的书籍对应的出版社名称及书名
res = models.Book.objects.filter(pk=1).values('publish__name','title')
# values('publish__name')实现了跨表取值的操作,而查询书名不需要跨表,所以能够直接用values('title')实现

2.查询主键为3的书籍对应的作者姓名及书名
res = models.Book.objects.filter(pk=3).values('author__name', 'title')

3.查询jason的作者的电话号码和地址
res = models.Author.objects.filter(name='jason').values('author_detail__addr', 'author_detail__phone')


'''基于双下划线的反向跨表查询 好像和上面没啥区别'''
4.查询南方出版社出版的书籍名称和价格
res = models.Publish.objects.filter(name='南方出版社').values('book__title', 'book__price')

5.查询jason写过的书的名称和日期
res = models.Author.objects.filter(name='jason').values('book__title','book__publish_time')

6.查询电话是110的作者姓名和年龄
res = models.AuthorDetail.objects.filter(phone=110).values('author__name','author__age')

7.查询主键为1的书籍对应的作者电话号码  #涉及到了三张表的使用
res = models.Book.objects.filter(pk=1).values('author__author_detail__phone')
"""
这里说明了跨表查询的本质
1.先跨表到author表中,之后author表中刚好有author_detail的外键字段利用__author_detail进行跨表,最后使用__phone进行跨表查询
2.注意这里的author_detail,因为是正向查询得使用外键字段author_detail,而他的表名为authordetail
"""
posted @ 2023-12-15 15:40  暧昧的花蝴蝶  阅读(23)  评论(0)    收藏  举报