文章目录
一、多对一
正向操作
如果一个模型有外键字段,通过这个模型对外键的操作,叫做正向
1、改
给学生对象,赋值channel字段
方法一
**
s.channel_id=关联模型对象.id
**
In [29]: s=Student.objects.get(id=3)
In [32]: ch1=Channel.objects.get(id=4)
In [33]: s.channel_id=ch1.id
In [34]: s.save()
方法二
**
s.channel=关联模型对象
**
In [35]: s=Student.objects.get(id=4)
In [36]: ch1=Channel.objects.get(id=5)
In [37]: s.channel=ch1
In [38]: s.save()
2、删
把一个学生对象的channel字段删除,如果想要删除一个外键,定义模型类时定义外键字段需要有null=True的设置
s.channel=None 直接赋值外键为None
s.save()
In [40]: s=Student.objects.get(id=4)
In [41]: s.channel=None
In [42]: s.save()
或者
s.channel_id=None 直接赋值外键为None
s.save()
In [43]: s.channel_id=None
In [44]: s.save()
查看sql
In [45]:print(connection.queries[-1]){'sql': "UPDATE `t_student` SET `name` ='james', `age` =18, `phone` ='13888888888', `address` = NULL, `create_tim
e` ='2023-01-06 07:07:17.669711', `channel_id` = NULL WHERE `t_student`.`id` =4",'time':'0.000'}
3、查
涉及跨表查询
反向操作
一个模型如果被另外一个模型的外键关联,那么通过这个模型对关联它的模型进行的操作叫反向
在这个模型的对象上,有一个反向字段,它的名字默认以关联模型名的小写加上_set构成,所以在Channel模型的对象上,有一个反向字段student_set,这个字段是一个关系型管理器,可以操作Student模型
In [48]: ch1.student_set
Out[48]:<django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager at
0x1950e19ad90>
案例1:查询百度渠道下的所有学生信息
**
ch1.student_set.all():是查询集
**
In [46]: ch1=Channel.objects.get(id=5)
In [51]: ch1.student_set.all()
Out[51]:<QuerySet [<Student: james>,<Student: harden>]>
In [52]:print(connection.queries[-1]){'sql': 'SELECT `t_student`.`id`, `t_student`.`name`, `t_student`.`age`, `t_student`.`phone`, `t_student`.`address`,
`t_student`.`create_time`, `t_student`.`channel_id` FROM `t_student` WHERE `t_student`.`channel_id` =5 ORDER BY `t
_student`.`id` ASC LIMIT 21', 'time': '0.015'}
案例2:新增一个百度渠道下的学生
**
ch1.student_set.create(name='happy')
**
In [53]: ch1.student_set.create(name='happy')
Out[53]:<Student: happy>
In [54]:print(connection.queries[-1]){'sql': "INSERT INTO `t_student` (`name`, `age`, `phone`, `address`, `create_time`, `channel_id`) VALUES ('happy', N
ULL, NULL, NULL,'2023-01-07 08:19:56.927533',5) RETURNING `t_student`.`id`",'time':'0.015'}
1、增
直接创建Student对象
直接通过关系型管理器创建学生对象
channel1=Channel.objects.get(id=2)
channel1.student_set.create(name=‘happy’)
2、改
方法一:add()
把多个学生对象添加进渠道的关联对象集中
案例1:将s1,s2,s3添加到百度渠道中
**
channel1.student_set.add(模型对象1,模型对象2,......)
**
In [55]: s1,s2,s3=Student.objects.all()[:3]
In [57]: channel1=Channel.objects.get(id=5)
In [58]: channel1.student_set.add(s1,s2,s3)
In [59]:print(connection.queries[-1]){'sql':'UPDATE `t_student` SET `channel_id` = 5 WHERE `t_student`.`id` IN (2, 3, 4)','time':'0.000'}
方法二:替换对象集
**
channel1.student_set.set([模型对象1,模型对象2,......])
这个set方法会先清空clear,后添加**
案例2:将channel1模型对象下设置为学生s1和s2,之前不管channel1下面有多少学生都置为None
In [60]: channel1.student_set.set([s1,s2])
3、删
这里的删,是删关系,从channel对象的关联对象集中删除
a、从相关对象中清空指定的模型对象
案例1:清除某个渠道中的某些学生
**
ch1.student_set.remove(模型对象1,模型对象2,......)
**
In [64]: s1,s2,s3=Student.objects.all()[:3]
In [65]: channel1.student_set.remove(s1)
In [66]:print(connection.queries[-1]){'sql': 'UPDATE `t_student` SET `channel_id` = NULL WHERE (`t_student`.`channel_id` =5 AND `t_student`.`id` IN (2))', 'time': '0.000'}
b、从相关对象中清空所有的模型对象
案例2:清除某个渠道中的所有的学生
**
channel1.student_set.clear()
**
In [67]: channel1.student_set.clear()
In [68]:print(connection.queries[-1]){'sql':'UPDATE `t_student` SET `channel_id` = NULL WHERE `t_student`.`channel_id` = 5','time':'0.000'}
4、查
关联管理器的查询和普通管理器没有区别
a、查所有
ch1.student_set.all():这里的查所有,有个前提条件,就是ch1的关联学生集合
b、查询来自抖音渠道的学生名叫kobe的学生
方法一
**
ch1.student_set.filter(name='kobe')
**
方法二
**
Student.objects.filter(channel__name='抖音',name='kobe')
**
In [75]: q1=Student.objects.filter(channel__name='抖音',name='kobe')
In [76]: q2=ch1.student_set.filter(name='kobe')
In [77]:print(q1.query)
SELECT `t_student`.`id`, `t_student`.`name`, `t_student`.`age`, `t_student`.`phone`, `t_student`.`address`, `t_stude
nt`.`create_time`, `t_student`.`channel_id` FROM `t_student` INNER JOIN `t_channel` ON (`t_student`.`channel_id` = `
t_channel`.`id`) WHERE (`t_channel`.`name` = 抖音 AND `t_student`.`name` = kobe) ORDER BY `t_student`.`id` ASC
In [78]:print(q2.query)
SELECT `t_student`.`id`, `t_student`.`name`, `t_student`.`age`, `t_student`.`phone`, `t_student`.`address`, `t_stude
nt`.`create_time`, `t_student`.`channel_id` FROM `t_student` WHERE (`t_student`.`channel_id` =5 AND `t_student`.`na
me` = kobe) ORDER BY `t_student`.`id` ASC
c、自定义关联关系字段
在外键字段中使用related_name,可以自定义反向字段
channel = models.ForeignKey('Channel',
on_delete=models.RESTRICT,
null=True,
help_text='外键字段'),# 不允许删除,
related_name='students'
那么通过students进行操作
**
ch1.students.filter(name='kobe')
**
二、多对多
多对多两端都可以获得另一端的关系管理器,类似于多对一的反向关系
1、增
增一方面指的是创建关系对象
案例1:通过学生创建课程
**
s1.course_set.create(name='java')
**
course_set字段和多对一的反向字段一样
案例2:通过课程创建学生
**
c1.students.create(name='hao')
**
在当前业务场景下,这个业务不对
另一方面这里的改是建立关联关系,也就是在第三张表中插入数据
案例3:有学生s1,s2,s3,课程c1,c2,c3,学生s1报名了c1,c2
**
s1.course_set.add(c1,c2)
**
In [13]: c1=Course.objects.all()[0]
In [14]: c2=Course.objects.all()[1]
In [15]: c3=Course.objects.all()[2]
In [16]: s1,s2,s3=Student.objects.all()[:3]
In [17]: s1.student_set.add(c1,c2)
In [22]:print(connection.queries[-1]){'sql':'INSERT IGNORE INTO `t_course_students` (`course_id`, `student_id`) VALUES (1, 2), (2, 2)','time':'0.015'}
案例4:学生s2,s3报名了c2
**
c2.students.add(s2,s3)
**
In [23]: c2.students.add(s2,s3)
In [24]:print(connection.queries[-1]){'sql':'INSERT IGNORE INTO `t_course_students` (`course_id`, `student_id`) VALUES (1, 3), (1, 4)','time':'0.015'}
2、删
同多对一,也有remove和clear
案例5:学生s1,去掉课程c1的报名
**
s1.course_set.remove(c1)
**
In [25]: s1.course_set.remove(c1)
In [26]:print(connection.queries[-1]){'sql': 'DELETE FROM `t_course_students` WHERE (`t_course_students`.`student_id` =2 AND `t_course_students`.`course_id` I
N (2))', 'time': '0.000'}
案例6:课程c2,去掉学生s2的报名
In [27]: c2.students.remove(s2)
In [28]:print(connection.queries[-1]){'sql': 'DELETE FROM `t_course_students` WHERE (`t_course_students`.`course_id` =1 AND `t_course_students`.`student_id` I
N (3))', 'time': '0.000'}
案例7:清空clear
**
s.course_set.clear()
**
**
c.students.clear()
**
3、改
这里的改是建立关联关系
案例8:修改学生s1的报名为c1和c2,如果学生s2还有其他的报名,会被删掉
**
s1.course_set.set([c1,c2])
**
In [29]: s1.course_set.set([c1,c2])
In [30]:print(connection.queries[-1]){'sql':'INSERT IGNORE INTO `t_course_students` (`course_id`, `student_id`) VALUES (2, 2)','time':'0.000'}
案例9:修改课程c1的报名为s1和s2,如果还有其他的学生报名, 也会被删掉
**
c1.students.set([s1,s2])
**
4、查
一般多对多,主要用来查。
案例10:查报名了某个课程的学生
**
c1.students.all()
**
案例11:查某个学生报名的课程
**
s1.course_set.all()
**
多对多字段都是关系管理器,查询方法和默认管理器一致
可定义的反向字段名
同多对一
students = models.ManyToManyField('Student',
help_text='报名学生',
verbose_name='报名学生'),
related_name='courses'
**
s1.courses.all()
**
三、一对一
一对一和多对一很像
一对一字段,指向的是关系模型的一个对象
在被关联的模型对象上,默认有一个字段,以关联模型名的小写作为名称。
所以student对象上,有一个字段studentdetail。这个字段指向StudentDetail的对象。
如果student对象还没有关联,调用这个字段会报错
在StudentDetail对象上有一个student字段,它指向Student对象
1、增
一般一对一字段的模型,在创建的时候,必须传递一对一关系的对象
案例1:给某个学生添加学生详情
**
StudentDetail.objects.create(student=s1,city='北京',salary=20000)
**
In [36]: StudentDetail.objects.create(student=s1,city='北京',salary=20000)
Out[36]:<StudentDetail: kobe>
In [37]:print(connection.queries[-1]){'sql': "INSERT INTO `t_student_detail` (`student_id`, `city`, `company`, `station`, `salary`) VALUES (2,'北京', NULL, '
测试开发', '20000') RETURNING `t_student_detail`.`id`", 'time': '0.000'}
2、删
案例2:删除某个学生的学生详情
**
s1.studentdetail.delete()
**
3、改
案例3:修改某个学生的学生详情
**
s1.studentdetail.city='北京'
s1.save()
**
In [47]: StudentDetail.objects.create(student=s3,city='北京',salary=20000)
Out[47]:<StudentDetail: james>
In [48]: s3.studentdetail.city='长沙'
In [49]: s3.studentdetail.save()
案例4:修改某个学生详情对应学生的信息
**
sd.student.name='zs' sd.student.save()
**
4、查
不存在查
四、跨表查询
案例1:例如查询年龄大于18岁的学生都报名了那些课程?
**
Course.objects.filter(students__age__18)
**
In [51]: Course.objects.filter(students__age__gt=18)
Out[51]:<QuerySet [<Course: python开发>,<Course: 测试开发>]>
In [52]:print(connection.queries[-1]){'sql': 'SELECT `t_course`.`id`, `t_course`.`name` FROM `t_course` INNER JOIN `t_course_students` ON (`t_course`.`id` = `t
_course_students`.`course_id`) INNER JOIN `t_student` ON (`t_course_students`.`student_id` = `t_student`.`id`) WHERE `t_st
udent`.`age` >18 LIMIT 21', 'time': '0.000'}
**
Course.objects.filter(students__age__gt=18).distinct():过滤
**
In [54]: Course.objects.filter(students__age__gt=18).distinct()
Out[54]:<QuerySet [<Course: python开发>,<Course: 测试开发>]>
In [55]:print(connection.queries[-1]){'sql': 'SELECT DISTINCT `t_course`.`id`, `t_course`.`name` FROM `t_course` INNER JOIN `t_course_students` ON (`t_course`.
`id` = `t_course_students`.`course_id`) INNER JOIN `t_student` ON (`t_course_students`.`student_id` = `t_student`.`id`) WH
ERE `t_student`.`age` >18 LIMIT 21', 'time': '0.000'}
案例2:查询报名了python课程的学生?
**
Student.objects.filter(course__name__contains='python开发')
**
In [57]: Student.objects.filter(course__name__contains='python开发')
Out[57]:<QuerySet [<Student: kobe>]>
In [58]:print(connection.queries[-1]){'sql': "SELECT `t_student`.`id`, `t_student`.`name`, `t_student`.`age`, `t_student`.`phone`, `t_student`.`address`, `t_st
udent`.`create_time`, `t_student`.`channel_id` FROM `t_student` INNER JOIN `t_course_students` ON (`t_student`.`id` = `t_c
ourse_students`.`student_id`) INNER JOIN `t_course` ON (`t_course_students`.`course_id` = `t_course`.`id`) WHERE `t_course
`.`name` LIKE BINARY '%python开发%' ORDER BY `t_student`.`id` ASC LIMIT 21",'time':'0.000'}
案例3:查询百度渠道的学生,报名了那些课程
**
Course.objects.filter(students__channel__name='百度')
**
In [3]: Course.objects.filter(students__channel__name='抖音')
Out[3]:<QuerySet [<Course: python开发>,<Course: 测试开发>,<Course: 测试开发>]>
通过关系字段加双下划线进行跨表查询。
五、执行原生SQL
1、raw()方法
返回查询集,通过在管理器上调用raw方法来实现
**
q=Student.objects.raw('select id,name' from t_student where id=2)
**
In [6]: q=Student.objects.raw('select id,name from t_student where id=2')
In [7]: q
Out[7]:<RawQuerySet: select id,name from t_student where id=2>
打印sql
In [8]:print(q.query)
select id,name from t_student where id=2
取值
In [9]: q[0]
Out[9]:<Student: kobe>
In [10]: q[0].name
Out[10]:'kobe'
In [11]: q[0].age
Out[11]:20
2、执行原生查询
执行调用原生数据驱动执行sql
with connection.cursor()as cursor:
cursor.execute("select * from t_student")
one=cursor.fetchone()print(one)
two=cursor.fetchmany(2)print(two)all=cursor.fetchall()print(all)
执行结果
(2,'kobe',20,None,None, datetime.datetime(2023,1,1,15,26,3,84534),5)((3,'li',20,None,None, datetime.datetime(2023,1,1,15,26,3,84534),1),(4,'james',18,'13888888888',None, datetime.datetime(2023,1,6,7,7,17,669711),5))((5,'kelai',22,'13688888888',None, datetime.datetime(2023,1,6,7,7,17,850752),1),(6,'kd',19,'13888888777',None, datetime.datetime(2023,1,6,7,7,17,875757),4),(7,'harden',18,'13888878228',None, datetime.datetime(2023,1,6,7,21,25,347180),4),(8,'curry',22,'13688883388',None, datetime.datetime(2023,1,6,7,21,25,347180),1),(9,'ad',19,'13888855577',None, datetime.datetime(2023,1,6,7,21,25,347180),1),(10,'happy',None,None,None, datetime.datetime(2023,1,7,8,19,56,927533),4))
版权归原作者 敲代码敲到头发茂密 所有, 如有侵权,请联系我们删除。