Django测试环境搭建及ORM查询(创建外键|跨表查询|双下划线查询 )

news2024/11/25 13:52:22

文章目录

  • 一、表查询数据准备及测试环境搭建
    • 模型层前期准备
    • 测试环境搭建
    • 代码演示
  • 二、ORM操作相关方法
  • 三、ORM常见的查询关键字
  • 四、ORM底层SQL语句
  • 五、双下划线查询
    • 数据查询(双下划线)
    • 双下划线小训练
    • Django ORM __双下划线细解
  • 六、ORM外键字段创建
    • 基础表的准备
    • 模型表创建一对一、一对多和多对多的实例
    • 多对多三种创建方法的补充
  • 七、外键字段的相关操作
  • 八、正反向概念
  • 九、ORM跨表查询
    • 基于对象的跨表查询(子查询)
    • 基于双下划线的跨表查询(联表查询)
    • 聚合查询
    • 分组查询
    • F与Q查询

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

模型层前期准备

使用django ORM要注意

  1. django自带的sqlite3数据可对时间字段不敏感,有时候会展示错乱,所以我们习惯切换成常见的数据库比如MySQL。

  2. django ORM并不会自动帮我们创建库,所以需要提前准备好’‘数据库’’

  3. id字段是自动添加的,如果想自定义主键,只需要在其中一个字段指定primary_key = True,如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。

  4. Django支持MySQL5.5及更高版本。

测试环境搭建

我们需要新建一个Django项目,为了便于我们更加方便操作模型层,有两种方式可以直接调用到模型层。

方式一:在Django自带的测试环境
pycharm提供的python console(临时保存,不推荐使用)
在这里插入图片描述
方式二:在项目内的任意py文件内,推荐在应用下面的一个tests.py文件进行

	import os

	if __name__ == "__main__":
		# 注意:mysite.settings修改成自己的!!项目名.settings
	    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Ku.settings')
	    import django
	    django.setup() # 以独立的方式运行Django程序
	    
	    # 以下编辑我们需要的代码:

代码演示

测试test.py

	import os

	if __name__ == "__main__":
		# 注意:mysite.settings修改成自己的!!项目名.settings
	    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Ku.settings')
	    import django
	    django.setup() # 以独立的方式运行Django程序
	    
	    from app import models
	    models.UserInfo.objects.all()

models.py

	class UserInfo(models.Model):
	    uid = models.AutoField(primary_key=True)
	    name = models.CharField(max_length=32,verbose_name='用户名')
	    age = models.IntegerField(verbose_name='年龄')
	    register_time = models.DateTimeField(verbose_name='注册事件',auto_now_add=True)
	    
		'''
		 DateField       : 年月日
	    DateTimeField   : 年月日 时分秒
	    
	    两个重要参数
	    auto_now        : 每次操作数据的时候 该字段会自动将当前时间更新 
	    auto_now_add    : 在创建数据的时候会自动将当前创建时间记录下来 之后只要不人为的修改 那么就一直不变
		'''		

	    def __str__(self):
	        return self.name

在这里插入图片描述

切换MySQL数据库

	1.提前终端创建好库list_user
	2.将DATABASES的配置更改
		DATABASES = {
	    'default': {
	        'ENGINE': 'django.db.backends.mysql',
	        'NAME': 'list_user',
	        'USER':'root',
	        #'PASSWORD':'', 因为我的mysql用户没有设置密码所以这里就不需要写了
	        'HOST':'127.0.0.1',
	        'PORT':3306,
	        'CHARSET':'utf8'
	    }
	}
	3.连接MySQL库
	4.python38 manage.py makemigrations
	5.python38 manage.py migrate

如何查看django ORM 底层原理?

django ORM本质还是SQL语句。

1.如果有QuerySet对象,那么可以直接点query查看SQL语句

	res = models.UserInfo.objects.filter(name='jack')
    print(res)
    print(res.query)

    #SELECT `app_userinfo`.`uid`, `app_userinfo`.`name`, `app_userinfo`.`age`, `app_userinfo`.`register_time` FROM `app_userinfo` WHERE `app_userinfo`.`name` = jack

结论:有些不是QuerySet对象,就不能通过点query的形式点出来,就只能使用通过的方法

2.如果想查看所有ORM底层的SQL语句,也可以直接在配置文件添加日志记录

res1 = models.User.objects.create(name='jack',age=18)
print(res.query)  # 会报错

settings最后>>>拷贝代码放在settings

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常见关键字
基础的增删改查

方法返回值
create(字段名=数据)刚创建的数据记录对象
filter(筛选条件)QuerySet列表对象
filter().update(修改内容)受影响的行数
filter().delete()受影响的行数即各表受影响的行数

三、ORM常见的查询关键字

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

1.create 创建数据并直接获取当前创建的数据对象

	res = models.UserInfo.objects.create(name='jack',age=18)
    print(res)

    res1 = models.UserInfo.objects.create(name='tom',age=19)
    print(res1)

    res2 = models.UserInfo.objects.create(name='oscar',age=20)
    print(res2)

    res3 = models.UserInfo.objects.create(name='ankn',age=22)
    print(res3)

