Django モデル レイヤー パート 2 - 複数テーブルのリレーションシップの作成と複数テーブルの操作

序文

前回の記事に引き続き、本記事ではマルチテーブル操作について紹介します。django ORM を使用すると、複数テーブルの関係を作成でき、複数のテーブル間の操作もサポートされます。テーブルの関係を作成し、2 つの部分をクエリして、django ORM の複数テーブル操作を説明します。説明のための例として、著者、書籍、出版社、および著者情報のいくつかの表を取り上げます。

テーブル リレーションシップの作成

注: 実際の開発では、外部キーを使用してテーブル関係を確立すること、つまり、カスケード更新およびカスケード削除を使用するのではなく、論理外部キー関係を使用してテーブル関係を確立することはお勧めしません。

上記 4 つのテーブルのうち、Books と Publishing House の関係は 1 対多の関係であり、クエリの頻度が高い側に外部キーが設定されます。著者テーブルと著者詳細テーブルは1対1の関係に属します. 外部キーはクエリ頻度の高い側に確立されます. 著者と本は多対多の関係に属します. 3番目のテーブルは、クエリの頻度が高い側に外部キーを構築することをお勧めします。テーブル作成時は必ずデータベース移行コマンドを実行してくださいね~

テーブル リレーションシップを作成する場合、最初にテーブル モデルを作成してから、外部キー フィールドを追加できます. また、django ORM を使用して外部キー リレーションシップを作成する場合、関連する外部キー フィールドがフィールドの後に自動的に追加されます。テーブルとテーブルの_idデフォルトの関係 関連付けられたフィールドとして主キーを使用します。また、テーブル リレーションシップを作成する場合、実際の外部キーを関連付けに使用するのではなく、論理リレーションシップを使用してテーブル リレーションシップを指定することをお勧めします。

class Book(models.Model):
    name = models.CharField(max_length=60, verbose_name='图书名')
    # 浮点数字段,max_digits表示数字共8位, decimal_place表示小数点占2位
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='图书价格')
    inventory_num = models.IntegerField(verbose_name='库存数量')
    sell_num = models.IntegerField(verbose_name='卖出数量')
    # 一对多关系外键字段创建使用 ForeigenKey(to='表名'),默认关联主键字段, db_constraint=Flase表示不建立实质的外键关系,只是逻辑上的关系
    publish = models.ForeignKey(to='Publish', on_delete=models.DO_NOTHING, db_constraint=False, verbose_name='外键关联出版社')
    # 多对多关系,使用ManyToManyField(to='表名'),author是一个虚拟的字段,主要是用来告诉ORM,书籍和作者是多对多的关系,而且ORM会自动创建多对多关系的第三张
    author = models.ManyToManyField(to='Author', on_delete=models.DO_NOTHING, db_constraint=False, verbose_name='外键关联作者')


class Publish(models.Model):
    name = models.CharField(max_length=12, verbose_name='出版社名称')


class Author(models.Model):
    name = models.CharField(max_length=10, verbose_name='作者名称')
    # 一对一关系使用OneToOneField(to='表名')
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.DO_NOTHING, db_constraint=False, verbose_name='外间关联作者详情')


class AuthorDetail(models.Model):
    age = models.IntegerField(verbose_name='年龄')
    phone = models.CharField(max_length=11, verbose_name='手机号')

さらに、多対多のテーブル リレーションシップを作成するには、完全自動作成、半自動作成、および完全手動作成の 3 つの方法があることを追加する必要があります。

# 全自动创建 - ManyToManyField,一般这种方式可以满足需求
'''
使用全自动创建多对多关系的优点就是无需手动创建第三张表,非常方便,django ORM直接提供操作第三张表关系的方法
缺点就是无法扩展第三张关系表
'''
class Book(models.Model):
	name = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author')
    
class Author(models.Model):
    name = models.CharField(max_length=32)
    
    
    
# 纯手动创建 -  ForeignKey手动创建第三张表
'''
第三张表完全取决于手动的扩展,但是需要写的代码较多,而且无法使用ORM提供的简单方法
'''
class Book(models.Model):
    name = models.CharField(max_length=32)
    
class Author(models.Model):
	name = models.CharField(max_length=32)
	
class Book2Author(models.Model):
    book_id = models.ForeignKey(to='Book')
    author_id = models.ForeignKey(to='Author')
    
