【保存の極意】Django admin で関連オブジェクトを柔軟に扱う:save_related() とその代替方法
admin.ModelAdmin.save_related()
は、Django 管理画面において、モデルインスタンスを保存した際に、関連するオブジェクトも自動的に保存するメソッドです。これは、多くの場合、インラインフォームで関連オブジェクトを編集する場合に使用されます。
役割
save_related()
メソッドは、以下の役割を果たします。
- 関連オブジェクトの取得
モデルインスタンスに関連するオブジェクトをすべて取得します。 - 関連オブジェクトの保存
取得した関連オブジェクトをそれぞれ保存します。 - M2M フィールドの処理
モデルインスタンスと M2M 関係にあるオブジェクトの関連付けを更新します。
使用方法
save_related()
メソッドは、ModelAdmin
クラスのメソッドとして定義されます。引数として、request
オブジェクトと、保存対象のモデルインスタンスが渡されます。
def save_related(self, request, form, formset, instance):
# 関連オブジェクトを取得
related_objects = self.get_related_objects(request, instance)
# 関連オブジェクトを保存
for obj in related_objects:
obj.save()
# M2M フィールドの処理
self.save_for_many_to_many(request, form, instance)
カスタマイズ
save_related()
メソッドは、必要に応じてカスタマイズすることができます。例えば、以下のことができます。
- M2M フィールドの処理を独自に行う
- 関連オブジェクトを保存する前に処理を行う
- 関連オブジェクトをフィルタリングする
例
以下の例では、Book
モデルと Author
モデルが M2M 関係にある場合、Book
モデルを保存した際に、関連する Author
オブジェクトも自動的に保存するように save_related()
メソッドをカスタマイズしています。
class BookAdmin(admin.ModelAdmin):
def save_related(self, request, form, formset, instance):
# 関連オブジェクトを取得
related_objects = self.get_related_objects(request, instance)
# 関連オブジェクトを保存
for obj in related_objects:
if obj.is_published:
obj.save()
# M2M フィールドの処理
self.save_for_many_to_many(request, form, instance)
この例では、is_published
属性が True
である Author
オブジェクトのみを保存しています。
from django.contrib import admin
from .models import Book, Author
class BookAdmin(admin.ModelAdmin):
inlines = [AuthorInline]
def save_related(self, request, form, formsets, change):
super(BookAdmin, self).save_related(request, form, formsets, change)
# Get the related Author objects
related_authors = form.instance.authors.all()
# Save the related Author objects
for author in related_authors:
if author.is_published:
author.save()
class AuthorInline(admin.TabularInline):
model = Author
extra = 0
In this example, the BookAdmin
class has an inlines
attribute that specifies the AuthorInline
inline form. This inline form allows users to add and edit related Author
objects when they are editing a Book
object.
The save_related()
method is overridden in the BookAdmin
class to save the related Author
objects. The method first gets all of the related Author
objects for the current Book
instance. Then, it iterates over the related Author
objects and saves each one that is published.
This code ensures that only published Author
objects are saved when a Book
object is saved.
Here is a breakdown of the code:
- The
extra = 0
attribute specifies that there should be no extra empty rows in the inline form. - The
model = Author
attribute specifies theAuthor
model. - The
class AuthorInline(admin.TabularInline)
statement defines aTabularInline
class for theAuthor
model. - The
author.save()
statement saves the currentAuthor
object. - The
if author.is_published:
statement checks if the currentAuthor
object is published. - The
for author in related_authors:
loop iterates over the relatedAuthor
objects. - The
related_authors = form.instance.authors.all()
statement gets all of the relatedAuthor
objects for the currentBook
instance. - The
super(BookAdmin, self).save_related(request, form, formsets, change)
statement calls the originalsave_related()
method. - The
def save_related(self, request, form, formsets, change)
method overrides thesave_related()
method from theModelAdmin
class. - The
inlines = [AuthorInline]
attribute specifies theAuthorInline
inline form. - The
class BookAdmin(admin.ModelAdmin)
statement defines aModelAdmin
class for theBook
model. - The
from .models import Book, Author
statement imports theBook
andAuthor
models from the current app. - The
from django.contrib import admin
statement imports theadmin
module, which contains theModelAdmin
class.
代替方法
save_related()
の代替方法として、以下の方法が考えられます。
手動で関連オブジェクトを保存する
最も単純な方法は、モデルインスタンスを保存した後に、関連オブジェクトを個別に保存することです。
def save_model(self, request, form, instance, change):
instance.save()
# 関連オブジェクトを保存
for author in instance.authors.all():
author.save()
save_m2m() メソッドを使用する
M2M 関係の場合、save_m2m()
メソッドを使用することができます。このメソッドは、M2M 関係にあるオブジェクトの関連付けを更新します。
def save_model(self, request, form, instance, change):
instance.save()
# M2M 関係の関連付けを更新
self.save_for_many_to_many(request, form, instance)
シグナルを使用する
モデルインスタンスが保存された後に、シグナルを送信し、関連オブジェクトを保存する処理を実行することができます。
from django.dispatch import receiver
@receiver(models.post_save, sender=Book)
def save_related_authors(sender, instance, created, **kwargs):
if created:
for author in instance.authors.all():
author.save()
カスタムビューを使用する
def save_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
book = form.save()
# 関連オブジェクトを保存
for author in request.POST.getlist('authors'):
author = Author.objects.get(pk=author)
book.authors.add(author)
return redirect('/books/')
最適な方法を選択する
どの方法が最適かは、状況によって異なります。
- カスタムロジックが必要な場合は、カスタムビュー を使用するのが良いでしょう。
- 関連オブジェクトの保存処理が複雑な場合は、シグナル を使用すると柔軟に処理することができます。
- M2M 関係の場合は、
save_m2m()
メソッド を使用するのが効率的です。 - シンプルな場合は、手動で関連オブジェクトを保存する 方法がおすすめです。