Django中多对多关系的orm表设计
作者的管理
1.设计表结构
出版社 书籍 作者
一个出版社出版多个书籍 1对多
书籍和作者的关系:一个作者写多本书,一本书可以是多个作者写。多对多
1)创建一张表,表中多对多的数据关系。使用 多对多字段的方法
2)创建的这个类,会创建两个表,app_加类名(开头变小写),app_这个类名_另一张表类名
3)这个类创建的表中没有多对多的字段,多对多的关系是以这张表表名加_id和另一张表表名加_id两个字段合成的新的表,id主键自动生成
4)一般多对多字段关联的是有外键的那张表。
2.展示含有多对多关系的作者表
2.1、设计url
url(r'^author_list/', views.author_list),
2.2、写函数
# 展示作者
for author in all_authors: print(author) print(author.pk) print(author.name) print(author.books,type(author.books)) # 关系管理对象 print(author.books.all(),type(author.books.all())) print('*'*30)
def author_list(request): # 查询所有的作者 all_authors = models.Author.objects.all() return render(request,'author_list.html',{'all_authors':all_authors})
点击这里可清空打印内容:
打印查看各个变量的含义
<class 'django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager'> 关系管理对象
通过关系管理对象.all()拿到所有关联的对象
由上可知:从作者表author.books.all()[0].title 获得了book表的书名,那么由author.books.all()[0].pub就能获得对应的一条出版社表的记录。从这张多对多关系的表中,获取到对应多对多关系另一张表的多条记录,又由此能获得另一张表的具有外键的对应的外键表记录,三表联系在一起了。
Book和Author多对多的关系。books是Author中你想要的对象跟Book关联的所有对象。books的作用是在Author和Book中创建一个中间的关系对象,放在第三张表中,再根据这个中间的关系来获取Book中关联的对象
外键和多对多关系区别:
外键是根据book.pub拿到的就是关联的外键对象(每行数据);而多对多是Auther.books直接拿到的是一个关系管理对象,关系管理对象.all()才能拿到所有关联对象(每行数据)。
一个错误:一个函数重定向到自己这个函数会出现递归现象,达到递归上限,报错重定向次数过多,因此当这个函数想要重新返回到自身页面的时候用render,转到其它页面用render或者重定向
2.3、写模板
{% for author in all_authors %} <tr> <td>{{ forloop.counter }}</td> <td>{{ author.pk }}</td> <td>{{ author.name }}</td> <td> {% for book in author.books.all %} {% if forloop.last %} 《{{ book.title }}》 {% else %} 《{{ book.title }}》、 {% endif %} {% endfor %} </td> </tr> {% endfor %}
将对象列表循环出来再取需要的字段,并添加书名号。
将循环出来的每个内容用顿号隔开。
如果只是在后面加个逗号,那么最后那个都会显示逗号:
for循环中批量操作数据,通过添加判断实现满足条件的输出不同内容,
3.多对多关系表的数据增加
# 新增出版社 def add_publisher(request): error = '' # 对请求方式进行判断 if request.method == 'POST': # 处理POST请求 # 获取到出版社的名称 publisher_name = request.POST.get('publisher_name') # 判断出版社名称是否有重复的 if models.Publisher.objects.filter(name=publisher_name): error = '出版社名称已存在' # 判断输入的值是否为空 if not publisher_name: error = '不能输入为空' if not error: # 使用ORM将数据插入到数据库中 obj = models.Publisher.objects.create(name=publisher_name) # 跳转到展示出版社的页面 return redirect('/publisher_list/') # 返回一个包含form表单的页面 return render(request, 'add_publisher.html', {'error': error})
author_obj = models.Author.objects.create(name=author_name) # 只插入book表中的内容 author_obj.books.set(books) # 设置作者和书籍多对多的关系
一个功能,一个url地址,一个函数,外加一个模板。每个功能都是这样
添加作者,选择作者写的多个书籍,提交。实现多选功能:
添加作者:将提交的作者名字和作品获取到并写入数据库,然后重定向到作者展示页面
如下:在页面选中多个提交,但是get获取book的值只获取到一个,应该是选择的book列表才是我们想要的,但是get只是获取到了最后一个数值
正确使用时getlist方法,获取select中多选的值
getlist获取到的类型是列表。多选中选中多个选项获取的值:
存入数据库:
1)先用input输入的作者名字创建作者表对象
2)再获取出来select选中多个关联选项
3)新建的作者对象.books获得关系管理对象,关系管理对象.set(要关联的多个外表的id列表)。由此,这个作者表对象set设置了和外表的多条记录的关系映射,并在第三张表中体现出来
【1】插入数据【2】设置它对多的关系
如下演示:
结果如下:表之接.东西,报错对象没有这个属性,这里是缺少objects了,直接表.create会把它当做表的字段看待
正确结果:,注意create不要将给多对多的代表关系管理对象的设置值
然后再设置重定向到book_list页面
4.多对多关系表的数据删除
删除操作逻辑如下:
拿到要删除的数据id,然后查表找到对应数据进行删除再返回到展示页面
url设置,写函数,作者展示首页添加删除按钮:
删除成功;
5.多对多关系表的数据修改
books = request.POST.getlist('books') # 修改对象的数据 author_obj.name = name author_obj.save() # 多对多的关系 author_obj.books.set(books) # 每次重新设置
编辑和添加的html差不多,直接复制过来修改一下:
有多对多关系的表数据编辑:
1)url
2)函数
3)html
(1)作者展示页点击编辑a标签get请求并带有编辑的对象pk跳转到编辑页
(2)编辑页不是多对多关系的字段可以用输入框
(3)是多对多关系的需要使用多选框,多选框中是另一个表中所有作品,被选中的即已经是作者的作品 是与作者pk条件相等的作者表中关系管理对象的所有对象
逻辑关系如下:
# 编辑作者 def edit_author(request): # 查询编辑的作者对象 pk = request.GET.get('pk') author_obj = models.Author.objects.get(pk=pk) if request.method == 'POST': # 获取提交的数据 name = request.POST.get('author_name') books = request.POST.getlist('books') #注意是getlist,不然只能获取到一个数字 # 修改对象的数据 author_obj.name = name author_obj.save() # 多对多的关系 author_obj.books.set(books) # 每次重新设置 #不管是新增还是修改都使用set就行了 # 重定向 return redirect('/author_list/') # 查询所有的书籍 all_books = models.Book.objects.all() #请求方式判断在前,因为POST中用不到这个 return render(request, 'edit_author.html', {'author_obj': author_obj, 'all_books': all_books})
每次编辑都是重新设置,也就是set之前会把和这个对象在关联表中关联的删掉并重新创建
6.django设置多对多关系三种方法
1.django帮我们生成第三张表
class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField('Book') # 不在Author表中生产字段,生产第三张表
2.自己创建第三张表
class AuthorBook(models.Model): author = models.ForeignKey(Author, on_delete=models.CASCADE) book = models.ForeignKey(Book, on_delete=models.CASCADE) date = models.DateField()
注销不用books
class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField('Book',through='AuthorBook') # 不在Author表中生产字段,生产第三张表 class AuthorBook(models.Model): author = models.ForeignKey(Author, on_delete=models.CASCA DE) book = models.ForeignKey(Book, on_delete=models.CASCADE) date = models.DateField()
1)第三种方式可以使用 ManyToManyField 方法,通过第一个字符串参数指定和哪个表关联;通过 through='自建第三张表'和自建第三张表联系起来,这样它本身不会创建第三张关联表了,而是以你写的第三张表的来作为它和它关联表之间的多对多关联关系。
2)这样的好处是我可以在django建立的关系表上添加字段(比如建立第三张关联表,图书id和作者id一一对应,但是我想要作者编写图书的时间,那么最好的地方就是放到这张表,而自建第三张表的方式才能给表添加字段;但是自建的表查询语句还要自己写,)
3)自建第三张表既能增加数据表字段,也能通过books.all()获取数据,但是不能使用set等某些方法,要想查数据还是要自己写语句
4)自建第三张表是自己设置另外两张表的外键字段