# 半自动创建,通过ManyToManyField的参数控制第三张表
class Book(models.Model):
    name = models.CharField(max_length=32)
    authors = models.ManyToManyField(
        to='Author', # 告诉ORM不需要自动帮忙创建第三张关系表
        through='Book2Author', # 告诉ORM第三张关系表对应的表的外键字段
        through_fields=('book','author')  # 通过哪两个字段关联表,当前在哪张表,就先写哪个表的关联字段
                                     )
class Author(models.Model):
    name = models.CharField(max_length=32) 
    
class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')

複数テーブルのデータ操作 - 追加、削除、変更

最初に、複数テーブル操作の追加、削除、および変更操作を紹介します。複数テーブルのクエリ データ操作はもう少し面倒なので、小さな作業を個別に行います。

1 対多および 1 対 1 の関係 - 追加、削除、変更

1 対 1 および 1 対多の追加、削除、および変更操作は、基本的に同じです。

データを増やす

データを追加するには 2 つの方法があります。1 つは実際のフィールドを介して追加する方法で、もう 1 つは仮想フィールド オブジェクトの割り当てを介して追加する方法です。

# 方式1:通过实际字段
book_obj = models.Book.objects.create(name='哈利波特', price=10.2, publish_id=1)

# 方式2,先获取出版社对象,再将书籍和出版社通过出版社对象进行关联
publis_obj = models.Publish.objects.filter(pk=1).first()
book_obj = models.Book.objects.create(name='哈利波特', price=10.2, publish=publis_obj)

データを削除する

実際のプロジェクト開発でのデータの削除は、実際には削除されませんが、データが削除されるかどうかを示すためにブール フィールドが使用されることに注意してください。

データの削除時に指定しない場合、on_delete=models.DO_NOTHINGデフォルトはカスケード更新およびカスケード削除です。

models.Publish.objects.filter(pk=1).delete()

データを変更する

データの変更と追加には 2 つの方法があります。

# 方式1
models.Book.objects.filter(pk=1).update(publish_id=1)

# 方式2
pub_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=1).update(publish=pub_obj)

多対多の関係 - 追加、削除、変更

まず第一に、多対多の追加、削除、および変更が 3 番目の関係テーブルを操作していることを明確にする必要がありますが、3 番目の関係テーブルは django によって自動的に作成されます。多対多の外部キー関係は book テーブルに構築されており、多対多の外部キー フィールドはauthorフィールドなので、book_obj.authorそれを渡すことで 3 つ目のテーブルを操作できます。

django によって自動的に作成される 3 番目のテーブルの多対多の関係のために、django はデータを操作するための追加のメソッドを提供します。

多対多の関係を追加する - add()

add()このメソッドは、データを 3 番目のリレーショナル テーブルに追加します。数値とオブジェクトの両方を角かっこで渡すことができ、複数の同時操作がサポートされます。

# 方式1,直接添加id值
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.add(1)		# 增加 1 1记录,即id为1的书绑定id为1的作者
book_obj.author.add(2, 3)   # 增加两条记录,1 2 和 1 3

# 方式2,通过对象添加关系
book_obj = models.Book.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
author_obj3 = models.Author.objects.filter(pk=3).first()
book_obj.author.add(author_obj1)				# 增加1条记录
book_obj.author.add(author_obj2, author_obj3)	# 增加2条

多対多の関係を削除する - remove()

remove()このメソッドは、3 番目のテーブルのデータを削除するために使用されます. 同様に、数値とオブジェクトの両方を括弧内に渡すことができ、複数のデータを同時に操作できます。

# 方式1:直接删除值
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.remove(2)  # 删除book_id为1和author_id都为2的记录
book_obj.authors.remove(1, 3)  # 删除多条

# 方式2:通过对象删除
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.remove(author_obj1, author_obj2)

多対多の関係を変更する - set()

set()メソッドは 3 番目のテーブルを変更するために使用されます. このメソッドは上書き操作であり, 以前の関係を新しい関係で上書きします. このメソッドのパラメータは, 数値またはオブジェクトを示すリストまたはタプルでなければなりません. また, 複数のデータをサポートします.同時に操作します。

# 方式1:直接通过值进行修改
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.set([2])  # 将book_id为1对应的author_id修改为2
book_obj.authors.set([1, 2])  # 将书的作者设置为id=1 和id=2的作者

# 方式2:通过对象进行修改
author_obj2 = models.Author.objects.filter(pk=2).first()
author_obj3 = models.Author.objects.filter(pk=3).first()
book_obj.authors.set([author_obj2, author_obj3])

3 番目のテーブルのオブジェクトのバインド関係をクリアする - clear()

clear()このメソッドは、3 番目の関係テーブル内のオブジェクトのバインド関係をクリアします。

book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.clear()

