目錄結構
3.1.第一步:在【helloworld/hello/models.py】裏新增模型類
3.2.第二步:在【helloworld/hello/admin.py】裏註冊模型類
3.3.第三步:在【helloworld/】根目錄下執行生成數據表的相關語句
4.1.類ManyToManyField裏的一個必填參數對應入參值的簡單分析
4.1.1.第一步:看類ManyToManyField源碼裏的【__init__】方法裏的內容
4.1.2.第二步:結合實際業務代碼,對必填參數【to】的參數值做分析
4.2.當一張【多】表的數據被刪除後,對應另外一張【多】表的數據和中間表的數據會有對應的哪些變化的操作記錄
1.寫這篇博客的目的
主要記錄:
- 如何通過django去創建【多對多】的表關係;
- 以及如何通過【多對多】的表關係去走對應的完整業務操作;
- 通過修改django裏的哪些代碼配置,使當一張【多】表的數據被刪除後,對應另外一張【多】表的數據和中間表的數據會有對應的哪些變化?
2.【多對多】表關係對應的業務例子
我們在現實生活裏會遇到很多多對多的場景。
比如一個最常見的例子是:一本書可以有多個作者,一個作者也可以寫多本書,書和作者就是多對多的關係;
一般會建三張數據表,一張表放書的信息,一張表放作者的信息,一張中間表一般有三個表字段且三個表字段分別存放書的主鍵值和作者的主鍵值和中間表本身的主鍵值;
完整操作流程可以看接下來的內容;
3.完整操作流程
3.1.第一步:在【helloworld/hello/models.py】裏新增模型類
from django.db import models class Auther(models.Model): '''作者''' name = models.CharField(max_length=10, verbose_name="作者") mail = models.CharField(max_length=30, verbose_name="郵箱") city = models.CharField(max_length=10, verbose_name="城市") class Meta: verbose_name_plural = '作者' def __str__(self): return self.name class Book(models.Model): '''書籍''' book_name = models.CharField(max_length=50, verbose_name="書名") auther_of_be_associated = models.ManyToManyField(to=Auther) class Meta: verbose_name_plural = '書籍' def __str__(self): return self.book_name
3.2.第二步:在【helloworld/hello/admin.py】裏註冊模型類
from django.contrib import admin # Register your models here. from hello import models class ControlAuther(admin.ModelAdmin): '''作者信息''' # 顯示的字段 list_display = ["id","name", "city", "mail"] class ControlBook(admin.ModelAdmin): '''書信息''' # 顯示的字段 # 屬性list_display裏的索引值爲1對應的【'被關聯的作者'】,這是我們自定義的要展示在列表頁面的列表字段(該列表字段不是表裏的表字段),且這必須是這個類ControlBook裏的我們手工創建的方法名【被關聯的作者】; list_display = ["book_name",'被關聯的作者' ] # 第一步:定義一個方法(方法名任意,方法名爲中文也可以只是不規範),比如方法名爲【被關聯的作者】; # 第二步:在方法【被關聯的作者】裏,首先調用模型類Book提供的方法【all()】即通過【obj.auther_of_selected.all()】,獲取到book表裏的一條書籍數據關聯的auther表裏的0到多條的完整的作者數據(每條作者數據都包含了每個表字段和每個表字段對應的表字段值); # 細節:【obj.auther_of_selected.all()】裏的【auther_of_selected】是模型類Book裏的一個屬性且這個屬性的屬性值必須是類ManyToManyField被實例化後生成的對象; # 第三步:在方法【被關聯的作者】裏,接着用for遍歷獲取每條作者數據裏的表字段name的表字段值並用list類提供的append方法用一個空列表來存儲這些表字段值,然後返回這個列表; def 被關聯的作者(self,obj): # 第一種寫法,官方推薦寫法,語法最簡潔,因爲只需要一行代碼; # return [a.name for a in obj.auther_of_selected.all()] # 第二種寫法,個人根據第一種寫法寫的比較簡單易懂的寫法,語法不簡潔,因爲需要多行代碼; emptyList = [] for a in obj.auther_of_be_associated.all(): emptyList.append(a.name) return emptyList # list_display = ["book_name",'show_all_auther_of_be_associated' ] # def show_all_auther_of_be_associated(self,obj): # # return [a.name for a in obj.auther_of_selected.all()] # emptyList = [] # for a in obj.auther_of_be_associated.all(): # emptyList.append(a.name) # return emptyList
3.3.第三步:在【helloworld/】根目錄下執行生成數據表的相關語句
執行的相關語句和順序是:
- 首先,先執行語句【python manage.py makemigrations】
- 最後,再執行語句【python manage.py migrate】
相關結果如下:
- 會生成三張對應的數據表,其中會包含一張中間表;
3.4.第四步:重啓服務
3.5.第五步:成功登陸admin管理後臺
3.6.第六步:在後臺新增2個作者信息
3.7.第七步:在後臺新增1本書籍信息
4.相關知識點
4.1.類ManyToManyField裏的一個必填參數對應入參值的簡單分析
細節:
- 從類ManyToManyField的類名,可以直觀知道單詞ManyToManyField的中文含義是【多對多】;
4.1.1.第一步:看類ManyToManyField源碼裏的【__init__】方法裏的內容
class ManyToManyField(RelatedField): """ Provide a many-to-many relation by using an intermediary model that holds two ForeignKey fields pointed at the two sides of the relation. Unless a ``through`` model was provided, ManyToManyField will use the create_many_to_many_intermediary_model factory to automatically generate the intermediary model. """ # Field flags many_to_many = True many_to_one = False one_to_many = False one_to_one = False rel_class = ManyToManyRel description = _("Many-to-many relationship") def __init__(self, to, related_name=None, related_query_name=None, limit_choices_to=None, symmetrical=None, through=None, through_fields=None, db_constraint=True, db_table=None, swappable=True, **kwargs): try: to._meta except AttributeError: assert isinstance(to, str), ( "%s(%r) is invalid. First parameter to ManyToManyField must be " "either a model, a model name, or the string %r" % (self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT) ) if symmetrical is None: symmetrical = (to == RECURSIVE_RELATIONSHIP_CONSTANT) if through is not None: assert db_table is None, ( "Cannot specify a db_table if an intermediary model is used." ) kwargs['rel'] = self.rel_class( self, to, related_name=related_name, related_query_name=related_query_name, limit_choices_to=limit_choices_to, symmetrical=symmetrical, through=through, through_fields=through_fields, db_constraint=db_constraint, ) self.has_null_arg = 'null' in kwargs super().__init__(**kwargs) self.db_table = db_table self.swappable = swappable
截取這部分源碼:
def __init__(self, to, related_name=None, related_query_name=None, limit_choices_to=None, symmetrical=None, through=None, through_fields=None, db_constraint=True, db_table=None, swappable=True, **kwargs):
從這部分源碼可以簡單看出來:類ManyToManyField被實例化時,必須給一個必填參數【to】賦值,其他入參的入參值一般情況下保持默認值即可;
4.1.2.第二步:結合實際業務代碼,對必填參數【to】的參數值做分析
實際業務代碼如下:
class Auther(models.Model): '''作者''' name = models.CharField(max_length=10, verbose_name="作者") mail = models.CharField(max_length=30, verbose_name="郵箱") city = models.CharField(max_length=10, verbose_name="城市") class Meta: verbose_name_plural = '作者' def __str__(self): return self.name class Book(models.Model): '''書籍''' book_name = models.CharField(max_length=50, verbose_name="書名") auther_of_be_associated = models.ManyToManyField(to=Auther) class Meta: verbose_name_plural = '書籍' def __str__(self): return self.book_name
截取這部分業務代碼:
auther_of_be_associated = models.ManyToManyField(to=Auther)
4.1.2.1.對必填參數【to】的參數值的理解
參數【to】的含義是:關聯到對應的一張表(這張表在多對多表關係裏是兩張表裏被關聯的那張表);
參數【to】的參數值:必須只能是這個【多對多表關係裏的】兩個模型類裏的其中一個被關聯的模型類的模型類名;
4.2.當一張【多】表的數據被刪除後,對應另外一張【多】表的數據和中間表的數據會有對應的哪些變化的操作記錄
假設作者表現在有這樣兩條數據(我們把這兩條數據分別命名爲數據A和數據B,方便後續相關文案的描述):
假設書籍表現在有這樣兩條數據(我們把這兩條數據分別命名爲數據C和數據D,方便後續相關文案的描述):
假設作者表和書籍表共同對應的中間表現在有這樣四條數據(我們把這四條數據分別命名爲數據E1和數據E2和數據E3和數據E4,方便後續相關文案的描述):
細節:
- 中間表這四條數據的含義是:書籍1的作者是:作者1和作者2、書籍2的作者是:作者1和作者2;
數據都造好了,接着我們驗證這些刪除場景(我都已在本地調試通過):
- 當從admin管理後臺,從作者列表裏刪除了【作者1】,那麼:作者表裏的數據A會被刪掉、中間表裏的數據E1和數據E3會被刪掉;
- 當從admin管理後臺,從作者列表裏刪除了【書籍1】,那麼:書籍表裏的數據C會被刪掉、中間表裏的數據E1和數據E2會被刪掉;