2.filter() 根据条件筛选数据 结果是QuerySet [数据对象1,数据对象2]

	res4 = models.UserInfo.objects.filter()  
    res5 = models.UserInfo.objects.filter(name='jack')  
    res6 = models.UserInfo.objects.filter(name='tom',age=19)  
    print(res4) #<QuerySet [<UserInfo: jack>, <UserInfo: tom>, <UserInfo: oscar>, <UserInfo: ankn>]>
    print(res5) #<QuerySet [<UserInfo: jack>]>
    print(res6) #<QuerySet [<UserInfo: tom>]>

3.first()/last() QuerySet支持索引取值但是只支持正数 并且orm不建议你使用索引

	res7 = models.UserInfo.objects.filter()[1] 
    res10 = models.UserInfo.objects.filter(uid=100)[0]  # 数据不存在索引取值会报错
    res8 = models.UserInfo.objects.filter(uid=100).filter() # 数据不存在不会报错而是返回None
    res9 = models.UserInfo.objects.filter(uid=99).last() # 数据不存在不会报错而是返回None

    print(res7)  # tom
    print(res8)  # None
    print(res9)  # None

4.update() 更新数据(批量更新)

	models.UserInfo.objects.filter().update()  # 批量更新
    models.UserInfo.objects.filter(uid=4).update(9) # 单个删除

5.delete() 删除数据(批量删除)

	models.UserInfo.objects.filter().delete() # 批量删除
    models.UserInfo.objects.filter(id=1).delete() # 单个删除

6.all() 查询所有数据 结果是QuerySet [数据对象1,数据对象2]

	res = models.UserInfo.objects.all()
    print(res)
    #<QuerySet [<UserInfo: jack>, <UserInfo: tom>, <UserInfo: oscar>, <UserInfo: ankn>]>

7.values() 根据指定字段获取数据 结果是QuerySet [{}},{},{},{}]

	res = models.UserInfo.objects.all().values('name')
    print(res)
    #<QuerySet [{'name': 'jack'}, {'name': 'tom'}, {'name': 'oscar'}, {'name': 'ankn'}]>
    res1 = models.UserInfo.objects.filter().values()
    print(res1)
    # <QuerySet [{'uid': 1, 'name': 'jack', 'age': 18, 'register_time': datetime.datetime(2023......
    res2 = models.UserInfo.objects.values()
    print(res2)
    # <QuerySet [{'uid': 1, 'name': 'jack', 'age': 18, 'register_time': datetime.datetime(2023......

8.values_list() 根据指定字段获取数据 结果是QuerySet [(),(),(),()]

	res = models.UserInfo.objects.all().values_list('name','age')
    print(res) #<QuerySet [('jack', 18), ('tom', 19), ('oscar', 20), ('ankn', 22)]>

9.distinct() 去重 数据一定要一模一样才可以 如果有主键肯定不行

	res = models.UserInfo.objects.values('name','age').distinct()
    print(res)

10.order_by() 根据指定条件排序 默认是升序 字段前面加负号就是降序

	res = models.UserInfo.objects.all().order_by('age')
    print(res)
    # <QuerySet [<UserInfo: jack>, <UserInfo: tom>, <UserInfo: oscar>, <UserInfo: ankn>]>

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

	res = models.UserInfo.objects.get(uid=1)
    print(res) # jack
    res = models.UserInfo.objects.get(uid=100)
    print(res) # 报错

12.exclude() 取反操作

	res = models.UserInfo.objects.exclude(uid=2)
    print(res)  # <QuerySet [<UserInfo: jack>, <UserInfo: oscar>, <UserInfo: ankn>]>

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

	res = models.UserInfo.objects.all().order_by('age') # 升序
    res1 = models.UserInfo.objects.all().order_by('age').reverse() # 返回升序之前
    print(res,res1)
    # <QuerySet [<UserInfo: jack>, <UserInfo: tom>, <UserInfo: oscar>, <UserInfo: ankn>]>
    # <QuerySet [<UserInfo: ankn>, <UserInfo: oscar>, <UserInfo: tom>, <UserInfo: jack>]>

14.count() 统计结果集中数据的个数

	res = models.UserInfo.objects.all().count()
    print(res)  # 4

15.exists() 判断结果集中是否含有数据 如果有则返回True 没有则返回False

	res = models.UserInfo.objects.all().exists() # 报错
    res1 = models.UserInfo.objects.filter(uid=100).exists()
    print(res1) # False

基础方法总结
1、返回QuerySet对象的方法有(大多通过模型类.objects.方法调用)
QuerySet对象形似存储了一个个记录对象的列表,但拥有一些特殊的属性,如query。

名称语法说明
filterres1 = models.User.objects.filter(name=‘Like’, age=20)筛选数据 返回值是一个QuerySet(可以看成是列表套数据对象)括号内不写查询条件 默认就是查询所有括号内可以填写条件 并且支持多个 逗号隔开 默认是and关系
allres2 = models.User.objects.all()查询所有数据 返回值是一个QuerySet(可以看成是列表套数据对象)
firstres3 = models.User.objects.first()获取Queryset中第一个数据对象 如果为空则返回None
lastres4 = models.User.objects.last()获取Queryset中最后一个数据对象 如果为空则返回None
getres5 = models.User.objects.get(pk=2)直接根据条件查询具体的数据对象 但是条件不存在直接报错 不推荐使用
valuesres6 = models.User.objects.values(‘name’, ‘age’)指定查询字段 结果是Queryset(可以看成是列表套字典数据)
values_listres7 = models.User.objects.values_list()指定全部字段 结果是Queryset(可以看成是列表套元组数据)
order_byres8 = models.User.objects.order_by(‘age’) 升序,res8_1 = models.User.objects.order_by(‘-age’, ‘name’)降序指定字段排序 默认是升序 在字段前加负号则为降序 并且支持多个字段排序
countres9 = models.User.objects.count()统计orm查询之后结果集中的数据格式
distinctres10 = models.User.objects.values(‘name’, ‘age’).distinct()针对重复的数据集进行去重 一定要注意数据对象中的主键
excluderes11 = models.User.objects.exclude()针对括号内的条件取反进行数据查询 QuerySet(可以看成是列表套数据对象)
reverseres12 = models.User.objects.all().order_by(‘age’).reverse()针对已经排了序的结果集做颠倒
existsres13 = models.User.objects.exists()判断查询结果集是否有数据 返回布尔值 但是几乎不用因为所有数据自带布尔值
rawres14 = models.User.objects.raw(‘select * from app01_user’)执行SQL语句

