Djangoのdjango-import-exportとForeignKeyフィールド

Django

はじめに

Djangoのdjango-import-exportを使って、管理画面でデータをインポートする方法を調べました。
今回インポートするモデルにはForeignKeyフィールドが含まれるので、デフォルトでは主キーで指定する必要があります。
django-import-exportには、一意な値で代替する機能があるので利用しました。

構成

- Debian bookworm
- Python 3.10.13
- pipenv 2023.10.3
- Django 5.0.3
- django-import-export 3.3.7

django-import-exportのインストール

Pythonの仮想環境にpipenvを使っているので、pipenvコマンドでインストールします。

pipenv install django-import-export

settings.py

インポート、エクスポートともにCSV、またはExcelファイルしか扱わないので設定します。

 INSTALLED_APPS = [
    ...(省略)...
    'import_export',
 ]

 IMPORT_EXPORT_AVAILABLE_IMPORT_FORMATS = [
    'xlsx', 'csv',
 ]

 IMPORT_EXPORT_AVAILABLE_EXPORT_FORMATS = [
    'xlsx', 'csv',
 ]

models.py

モデル定義は下記とします。

import uuid
from django.db import models

# カテゴリ
class Category(models.Model):
   id   = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
   name = models.CharField(max_length=100)

   def __str__(self):
      return self.name

# フルーツ
class Fruits(models.Model):
   id             = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
   name           = models.CharField(max_length=256)
   category_field = models.ForeignKey(Category, on_delete=models.PROTECT, related_name='fruits')

   def __str__(self):
      return self.name

admin.py

ForeignKeyフィールドであるcategory_fieldの設定などを行います。
・column_nameは、インポートするExcelファイル等に追加する列の項目名。
・attributeは、ForeignKeyのフィールド名。
・widgetは、代替する値のモデル名とフィールド名。

from django.contrib import admin
from import_export import resources, fields
from import_export.widgets import ForeignKeyWidget

from .models import Fruits
from .models import Category

# django-import-exportの設定
class FruitsResource(resources.ModelResource):
   class Meta:
      model = Fruits

      # インポート時にデータベースの既存データと同一の場合にスキップする。
      skip_unchanged = True
      # スキップしたレコードの詳細を表示する。
      report_skipped = True
      # 毎回データベースへのクエリを発行しないため、インポート処理速度の向上が期待できる。
      instance_loader_class = CachedInstanceLoader

      # インポート、エクスポートの対象を指定する。
      fields = ('id', 'name', 'category_field')
      # エクスポートしたときの項目の並び順を指定する。(今回はfieldsと同じ。)
      export_order = fields

   # ForeignKeyフィールド(category_field)の設定
   category_field  = fields.Field(
      column_name = 'category_name',
      attribute   = 'category_field',
      widget      = ForeignKeyWidget(Category, 'name')
   )

# 管理サイトに表示
class ProductAdmin(admin.ModelAdmin):
   list_display = ('name', 'category', 'price')

# 管理サイトに表示
class CategoryAdmin(admin.ModelAdmin):
   list_display = ('name',)

項目のみのExcelファイルのダウンロード

テストサーバを起動します。

python manage.py runserver 192.168.12.34:8080

何もデータを登録していない状態で、管理画面からFruitsモデルのデータをExcel形式でエクスポートして、export_orderで指定した並び順かどうか確認します。
また、このファイルにcategory_nameの列を追加して、インポートするデータを作成します。

category_fieldの値は、category_name(Categoryのnameフィールド)で代替するので、インポートする前にCategoryモデルにデータを登録します。

データは例えば以下のようになります。

Categoryモデルには「果物」と「野菜」を登録しておきます。
idの列は、空にしておけば自動的にUUIDが割り当てられます。

id,name,category_name,
,りんご,果物,
,バナナ,果物,
,トマト,野菜,

テスト

管理画面から作成したデータをインポートして、category_nameに記述したCategoryのnameフィールドの値で登録できているかを確認します。

Comments