1. 程式人生 > >Python筆記18(Django之ORM(多對多))

Python筆記18(Django之ORM(多對多))

方式 The short erro edm 關聯對象 對象的關聯 .site views

一、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、書籍與作者多對多舉例

技術分享圖片
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值) --> 在原來的基礎上增加新的紀錄

Python筆記18(Django之ORM(多對多))