四、ORM底层SQL语句

	我们现在知道了怎么查询数据了但是它的底层语句逻辑是什么呢?
	
	方式1:
		如果是Queryset对象 那么可以直接点Ctrl+左键点击query查看SQL语句
		
	方式2:
		需要到配置文件Settings中找一个空白位置复制一下代码 主要作用打印所有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',
		                },
		            }
		        }

五、双下划线查询

结果对象还是query对象就可以无限制的点queryset对象的方法。

	queryset.filter().values().filter().values_list().filter()....

django中将字段后加上__条件的方式让关键字参数拥有除等号外的其他含义。

数据查询(双下划线)

__gt大于
__lt小于
__gte大于等于
__lte小于等于
__in类似于成员运算,在…里面
__range在什么范围之内
__contains是否含有,区分大小写 ,模糊查询
__icontains是否含有,不区分大小写 ,模糊查询
__year查询年份
__day查询日期天数
__second/minute查看秒/分

双下划线小训练

数据表提前准备好
在这里插入图片描述

1.查询年龄大于18的用户数据

	'''sql语句'''
    # select * form userinfo where age>18;
    
	res = models.UserInfo.objects.filter(age__gt=18)
    print(res)
    # <QuerySet [<UserInfo: tom1>, <UserInfo: oscar>, <UserInfo: ankn>, <UserInfo: jason>]>

2.查询年龄小于38的用户数据

	'''sql语句'''
    # select * form userinfo where age<38;
    
	res = models.UserInfo.objects.filter(age__lt=38)
    print(res)
    # <QuerySet [<UserInfo: jack>, <UserInfo: tom1>, <UserInfo: oscar>, <UserInfo: ankn>]>

3.查询年龄大于等于38的用户数据

	'''sql语句'''
    # select * form userinfo where age>=38;
    
	res = models.UserInfo.objects.filter(age__gte=38)
    print(res)
    # <QuerySet [<UserInfo: jason>]>

4.查询年龄小于等于38的用户数据

	'''sql语句'''
    # select * form userinfo where age<=38;
    
	res = models.UserInfo.objects.filter(age__lte=38)
    print(res)
    # <QuerySet [<UserInfo: jack>, <UserInfo: tom1>, <UserInfo: oscar>, <UserInfo: ankn>]>

5.查询年龄是18或者20或者38的数据

	'''sql语句'''
    # select * form userinfo where age=18 or age=20 or age=38;
    
	res = models.UserInfo.objects.filter(age__in=[18,20,38])
    print(res)
    # <QuerySet [<UserInfo: jack>, <UserInfo: oscar>]>

6.查询年龄在18到38范围之间的用户数据

	'''sql语句'''
    # select * form userinfo where age>=18 and age<=38;
    
	res = models.UserInfo.objects.filter(age__range=[18,38])
    print(res)
    # <QuerySet [<UserInfo: jack>, <UserInfo: tom1>, <UserInfo: oscar>, <UserInfo: ankn>]>

7.查询名字中含有字母j的用户数据

	'''sql语句'''
    # select * form userinfo where name like '%j%';
    
	1.区分大小写
	res = models.UserInfo.objects.filter(name__contains='j')
    print(res)
    # <QuerySet [<UserInfo: jack>, <UserInfo: jason>]>

	2.不区分大小写
	res = models.UserInfo.objects.filter(name__icontains='j')

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

	res = models.UserInfo.objects.filter(register_time__year='2022')
    print(res)
    # <QuerySet [<UserInfo: tom1>, <UserInfo: ankn>]>

Django ORM __双下划线细解

exact:
	精确匹配,例如 Book.objects.filter(title__exact='Django') 将返回所有标题为 'Django' 的书籍。
iexact:
	不区分大小写的精确匹配,例如 Book.objects.filter(title__iexact='django') 将返回所有标题为 'django''Django' 的书籍。
contains:
	包含匹配,例如 Book.objects.filter(title__contains='Django') 将返回所有标题中包含 'Django' 的书籍。
icontains:
	不区分大小写的包含匹配,例如 Book.objects.filter(title__icontains='django') 将返回所有标题中包含 'django''Django' 的书籍。
in:
	范围匹配,例如 Book.objects.filter(id__in=[1, 2, 3]) 将返回 ID 为 123 的书籍。
gt:
	大于匹配,例如 Book.objects.filter(price__gt=10) 将返回价格大于 10 的书籍。
