ORM 创建manytomay的三种方法 反向查询 和一些 双下方法版学员管理系统3

老师信息管理

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

  三种创建多对对外键的方式常用第二种和第三种

思考

三种方式创建多对多外键方式及其优缺点。

外键的查询和使用

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

1外键的创建:

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

在数据库表中的表现形式

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

如何连表查询和使用

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

表里边:  student_obj.cid_id=Class对像.id  ;   Student_obj.cid = Class对象

如何继续往查外键对应的数据      用查出来的     对象.cid.属性名   的方法来查                   cid就表示Class表中的一个对象,即某一条记录

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

增加数据的写法

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

如何反向从被外键关联的对象中找到与他关联的数据            注意:当设置了related_name:  时候不能用此方法  默认用  related_name的值

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

设置了外键的反向查找

当设置了related_name 时可以这样查找

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

查找使用方法

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

一一对应的表用根据表名反向查找  因为只能用   .表名的方式反向查找

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

一对一正向查找

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

通过外键创建

class Class(models.Model):
id = models.AutoField(primary_key=True) # 主键
cname = models.CharField(max_length=32) # 班级名称
first_day = models.DateField() # 开班时间 class Teacher(models.Model):
tname = models.CharField(max_length=32) # 自定义第三张表,通过外键关联上面两张表
class Teacher2Class(models.Model):
tid = models.ForeignKey(to="Teacher")
cid = models.ForeignKey(to="Class") class Meta:
unique_together = ("tid", "cid") #因为不能出现重复,这里设置组合唯一键

通过ManyToManyField创建

自动创建的第三章表,多对多关联表,所以不能通过ORM单独查询或者操作那个自动出现的表格

class Class(models.Model):
id = models.AutoField(primary_key=True) # 主键
cname = models.CharField(max_length=32) # 班级名称
first_day = models.DateField() # 开班时间 class Teacher(models.Model):
tname = models.CharField(max_length=32)
# 通过ManyToManyField自动创建第三张表
cid = models.ManyToManyField(to="Class", related_name="teachers")
注意自动生成的第三章表的名字的格式

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3


通过外键和ManyToManyField创建

第三张表是独立的表,可以通过django ORM单独的对他进行去查找和更新

class Class(models.Model):
id = models.AutoField(primary_key=True) # 主键
cname = models.CharField(max_length=32) # 班级名称
first_day = models.DateField() # 开班时间 class Teacher(models.Model):
tname = models.CharField(max_length=32)
# 通过ManyToManyField和手动创建第三张表
cid = models.ManyToManyField(to="Class", through="Teacher2Class", through_fields=("tid", "cid")) class Teacher2Class(models.Model):
tid = models.ForeignKey(to="Teacher")
cid = models.ForeignKey(to="Class") class Meta:
unique_together = ("tid", "cid")

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3


用法的选择

当想扩展第三那张,多对多的表进行单独查询或者操作,修改的时候用第三种方式,‘

否则一般用第二种

给第三个表加数据

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

表结构设计

class Teacher(models.Model):
tname = models.CharField(max_length=32)
cid = models.ManyToManyField(to="Class", related_name="teachers")

老师信息列表

URL部分

url(r'^teacher_list/$', app01_views.teacher_list, name="teacher_list"),

视图部分

def teacher_list(request):
teacher_list = models.Teacher.objects.all()
return render(request, "teacher_list.html", {"teacher_list": teacher_list})

前端部分

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>老师信息页面</title>
</head>
<body>
<a href="{% url 'add_teacher' %}">添加新老师</a>
<table border="1">
<thead>
<tr>
<th>#</th>
<th>id</th>
<th>老师姓名</th>
<th>授课班级</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for teacher in teacher_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ teacher.id }}</td>
<td>{{ teacher.tname }}</td>
<td>
{% for class in teacher.cid.all %}
{% if forloop.last %}
{{ class.cname }}
{% else %}
{{ class.cname }},
{% endif %}
{% endfor %}
</td>
<td>
<a href="{% url 'delete_teacher' teacher.id %}">删除</a>
<a href="{% url 'edit_teacher' teacher.id %}">编辑</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

删除老师信息

URL部分

