一、ManyToManyField
1、class RelatedManager
"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。
它存在于下面两种情况:
- 外键关系的反向查询
- 多对多关联关系
简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。
2、方法
1)create()
创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。
>>> import datetime
>>> models.Author.objects.first().book_set.create(title="番茄物语", publish_date=datetime.date.today())
2)add()
把指定的model对象添加到关联对象集中。
# 添加对象
>>> author_objs = models.Author.objects.filter(id__lt=3)
>>> models.Book.objects.first().authors.add(*author_objs)
# 添加id
>>> models.Book.objects.first().authors.add(*[1, 2])
3)set()
更新model对象的关联对象。
>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.set([2, 3])
4)remove()
从关联对象集中移除执行的model对象。
>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.remove(3)
5)clear()
从关联对象集中移除一切对象。
>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.clear()
注意:
对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。
示例:
# ForeignKey字段没设置null=True时,
class Book(models.Model):
title = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Publisher)
# 没有clear()和remove()方法:
>>> models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'RelatedManager' object has no attribute 'clear'
# 当ForeignKey字段设置null=True时,
class Book(models.Model):
name = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Class, null=True)
# 此时就有clear()和remove()方法:
>>> models.Publisher.objects.first().book_set.clear()
4、书籍与作者多对多举例
from django.db import models
# Create your models here.
class Publisher(models.Model):
name = models.CharField(max_length=12)
# 书籍表
class Book(models.Model):
title = models.CharField(max_length=32)
publisher = models.ForeignKey(to="Publisher", on_delete=models.CASCADE)
# 作者表
class Author(models.Model):
name = models.CharField(max_length=12)
# 多对多,自动帮我们在数据库建立第三张关系表
books = models.ManyToManyField(to='Book', related_name="authors")
models.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^author_list/$', views.author_list),
url(r'^delete_author/(\d+)/$', views.delete_author),
url(r'^add_author/$', views.AddAuthor.as_view()),
url(r'^edit_author/(\d+)/$', views.EditAuthor.as_view()),
]
urls.py
from django.shortcuts import render, redirect, HttpResponse
from app01 import models
from django import views
# Create your views here.
def author_list(request):
author_list = models.Author.objects.all()
return render(request, "author_list.html", {"data": author_list})
def delete_author(request, delete_id):
# models.Author.objects.get(id=delete_id) # 很少用,谨慎使用
models.Author.objects.filter(id=delete_id).delete()
return redirect("/author_list/")
# 添加作者
class AddAuthor(views.View):
def get(self, request):
book_list = models.Book.objects.all()
return render(request, "add_author.html", {"book_list": book_list})
def post(self, request):
print(request.POST)
# 用户新创建的作者名字
author_name = request.POST.get("name")
# 用户给新作者设置的书名id, 因为是多选所以要用getlist取值
books_ids = request.POST.getlist("books")
print(author_name, books_ids)
# 1. 先创建一个新的作者对象
author_obj = models.Author.objects.create(name=author_name)
# 2. 去第三张关系表,建立关系记录
author_obj.books.set(books_ids)
return redirect("/author_list/")
# return HttpResponse("OK")
class EditAuthor(views.View):
def get(self, request, edit_id):
author_obj = models.Author.objects.filter(id=edit_id).first()
book_list = models.Book.objects.all()
return render(request, "edit_author.html", {"author": author_obj, "book_list": book_list})
def post(self, request, edit_id):
author_obj = models.Author.objects.filter(id=edit_id).first()
new_name = request.POST.get("name")
new_books = request.POST.getlist("books")
# 真正的更新操作
author_obj.name = new_name
author_obj.save()
author_obj.books.set(new_books)
return redirect("/author_list/")
views.py
{#author_list.html#}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>作者列表</title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>#</th>
<th>id</th>
<th>作者名字</th>
<th>写过的书</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for author in data %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ author.id }}</td>
<td>{{ author.name }}</td>
<td>{% for book in author.books.all %}{{ book.title }},{% endfor %}</td>
<td>
<a href="/delete_author/{{ author.id }}/">删除</a>
<a href="/edit_author/{{ author.id }}/">编辑</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
{#edit_author.html#}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>编辑作者</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>作者名:
<input type="text" name="name" value="{{ author.name }}">
</p>
<p>书名:
<select name="books" multiple>
{% for book in book_list %}
<!-- 如果当前for循环的这本书在作者关联的书的列表里面 -->
{% if book in author.books.all %}
<option selected value="{{ book.id }}">{{ book.title }}</option>
<!-- 否则 -->
{% else %}
<option value="{{ book.id }}">{{ book.title }}</option>
{% endif %}
{% endfor %}
</select>
</p>
<p>
<input type="submit" value="提交">
</p>
</form>
</body>
</html>
{#add_author.html#}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加作者</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>作者名:
<input type="text" name="name">
</p>
<p>书名:
<select name="books" multiple>
{% for book in book_list %}
<option value="{{ book.id }}">{{ book.title }}</option>
{% endfor %}
</select>
</p>
<p>
<input type="submit" value="提交">
</p>
<p>
爱好:
<input type="checkbox" value="basketball" name="hobby">篮球
<input type="checkbox" value="football" name="hobby">足球
<input type="checkbox" value="doublecolorball" name="hobby">双色球
</p>
</form>
</body>
</html>
html代码
5、基于对象和QuerySet查询
import os
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()
from app01 import models
author_obj = models.Author.objects.first()
# 多对多的正向查询
ret = author_obj.books.all()
print(ret)
#多对多的反向查询
book_obj = models.Book.objects.last()
# 默认按照表名(全小写)_set.all()
# ret = book_obj.author_set.all()
# 如果多对多字段设置了related_name属性,反向查询的时候就按该属性值来查询
ret = book_obj.authors.all()
print(ret)
# add方法
author_obj = models.Author.objects.first()
ret = author_obj.books.all()
print(ret)
# 给作者加一本关联的书籍
author_obj.books.set([2, 3])
author_obj.books.add(2)
ret = author_obj.books.all()
print(ret)
#查询第一个作者写过的书的名字
#1. 基于对象的查询
ret = models.Author.objects.first().books.all().values("title")
print(ret)
#基于QuerySet的双下划线查询
ret = models.Author.objects.filter(id=2).values("books__title")
print(ret)
#基于QuerySet的双下划线的反向查询
#由书找作者
ret = models.Book.objects.filter(id=2).values("authors__name")
print(ret)
6、总结
ORM(多对多)
1. ORM多对多字段
# 多对多,自动帮我们在数据库建立第三张关系表
books = models.ManyToManyField(to='Book', related_name="authors")
参数:
- to:表示和哪张表建立多对多的关系
- related_name:表示返乡查询时使用的那个字段名,默认反向查询时使用表名_set的方式
2. 多对多字段的方法
1. 查询
.all() --> 多对多查询的方法,
2. 删除
3. 添加新作者
1. 当form表单提交的数据是列表(多选的select\多选的checkbox)取值?
request.POST.getlist("hobby")
2. .set([id1,id2,...]) 参数是一个列表 --> 删除原来的设置新的
3. .add(id值) --> 在原来的基础上增加新的纪录