lt:
	小于匹配,例如 Book.objects.filter(price__lt=10) 将返回价格小于 10 的书籍。
gte:
	大于等于匹配,例如 Book.objects.filter(price__gte=10) 将返回价格大于等于 10 的书籍。
lte:
	小于等于匹配,例如 Book.objects.filter(price__lte=10) 将返回价格小于等于 10 的书籍。
startswith:
	以指定字符串开头匹配,例如 Book.objects.filter(title__startswith='Django') 将返回标题以 'Django' 开头的书籍。
istartswith:
	不区分大小写的以指定字符串开头匹配,例如 Book.objects.filter(title__istartswith='django') 将返回标题以 'django''Django' 开头的书籍。
endswith:
	以指定字符串结尾匹配,例如 Book.objects.filter(title__endswith='Django') 将返回标题以 'Django' 结尾的书籍。
iendswith:
	不区分大小写的以指定字符串结尾匹配,例如 Book.objects.filter(title__iendswith='django') 将返回标题以 'django''Django' 结尾的书籍。
range:
	范围匹配,例如 Book.objects.filter(price__range=[10, 20]) 将返回价格在 1020 之间的书籍。
date:
	日期匹配,例如 Book.objects.filter(publish_date__date=date(2021, 8, 1)) 将返回发行日期为 202181 日的书籍。
year:
	年份匹配,例如 Book.objects.filter(publish_date__year=2021) 将返回发行日期为 2021 年的书籍。
month:
	月份匹配,例如 Book.objects.filter(publish_date__month=8) 将返回发行日期为 8 月的书籍。
day:
	日期匹配,例如 Book.objects.filter(publish_date__day=1) 将返回发行日期为 1 日的书籍。
hour:
	小时匹配,例如 Book.objects.filter(publish_time__hour=10) 将返回发布时间为上午 10 点的书籍。
minute:
	分钟匹配,例如 Book.objects.filter(publish_time__minute=30) 将返回发布时间为 30 分钟的书籍。
second:
	秒匹配,例如 Book.objects.filter(publish_time__second=0) 将返回发布时间为整点的书籍。
isnull:
	为空匹配,例如 Book.objects.filter(author__isnull=True) 将返回没有作者的书籍。

六、ORM外键字段创建

跟MySQL外键关系一样的判断规律

	1.一对多 外键字段建立在多的一方
	2.多对多 外键字段建立在第三张表中
	3.一对一 建立在任何一方都可以,但是建议建立在操作频率高的一张表中
	注意:目前关系的判断可以采用表与表之间换位思考原则

基础表的准备

  1. 创建基础表(书籍表、出版社表、作者表、作者详情表)
  2. 确定外键关系
	一对一 ORM与MySQL一致,外键字段建立在查询频率较高的一方
	一对多 ORM与MySQL一致,外键建立在多的一方
	多对多 ORM比MySQL有更多的变化

	1.外键字段可以之间建在某张表中(查询频率较高的)
		内部会自动帮你创建第三张关系表
		2.自己创建的三张关系表并创建外键字段
			后续讲解
  1. ORM创建

针对一对多和一对一同步到表中之后自动 _id的后缀,如book中建立的外键字段名publish,会自动变成publish_id

	1.一对多关系
	publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE) 
	在多的表中建立外键字段,会在表中产生一个实际的字段(自动加'_id后缀'2.一对一
	author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)
	在查询频率较高的表中建立外键字段,会在表中产生一个实际的字段(自动加'_id后缀')

	django1.x 针对外键的创建后的同步,是无需级联更新级联删除的,(on_delete = models.CASCADE)
	django2.x 3.x则需要添加on_delete参数

针对多对多,不会在表中有展示,而是自动创建第三张表

	1.多对多
	authors = models.ManyToManyField(to='Author')
	在查询频率较高的表中建立外键字段(ORM自动创建的,也可自己创建)
	不会在表中产生实际的字段,而是告诉ORM创建第三张关系表。

模型表创建一对一、一对多和多对多的实例

在这里插入图片描述
需要注意的事项:

1.创建一对多关系
和sql语句一样,外键建立到多的那张表上,不同的是,我们可以不讲究关联表和被关联表的建立顺序。字段类为ForeignKey

在django2.x版本以上,建立一对多关系时需要指定on_delete参数为CASCADE,不加会报错,不过也不一定就是CASCADE,可能为其他实参,这里不展开。

建立外键时,系统会自动加上_id后缀作为字段名。

2.创建多对多关系
sql中是将两张表建立好后,将外键字段创建在第三张表中,而django为我们省去了这一步骤,我们可以在多对多关系双方的一个模型表中直接建立一个虚拟外键,ManyToManyField

在底层,sql依旧创建了第三张表来存储两表的多对多关系,但是在orm操作中我们就可以将模型表中的外键当做实实在在的联系,因为在查询时,我们感受不到第三张的表的存在。

多对多关系的外键没有on_delete关键字参数。

3.创建一对多关系
一对一的字段类为OneToOneField,建议建立在查询频率高的一方。

建立一对一关系时需要指定on_delete参数,否则报错。

多对多三种创建方法的补充

注意:多对多关系这种虚拟外键才有add、set、clear、remove,一对一和一对多的表是无法使用这些方法

1.全自动创建

	class Book(models.Model):
	    title = models.CharField(max_length=32)
	    authors=models.ManyToManyField(to='Author')
	class Author(models.Model):
	    name = models.CharField(max_length=32)