url(r'^delete_teacher/(?P<tid>\d+)$', app01_views.delete_teacher, name="delete_teacher"),

视图部分

def delete_teacher(request, tid):
models.Teacher.objects.filter(id=tid).delete()
return redirect(reverse("teacher_list"))

前端部分

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>添加老师</title>
</head>
<body>
<form action="{% url 'add_teacher' %}" method="post">
{% csrf_token %}
<p>老师姓名:<input type="text" name="tname"></p>
<label for="class_id">授课班级:</label>
<select name="class_id" id="class_id" multiple>
{% for class in class_list %}
<option value="{{ class.id }}">{{ class.cname }}</option>
{% endfor %}
</select>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

添加老师信息

URL部分

url(r'^add_teacher/$', app01_views.add_teacher, name="add_teacher"),

视图部分

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3
def add_teacher(request):
if request.method == "POST":
tname = request.POST.get("tname")
class_ids = request.POST.getlist("class_id")
new_teacher = models.Teacher.objects.create(tname=tname)
# 查询出所有被选中的班级信息
class_objs = models.Class.objects.filter(id__in=class_ids)
# 将老师的授课班级设置为选中的班级, 以下四种都可以,注意什么时候加*
new_teacher.cid.set(class_objs)
# new_teacher.cid.add(*class_objs)
# new_teacher.cid.add(*class_ids)
# new_teacher.cid.set(class_ids)
new_teacher.save()
return redirect(reverse("teacher_list")) class_list = models.Class.objects.all()
return render(request, "add_teacher.html", {"class_list": class_list})
ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

前端部分

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>添加老师</title>
</head>
<body>
<form action="{% url 'add_teacher' %}" method="post">
{% csrf_token %}
<p>老师姓名:<input type="text" name="tname"></p>
<label for="class_id">授课班级:</label>
<select name="class_id" id="class_id" multiple>
{% for class in class_list %}
<option value="{{ class.id }}">{{ class.cname }}</option>
{% endfor %}
</select>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

编辑老师信息

URL部分

url(r'^edit_teacher/(?P<tid>\d+)$', app01_views.edit_teacher, name="edit_teacher"),

视图部分

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3
def edit_teacher(request, tid):
teacher_obj = models.Teacher.objects.get(id=tid)
class_list = models.Class.objects.all() if request.method == "POST":
tname = request.POST.get("tname")
class_ids = request.POST.getlist("class_id")
# 更新老师相关信息
teacher_obj.tname = tname
teacher_obj.cid.set(class_ids)
teacher_obj.save() # 一定记得更新完要保存
return redirect(reverse("teacher_list")) return render(request, "edit_teacher.html", {"class_list": class_list, "teacher": teacher_obj})
ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

前端部分

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>编辑老师信息</title>
</head>
<body>
<form action="{% url 'edit_teacher' teacher.id %}" method="post">
{% csrf_token %}
<p>老师姓名:<input type="text" name="tname" value="{{ teacher.tname }}"></p>
<label for="class_id">授课班级:</label>
<select name="class_id" id="class_id" multiple>
{% for class in class_list %}
{% if class in teacher.cid.all %}
<option value="{{ class.id }}" selected>{{ class.cname }}</option>
{% else %}
<option value="{{ class.id }}">{{ class.cname }}</option>
{% endif %}
{% endfor %}
</select>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>
ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

基于对象的跨表查询

一对多查询(班级表和学生表)

正向查询(由学生表查询班级表)

查询学生的班级信息

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3
>>> student_obj = models.Student.objects.first()
>>> student_obj.cid # 通过model类中的属性查找到对应的外键数据对象
<Class: Class object>
>>> student_obj.cid.cname
'1班'
>>> student_obj.cid_id # 获取实际外键的值
1
ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

反向查询(由班级表查询学生表)

查询班级的学生信息

>>> class_obj = models.Class.objects.first()  # 获取第一个班级对象
>>> class_obj.student_set.all() # 通过表名_set反向查询出所有的学生
<QuerySet [<Student: Student object>, <Student: Student object>]>

注意:

如果不在外键的字段中设置related_name的话,默认就用表名_set。

如果设置了related_name="students",反向查询时可直接使用students进行反向查询。

>>> class_obj.students.all() 