マルチテーブル クエリ

複数テーブルのクエリ操作を実行する前に、フォワード クエリとリバース クエリの概念を理解する必要があります。

フォワード クエリ: どのテーブルが外部キーで、関連付けられたテーブルをクエリすることは、書籍を通じて出版社にクエリを実行するなどのフォワード クエリです。

逆クエリ: 関連付けられたテーブルは、外部キー フィールドが配置されているテーブルを検索します。これは、出版社を通じて書籍をクエリするなどの逆クエリです。

サブクエリ

クエリがより複雑な場合は、サブクエリ方式を使用できます. サブクエリとは、段階的なクエリを意味し、最初のクエリで得られた結果は、次のクエリの条件として使用されます.

前方参照

フィールドごとにクエリを転送し、複数の結果が必要な場合外键字段.all()、複数のクエリ結果があることをどのように判断しますか? .all()追加せずに得られた結果が应用名.模型名.Noneたとえばfirst.Author.Noneこの場合、ORM ステートメントにエラーがないことを意味しますが、複数のクエリ結果がある場合は、追加する必要があります.all()クエリ結果が 1 つの場合は 1 つのモデル オブジェクトが取得され、複数ある場合は QuerySet オブジェクトが取得されます。

# 一对多关系查询:查询书籍主键为1的书籍由哪个出版社出版
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.publish  # Publish object (1)
print(res.name)

# 多对多关系查询:查询数据主键为1的作者
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.author  # first.Author.None,说明结果有多个
res_many = book_obj.author.all()  # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>
    
# 一对一关系查询:查询作者lili的年龄
author_obj = models.Author.objects.filter(name='lili').first()
res = author_obj.author_detail
print(res.phone)

逆引き

逆クエリが 1 対 1 の場合は表名小写1 対多または多対多の場合に true 表明小写——set. また、結果が複数ある場合は小文字を示した後に追加する必要があります_set.all(). の方法複数の結果があるかどうかの判断は forward と同じです。クエリは同じです。クエリ結果が 1 つの場合は 1 つのモデル オブジェクトが取得され、複数ある場合は QuerySet オブジェクトが取得されます。

# 一对多关系查询,查询出版社是东方出版社出版的书
publish_obj = models.Publish.objects.filter(name='东方').first()
res = publish_obj.book_set.all()  # <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>]>

# 多对多关系查询,查询作者是lili写过的书
author_obj = models.Author.objects.filter(name='lili').first()
res = author_obj.book_set  # first.Book.None,说明有多个结果
res_many = author_obj.book_set.all()
print(res_many)  # <QuerySet [<Book: Book object (1)>]>

# 一对一关系查询,查询手机号是119的作者
author_detail_obj = models.AuthorDetail.objects.filter(phone='119').first()
res = author_detail_obj.author
print(res.name)

ジョイン テーブル クエリ

結合テーブル クエリは、MySQ の SQL ステートメントの結合テーブル クエリと似ていますが、 django の ORM で二重アンダースコア結合テーブル クエリ(クロス テーブル クエリ)が使用されている点が異なります。1 行のコードを使用して、結合テーブル クエリの結果をクエリできます。また、結合テーブル クエリも、順方向および逆方向の関係に従います。

前方参照

# 一对多关系查询:查询书籍主键为1的出版社名称和书名
# 首先获取书籍对象,书籍是查询publish的基表,因此获取书名直接values('names')即可,而出版社的名字是通过外键字段跳到出版社的表中农,需要通过__找到需要的字段值
res = models.Book.objects.filter(pk=1).values('name', 'publish__name')  # <QuerySet [{'name': '哈利波特', 'publish__name': '东方'}]>

# 多对多关系查询,查询书籍主键为1的作者姓名
res = models.Book.objects.filter(pk=1).values('author__name')  # <QuerySet [{'author__name': 'lili'}, {'author__name': 'nana'}]>

# 一对一关系查询,查询lili的手机号和姓名
res = models.Author.objects.filter(name='lili').values('name', 'author_detail__phone').first()
print(res.get('name'), res.get('author_detail__phone'))

逆引き

# 一对多关系查询:查询数据主键为1的出版社名称和书的名字
res = models.Publish.objects.filter(book__id=1).values('name', 'book__name')  # <QuerySet [{'name': '东方', 'book__name': '哈利波特'}]>

# 多对多关系查询:查询书籍主键为1的作者姓名和书名
res = models.Author.objects.filter(book__id=1).values('name', 'book__name')  # <QuerySet [{'name': 'lili', 'book__name': '哈利波特'}, {'name': 'nana', 'book__name': '哈利波特'}]>