优势:自动创建第三张表,并且提供了add、remove、set、clear四种操作

劣势:第三张表无法创建更多的字段,扩展性较差。如果我们有一些业务逻辑就是在关系表上,我们就无法通过第三张表完成了。


2.纯手动创建

	class Book(models.Model):
    	title = models.CharField(max_length=32)
	class Author(models.Model):
	    name = models.CharField(max_length=32)
	class Book2Author(models.Model):
	    book=models.ForeignKey(to='Book')
	    author= models.ForeigKey(to='Author')
	    others=models.CharField(max_length=32)
	    join_time = models.DataField(auto_now_add=True)

优势:第三张表完全由自己创建,扩展性强

劣势:编写繁琐,并不支持add、remove、set、clear以及正反向概念。


3.半自动创建

	class Book(models.Model):
		title = models.CharField(max_length=32)
	    authors = models.ManyToManyField(to='Author',
	                          through='Book2Author',
	                          through_fields=('book','author')# 外键在哪个表就把book表放前面
                                        )
	class Author(models.Model):
		name = models.CharField(max_length=32)
	class Book2Author(models.Model):
	    book = models.ForeignKey(to='Book', on_delete=models.CASCADE)
	    author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
	    others = models.CharField(max_length=32)
	    join_time = models.DateField(auto_now_add=True)

优势:第三张表完全由自己创建,扩展性强,正反向概念依然可以使用

劣势:编写繁琐,并不支持add、remove、set、clear。


七、外键字段的相关操作

数据的创建
基本数据:提前将Publish,author以及authordetail三个表的数据信息录入,Book以及关系的绑定在下面详细介绍

一对多和一对一实际外键字段的绑定
1.外键关联的实际字段

针对一对多,插入数据可以直接填写表中的实际字段

	'''先创建未存储外键字段的表数据'''
    # models.Publish.objects.create(name='星海出版社',address='澳门')
    # models.Publish.objects.create(name='上海出版社',address='上海')
    # models.Publish.objects.create(name='北京出版社',address='北京')

    models.Book.objects.create(title='Python从入门到放弃',price='233.1',publish_id=1)
    models.Book.objects.create(title='Python爬虫从入门到入狱',price='666.6',publish_id=1)
    models.Book.objects.create(title='MySQL从入门到删库跑路',price='555.5',publish_id=2)
    models.Book.objects.create(title='论如何开启重启人生',price='999.9',publish_id=3)

2.外键的关联对象

针对一对多,插入数据也可以填写表中的类中字段名

	publish_obj = models.Publish.objects.filter(pk=3).first()
    print(publish_obj)
    models.Book.objects.create(title='老人与海',price='111.1',publish_id=publish_obj.id)

3.一对一与一对多插入数据的方式是一致的


关于多对多关系外键字段的绑定

多对多外键属于实际不在模型表中的虚拟字段,多对多关系则需要django提供给我们的方法来实现增删改关系。拿到设立多对多外键的模型表的对象,用它点出外键属性,可以进行add、set、remove方法,这些方法都是这条记录对象的操作。

数据的增加add

	 语法:book_obj.authors.add()   
       # 对象.外键.add()
	  add可以通过关联的id或者关联的对象进行绑定关系
	
	    book_obj = models.Book.objects.filter(pk=3).first()
	    1.书与作者一对一绑定
	    book_obj.authors.add(1)  # 在第三张关系表中给当前书籍绑定作者
	    2.书与作者一对多绑定
	    book_obj.authors.add(2,3)
	    3.作者对象与书对象的绑定
	    book_obj = models.Book.objects.filter(pk=4).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)  # 可以添加一个作者对象
	     book_obj.authors.add(author_obj1,author_obj2)  # 也可同时添加两个作者对象
	
	    总结:add(1)  add(1,2)  add(obj1)  add(obj1,obj2)

数据的修改set

	  语法:book_obj.authors.set()  
       # 对象.外键.set()
	  set可以通过关联的id或者关联的对象进行修改绑定关系
	
	
	 4.绑定错误,如何修改使用set修改关系
	   	""" 通过id修改的"""
	    book_obj = models.Book.objects.filter(pk=4).first()
	    book_obj.authors.set((1,3))  # set括号里面只能填写一个可跌倒对象()/[]或者对象
	    """ 原本id=4的书籍绑定的是作者2和作者3,通过set修改数据信息后绑定的是作者1和作者3"""
	    
	    book_obj.authors.set([2,4])
	    """通过对象修改的"""
	    
	    book_obj = models.Book.objects.filter(pk=2).first()
	    book_obj.authors.add(1,2,4)
	    """ id=2的书绑定了作者1,作者2和作者4"""
	    
	    book_obj = models.Book.objects.filter(pk=2).first()
	    author_obj1=models.Author.objects.filter(pk=1).first()
	    author_obj2=models.Author.objects.filter(pk=2).first()
	    author_obj4=models.Author.objects.filter(pk=4).first()
	    book_obj.authors.set((author_obj1,author_obj2))
	    """ id=2的书由绑定的作者1,作者2和作者4修改为作者1和作者4"""
	    
	    book_obj.authors.set((author_obj1,author_obj2,author_obj4))
	    """ 通过修改,id=2的书还是绑定了作者1,作者2和作者4"""
		
		总结set((1,))  set((1,2))  set((obj1,))  set((obj1,obj2))