一对一查询

表结构设计

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3
class Student(models.Model):
sname = models.CharField(max_length=32, verbose_name="学生姓名")
the_class = models.ForeignKey(to=Class, to_field="id", on_delete=models.CASCADE, related_name="students")
detail = models.OneToOneField(to="StudentDetail", null=True) class StudentDetail(models.Model):
height = models.PositiveIntegerField()
weight = models.PositiveIntegerField()
email = models.EmailField()
ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

正向查询(由学生信息表查询学生详情表)

>>> student_obj = models.Student.objects.first()
>>> student_obj.detail.email
'1@1.com'

反向查询(由学生详情表反向查询学生信息表)

>>> detail_obj = models.StudentDetail.objects.get(id=1)
>>> detail_obj.students.sname 根据related_name 反向取数据
'a'

多对多操作

正向查询(由老师表查询班级表)

>>> teacher_obj = models.Teacher.objects.first()
>>> teacher_obj.cid.all() # 查询该老师授课的所有班级
<QuerySet [<Class: Class object>, <Class: Class object>]>

反向查询(由班级表反向查询老师表)

>>> class_obj = models.Class.objects.first()
>>> class_obj.teachers.all() # 此处用到的是related_name,如果不设置的话就用默认的表名_set
<QuerySet [<Teacher: Teacher object>, <Teacher: Teacher object>, <Teacher: Teacher object>]>

class RelatedManager

"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。

它存在于下面两种情况:

  1. 外键关系的反向查询
  2. 多对多关联关系

常用方法

create()

创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。

>>> import datetime
>>> teacher_obj.cid.create(cname="9班", first_day=datetime.datetime.now())

创建一个新的班级对象,保存对象,并将它添加到关联对象集之中。返回新创建的对象:

>>> class_obj = models.Class.objects.first()
>>> class_obj.student_set.create(sname="小明")

上面的写法等价于下面的写法,但是比下面的这种写法更简单。

>>> class_obj = models.Class.objects.first()
>>> models.Student.objects.create(sname="小明", cid=class_obj)

add()

把指定的model

对象添加到关联对象集中。即多多对应的那张表中去

添加对象

>>> class_objs = models.Class.objects.filter(id__lt=3)
>>> models.Teacher.objects.first().cid.add(*class_objs) 这里要用打撒的方法

添加id

>>> models.Teacher.objects.first().cid.add(*[1, 2])

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

add()括号中被传入的内容可以是   对象,也可以是id 值 

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

set

set()  这个是设置(可以是对某些值的更改)

更新model对象的关联对象。  set 传入的列表对象不用打散  直接传入就可  add()  传入的需要打散

>>> teacher_obj = models.Teacher.objects.first()
>>> teacher_obj.cid.set([2, 3])

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

remove()

从关联对象集中移除执行的model对象

>>> teacher_obj = models.Teacher.objects.first()
>>> teacher_obj.cid.remove(3)

对于ForeignKey对象,这个方法仅在null=True时存在

clear()

从关联对象集中移除一切对象。

>>> teacher_obj = models.Teacher.objects.first()
>>> teacher_obj.cid.clear()

同理,对于ForeignKey对象,这个方法仅在null=True时存在。

注意:

  1. 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。

了不起的双下划线

使用场景:

他是用于跨表查询

正向使用:

要使用外键名__对应表的字段名   的方法来查找

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

反向使用

使用外键的related_name设置的名字 __字段名(关联表的字段名) 的方法

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

表的设计详见下图

在这之前我们所有的跨表查询都是基于对象的查询。

比如:

注意,双下方法是将写的内容作为条件放到筛选括号里边的,  如

models.Class.objects.filter(students__sname__contains="龙")  查找名字中有龙的学生    注意  students  是Class的一个外键,  通过他关联到Student 表

Django还提供了一种直观而高效的方式在查询中表示数据表之间的关联关系,它能自动确认 SQL JOIN 关系。

需要做跨关系查询时,就可以使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

ORM   创建manytomay的三种方法  反向查询 和一些 双下方法版学员管理系统3

上一篇:Liferay7 BPM门户开发之46: 集成Activiti用户、用户组、成员关系同步


下一篇:3.1 一个简单的Java应用程序