# 一对一关系查询:查询作者id是1作者的姓名和手机号
res = models.AuthorDetail.objects.filter(author__id=1).values('author__name', 'phone')  # <QuerySet [{'author__name': 'lili', 'phone': '119'}]>

# 综合大查询:查询书籍主键是1的作者的手机号,首先获取书籍对象,书籍关联了作者表,作者表又关联了作者详情表 
res = models.Book.objects.filter(pk=1).values('author__author_detail__phone')  # <QuerySet [{'author__author_detail__phone': '119'}, {'author__author_detail__phone': '120'}]>

集計クエリ

集計クエリは、通常、グループ化とともに使用されます. 集計クエリは、最大値、最小値、平均値などのいくつかの統計ツールと、集計関数のインポート方法を使用します. グループ化なしで集計関数を使用する場合は、使用する必要があります内部で使用されるfrom django.db.models import Max, Min, Sum, Count, Avg方法でそれをaggregate()

from django.db.models import Min,Max,Sum,Count,Avg
# 统计书的平均价格
res = models.Book.objects.aggregate(Avg('price'))
print(res)
# 可以将这些聚合函数同时使用
res = models.Book.objects.aggregate(Max('price'),Sum('price'),Count('pk'))
print(res)

グループ クエリ

集計関数は、通常、グループ化と一緒に使用されます. グループ化クエリの方法は、グループ化の基準annotateとしてデフォルトを使用することですmodels.分组依据, つまり、グループ化するテーブルの主キー. メソッドのannotate()前に表示される場合, それvalues()に従ってvaluesグループ化されますで指定された値。グループ クエリは__クロス テーブル クエリをサポートします。

from django.db.models import Sum, Max, Min, Avg, Count

# 1.统计每本书的作者个数
res = models.Book.objects.annotate(author_num=Count('author')).values('name', 'author_num')  # author_num是自己定义的字段用来存储统计出来的每本书对应的作者个数,暂时存为表中的字段

# 2.统计每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')

# 3.统计不止一个作者的图书
# 先按照图书分组,算出每本书的作者数量,再过滤出作者数量大于1的数据
res = models.Book.objects.annotate(author_num=Count('author')).filter(author_num__gt=1).values('name', 'author_num')

# 4.查询每个作者出版书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')

F および Q クエリ

Fクエリ

Fクエリは、テーブル内の特定のフィールドのデータ値を取得できます, 特にテーブル内の2つのフィールド間の比較操作に適しています. 文字型のデータを操作する場合, Fは文字列を直接つなぎ合わせることができず, 助けが必要ですConcat和Value.

from django.db.models import F

# 1.查询卖出数量大于库存数量的书籍
res = models.Book.objects.filter(sell_num__gt=F('inventory_num'))

# 将所有书籍的价格提升20元
res = models.Book.objects.update(price=F('price')+20)

F クエリでは、文字列操作に次の 2 つのメソッドConcatの助けが必要ですValue

# 将所有书的名称后面加上爆款两个字
from django.db.models.functions import Concat
from django.db.models import F, Value

models.Book.objects.update(name=Concat(F('name'),Value('爆款')))

Q クエリ

条件フィルタリングを使用する場合filter()、論理積演算が使用されます.複数のフィルタリング条件をOR関係andに変更する場合は、Qクエリを使用する必要があります. Qクエリで表現された関係表現された関係ornot|or~not

import django.db.models import Q

# 查询卖出数量大于100或者价格小于20的书籍
res = models.Book.objects.filter(~Q(sell_num__gt=100) | Q(price__lt=20))

さらに、Q クエリには別の高度な使用法があります。つまり、クエリ条件の左側も文字列に変換できます。

# 先产生一个Q的实例
q = Q()
# 修改q的连接条件的关系
q.connector = 'or'
q.children.append(('sell_num__gt',100))
q.children.append(('price__lt',200))
res = models.Book.objects.filter(q)
# filter的条件是Q实例化产生的对象,每个条件默认还是and关系,可以修改
print(res)

ジャンゴ開始トランザクション

MySQL にはデータのセキュリティを確保するためのトランザクション メカニズムがあり、django は MySQL に接続できるため、django は MySQL をサポートできます事务机制次のコードは、django でトランザクションを開始するためのものです。

from django.db import transaction

try:
    with transaction.atomic():		 # 在with代码快内书写的所有orm操作都属于同一个事务
        ...
except Exception as e:
    print(r)
...

おすすめ

転載: blog.csdn.net/nhb687095/article/details/130475199