数据的删除remove

	  语法:book_obj.authors.remove()   
       # 对象.外键.remove()
	  remove可以通过关联的id或者关联的对象进行移除绑定关系
	  
	5.数据的删除
	    book_obj= models.Book.objects.filter(pk=1).first()
	    author_obj1= models.Author.objects.filter(pk=1).first()
	    author_obj2= models.Author.objects.filter(pk=2).first()
	    """ 通过id去删除"""
	    book_obj.authors.remove(2)  # 作者2
	    #book_obj.authors.remove(1,3)
	    """ 通过作者对象去删除"""
	    book_obj.authors.remove(author_obj1)  # 作者1
	    #book_obj.authors.remove(author_obj1,author_obj2)
	
		总结:remove(1)  remove(1,2)  remove(obj1)  remove(obj1,obj2)
	  
	  add()\remove()	多个位置参数(数字 对象)
	  set()			  可迭代对象(元组 列表) 数字 对象 
	  clear()	      情况当前数据对象的关系 ,不需要传参数

数据的清空 clear

		语法:book_obj.authors.clear()   
		# 对象.外键.clear()
		clear() 直接清空与book的id1关联的所有作者
		
		
		""" 清空主键为1的绑定关系"""
		book_obj = models.Book.objects.filter(pk=1).first()
		book_obj.authors.clear()	

八、正反向概念

		'正反向的概念核心就在于外键字段在谁手上'
		外键在自己手上则是正向查询
		外键在别人手上则是反向查询

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

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

九、ORM跨表查询

MySQL跨表查询的思路

	1.子查询
    	分步操作:将一条SQL语句用括号括起来当做另外一条SQL语句的条件
	2.连表操作
	    先整合多张表之后基于单表查询即可
	    	inner join	内连接
			left join	左连接
			right join	右连接

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

数据准备
在这里插入图片描述

步骤:

  1. 先根据条件获取数据对象
  2. 判断正反向查询(正向查询按外键,反向查询按表名小写)
	1.查询书籍主键为3的出版社
    # 书查询出版社表  正向  按照外键字段
    book_obj = models.Book.objects.filter(pk=3).first()
    print(book_obj.publish.name)
    print(book_obj.publish.address)

    2.查询书籍主键为2的作者
    # 书查询作者 正向 按照外键字段
    res = models.Book.objects.filter(pk=5).first()
    print(res.title)
    print(res.authors)
    print(res.authors.all())

    3.查询作者李四的电话号码
    # 作者查询作者详情 正向 按照外键字段
    res = models.Author.objects.filter(name='李四').first()
    print(res.author_detail.phone)

	"""
    在书写orm语句的时候跟写sql语句一样的
    不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点
    
    正向什么时候需要加.all()
        当你的结果可能有多个的时候就需要加.all()
        如果是一个则直接拿到数据对象
            book_obj.publish
            book_obj.authors.all()
            author_obj.author_detail
    """

    4.查询出版社是上海出版社出版的书
    # 出版社查询书  反向 表名小写
    res = models.Publish.objects.filter(name='上海出版社').values('book__title')
    print(res)

    5.查询作者是王五写过的书
    # 作者查询书 反向 按照表名小写
    res = models.Author.objects.filter(name='王五').values("book__title")
    print(res)

    6.查询手机号是110的作者姓名
    # 作者详情表查询作者  反向 按照表名小写
    res = models.AuthorDetail.objects.filter(phone=110).select_related("author")
    for i in res:
    	print(i.author.name)

	"""
    基于对象 
        反向查询的时候
            当你的查询结果可以有多个的时候 就必须加_set.all()
            当你的结果只有一个的时候 不需要加_set.all()
        自己总结出 自己方便记忆的即可 每个人都可以不一样
    """

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

	1.查询李四的手机号和作者姓名
    res = models.Author.objects.filter(name='李四').values('author_detail__phone','name')
    print(res)

    '''反向查询'''
    res = models.AuthorDetail.objects.filter(author__name='李四').values('phone','author__name')
    print(res)

    2.查询书籍主键为4的出版社名称和书的名称
    # 书查询出版社 正向 外键字段
    res = models.Book.objects.filter(pk=4).values('publish__name','title')
    print(res)

    '''反向查询'''
    res = models.Publish.objects.filter(book__pk=4).values('book__title','name')
    print(res)

    3.查询书籍主键为3的作者姓名
    # 书查作者 正向 外键字段
    res = models.Book.objects.filter(pk=3).values('authors__name')
    print(res)
	
	'''反向查询'''
    res = models.Author.objects.filter(book__id = 3).values('name')
    print(res)

    查询书籍主键是5的作者的手机号
    res = models.Book.objects.filter(pk=5).values('authors__author_detail__phone')
    print(res)
	
	'''反向查询'''
    res = models.Author.objects.filter(book__id=5).values('author_detail__phone')
    print(res)

	"""
    你只要掌握了正反向的概念
    以及双下划线
    那么你就可以无限制的跨表
    """

聚合查询

