Django之ORM(多对多)

Stella981
• 阅读 759

一、ManyToManyField

1、class RelatedManager

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

它存在于下面两种情况:

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

简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。

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、书籍与作者多对多举例

Django之ORM(多对多) Django之ORM(多对多)

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

Django之ORM(多对多) Django之ORM(多对多)

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

Django之ORM(多对多) Django之ORM(多对多)

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

Django之ORM(多对多) Django之ORM(多对多)

{#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值)                           --> 在原来的基础上增加新的纪录

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Stella981 Stella981
3年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Wesley13 Wesley13
3年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这