函数名描述
Max大于
Min小于
Sum求和
Count统计某个数据
Avg平均值
	 """
    聚合查询通常情况下都是配合分组一起使用的
    只要是跟数据库相关的模块 
        基本上都在django.db.models里面
        如果上述没有那么应该在django.db里面
    """
    如果我们在ORM中使用聚合函数,ORM支持单独使用聚合函数,步骤如下:
	from django.db.models import Max,Min,Sum,Count,Avg

	使用关键字aggregate
	
	1 所有书的平均价格
    res = models.Book.objects.aggregate(Avg('price'))
    print(res)

	2.上述方法一次性使用
    res = models.Book.objects.aggregate(Avg('price'),Sum('price'),Min('price'),Max('price'),Count('pk'))
    print(res)

分组查询

如果执行ORM分组查询报错,并且又关键sql_mode / strict mode ,那么就去移除sql_mode中的only_full_group_by

	
	1.统计每一本书的作者个数
    res = models.Book.objects.annotate(num_author=Count('authors')).values('title','num_author')
    '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(book_price=Sum('book__price')).values('name','book_price')
    print(res)

上述分组都是按照表来分组,我们也可以按照表中的字段名来分组

	1.按照表分组
	models.表名.objects.annotate()
	2.按照表中字段名来分组
		models.表名.objects.values('字段名').annotate()
	    eg:
	        res= models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id','count_pk')
	        print(res)
	        
	注意:values在annotate前就是按照values()括号里面字段名来分组;values()在annotate后就是按照前面的表名分组,values就是拿值的

F与Q查询

F查询

当查询条件不是很明确的,也需要从数据库中获取,就需要使用F查询。

简单理解:

  1. 两个字段进行比较的筛选条件(库存数大于卖出数),
  2. 在原来的数值字段增加数值(500),
  3. 在原来的字段名后面加字(+爆款)

在上述条件等,我们只借助ORM操作,是实现不了的,我们需要在ORM中就需要借助F方法。

	from django.db.models import F  导入模块
	1.查询卖出数大于库存数的书籍
    res = models.Book.objects.filter(maichu__gt=F('kucun')).values('title')
    print(res)

    2.将所有书籍的价格提升500块
    models.Book.objects.update(price=F('price')+500)
	
	'在操作字符类型的数据的时候 F不能够直接做到字符串的拼接'
    3.将所有书的名称后面加上爆款两个字
    from django.db.models.functions import Concat
    from django.db.models import Value
    # models.Book.objects.update(title=F('title')+'爆款')  # 使用F会让所有的名称变为空白
    models.Book.objects.update(title=Concat(F'title',Value('爆款')))

Q查询

在ORM操作中,筛选条件中存在或、非的关系需要借助Q方法来实现。

符号描述
,(逗号)and的关系
|or的关系
~not的关系
	from django.db.models import Q
	1.查询卖出数大于1000或者价格小于800的书籍
    res = models.Book.objects.filter(Q(maichu__gt=1000),Q(price__lt=800)).values('title')
    '''Q包裹逗号分割 还是and关系'''
    
   	res = models.Book.objects.filter(Q(maichu__gt=1000)|Q(price__lt=800)).values('title')
    '''| or关系'''
    
    res = models.Book.objects.filter(~(Q(maichu__gt=1000)|Q(price__lt=800))).values('title')
    '''~ not关系'''
    # print(res)

Q方法使用总结:

  • 两个条件是或关系Q(条件1) | Q(条件2)
  • 两个条件是非关系~Q(条件)

Q查询的进阶操作

	from django.db.models import Q
	'Q的高阶用法  能够将查询条件的左边也变成字符串的形式'
    q = Q()  '产生一个Q对象'
    q.connector='or'  '默认是多个条件的连接时and,修改成or'
    q.children.append(('maichu__gt',1000))  '添加查询条件'
    q.children.append('price__lt',800)   '支持添加多个'
    res = models.Book.objects.filter(q) '查询文件直接填写Q对象'
    print(res)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1217357.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS进阶——作用域、解构、箭头函数

1、作用域 作用域&#xff08;scope&#xff09;规定了变量能够被访问的“范围”&#xff0c;离开了这个“范围”变量便不能被访问。 1.1 局部作用域 局部作用域可分为函数作用域和块作用域。 1.1.1 函数作用域 在函数内部声明的变量只能在函数内部被访问&#xff0c;外部无…

把jar包打到本地仓库然后上传到私服

1.首先把需要打成maven的包放到本地 2.然后本地配置maven的环境变量 没有配置的话可以看看下面这个&#xff0c;教程很详细 Windows系统配置maven环境_windows配置maven环境变量-CSDN博客 3.WinR cmd 输入如下的指令&#xff1a; mvn install:install-file -Dfile.\device…

汇川伺服【选型目录】

sv680旗舰&#xff1a; 编码器位数&#xff1a;26bit 电机额定转速&#xff1a;3000r【3k】圈脉冲&#xff1a; sv670标准&#xff1a; 编码器位数&#xff1a;23bit【台达B3:23bit&#xff0c;台达A2&#xff1a;bit】 电机额定转速&#xff1a;3000r【3k】圈脉冲&#xff1…

python科研绘图:帕累托图(Pareto chart)

目录 帕累托图基本构成 绘制帕累托图的步骤 帕累托图&#xff08;Pareto chart&#xff09;是将出现的质量问题和质量改进项目按照重要程度依次排列而采用的一种图表。以意大利经济学家V.Pareto的名字而命名的。帕累托图又叫排列图、主次图&#xff0c;是按照发生频率大小顺序…

ncbi-datasets-cli-高效便捷下载NCBI数据

文章目录 简介安装datasets download下载基因组/基因序列按照GCA list文件编号下载下载大基因组genome完整参数gene参数 datasets summary下载元数据dataformat将json转换成表格格式通过json文件解析其他字段问题 简介 NCBI Datasets 可以轻松从 NCBI 数据库中收集数据。使用命…

FISCOBCOS入门(十)Truffle自定义测试helloworld

在windos终端内安装truffle npm install -g truffle truffle --version 出现上图情况也没问题 下面就可以进行我们的操作了 创建一个文件truffle 创建一个空工程 truffle init 在contracts内加入HelloWorld合约 // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; c…

瑞吉外卖Day05

1.新增套餐 service层 Service public class SetmealServiceImpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {Autowiredprivate SetmealDishService setmealDishService;/*** 新增套餐&#xff0c;同时保存套餐和菜品的关系** param set…

【你哥电力电子】从耦合电感到变压器

从耦合电感到变压器 2023年7月12日 dk 文章目录 从耦合电感到变压器1. 耦合电感1.1 一个等效1.2 自感、互感与漏感1.3 耦合系数2. 变压器3. 其他模型的推导方法3.1 T型等效电路3.2 其他等效电路4. 小结下链1. 耦合电感 1.1 一个等效 通电导线的周围会产生磁场,磁场可以通过…

IntelliJ IDE 插件开发 |(一)快速入门

前言 IntelliJ IDEA 作为 Java 开发的首选 IDE&#xff0c;其强大、方便之处不必多说。不过&#xff0c;由于个人或者团队的个性化需求&#xff0c;我们或多或少会想对其功能进行拓展&#xff0c;这时就需要开发插件&#xff08;在 IntelliJ 平台下的所有 IDE 均可运行&#x…

SpringBoot配置数据库密码加密的方法

由于系统安全的考虑,配置文件中不能出现明文密码的问题,本文就给大家详细介绍下springboot配置数据库密码加密的方法,下面话不多说了,来一起看看详细的介绍吧,需要的朋友可以参考下 1.导入依赖 <!--数据库密码加密--> <dependency><groupId>com.github.uli…

Greek Alphabet Letters Symbols

Upper CaseLower CaseGreek Letter NameEnglish EquivalentSoundΑαAlphaa ΒβBetab ΓγGammag ΔδDeltad ΕεEpsilone ΖζZetaz ΗηEtah ΘθThetath ΙιIotai ΚκKappak ΛλLambdal ΜμMum ΝνNun ΞξXix ΟοOmicrono ΠπPip ΡρRhor Σσ,…

Apache SCXML2 RCE漏洞

文章目录 前言源码利用上传恶意xml文件构造payload搭建Apache服务器 远程RCE 前言 在做 [HDCTF 2023]BabyJxVx 遇到的知识点&#xff0c;但是没公网的服务器只能作罢&#xff0c;写下这篇文章记录 源码利用 public String Flag(RequestParam(required true) String filenam…

Leetcode刷题详解——扫雷游戏

1. 题目链接&#xff1a;529. 扫雷游戏 2. 题目描述&#xff1a; 让我们一起来玩扫雷游戏&#xff01; 给你一个大小为 m x n 二维字符矩阵 board &#xff0c;表示扫雷游戏的盘面&#xff0c;其中&#xff1a; M 代表一个 未挖出的 地雷&#xff0c;E 代表一个 未挖出的 空方…

基于django电影推荐系统

基于django电影推荐系统 摘要 该Django电影推荐系统是一个简单而基础的框架&#xff0c;旨在展示系统的基本组件。系统包括两个主要模型&#xff0c;即Movie和Rating&#xff0c;用于存储电影信息和用户评分。视图层包括展示电影列表和电影详情的功能&#xff0c;使用模板进行页…

linux高级篇基础理论二(详细文档、LAMP、SHELL、sed正则表达式)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️不能因为人生的道路坎坷,就使自己的身躯变得弯曲;不能因为生活的历程漫长,就使求索的 脚步迟缓。 ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xff1a;云计算技…

大数据架构Lambda-架构师(六十九)

随着信息时代技术的发展&#xff0c;数据量的快速增加逐渐飙升到了惊人的数量级别。并且数据的采集与处理技术还在更新加快。大数据中&#xff0c;结构化占比百分之15左右&#xff0c;其余百分之85都是非结构化数据&#xff0c;他们大量存在于社交网络、互联网和电子商务等领域…

深入理解锁

目录 常用锁策略 1.乐观锁 VS 悲观锁 2.轻量级锁 VS 重量级锁 3.自旋锁 VS 挂起等待锁 4.互斥锁 VS 读写锁 5.公平锁 VS 非公平锁 6.可重入锁 VS 可重入锁 CAS ABA问题 Synchronized原理 1. 锁升级/锁膨胀 2.锁消除 3.锁粗化 常用锁策略 1.乐观锁 VS 悲观锁 站在…

【Java实现图书管理系统】

图书管理系统 1. 设计背景2. 设计思路3. 模块展示代码演示3.1 Book类3.2 BookList类&#xff08;书架类&#xff09;3.4 用户类 - User类3.5 子类管理员类 -- AdminUser类3.6 子类普通用户类 -- NormalUser类3.7 操作接口3.8 操作类3.8.1 查找操作 -- FindOperation类3.8.2 增加…

PostgreSQL基于Citus实现的分布式集群

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

LCD1602指定位置显示字符串-详细版

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…