Djangoモデルの1つのフィールドのタイプをCharField
からForeignKey
に変更する必要があります。フィールドにはすでにデータが入力されているので、これを行うための最良または適切な方法は何かと思っていました。フィールドタイプを更新して移行することはできますか?それとも、知っておくべき「落とし穴」はありますか? N.B:南部ではなく、バニラDjango管理操作(makemigrations
およびmigrate
)のみを使用します。
これは、多段階の移行を実行する場合によく発生します。これに対する私の推奨は、次のようになります。
まず、これがdiscography
というアプリケーション内の初期モデルであると仮定します。
from Django.db import models
class Album(models.Model):
name = models.CharField(max_length=255)
artist = models.CharField(max_length=255)
ここで、代わりにアーティストにForeignKeyを使用することに気付きました。さて、述べたように、これはこれのための単純なプロセスではありません。これはいくつかのステップで実行する必要があります。
ステップ1、ForeignKeyの新しいフィールドを追加します。必ずnullとしてマークしてください。
from Django.db import models
class Album(models.Model):
name = models.CharField(max_length=255)
artist = models.CharField(max_length=255)
artist_link = models.ForeignKey('Artist', null=True)
class Artist(models.Model):
name = models.CharField(max_length=255)
...この変更の移行を作成します。
./manage.py makemigrations discography
ステップ2、新しいフィールドに入力します。これを行うには、空のマイグレーションを作成する必要があります。
./manage.py makemigrations --empty --name transfer_artists discography
この空の移行が完了したら、レコードをリンクするために、単一のRunPython
操作をそれに追加する必要があります。この場合、次のようになります。
def link_artists(apps, schema_editor):
Album = apps.get_model('discography', 'Album')
Artist = apps.get_model('discography', 'Artist')
for album in Album.objects.all():
artist, created = Artist.objects.get_or_create(name=album.artist)
album.artist_link = artist
album.save()
データが新しいフィールドに転送されたので、新しいフィールドをすべてに使用して、実際に作業を終えてすべてをそのままにすることができます。または、少しクリーンアップする場合は、さらに2つのマイグレーションを作成します。
最初の移行では、元のフィールドartist
を削除する必要があります。 2回目の移行では、新しいフィールドの名前をartist_link
からartist
に変更します。
Djangoが操作を正しく認識できるようにするために、これは複数のステップで行われます。これを処理するために手動で移行を作成することもできますが、理解するためにそれを残します。
それはジョーイの素晴らしい答えの続きです。 新しいフィールドの名前を元の名前に変更するには?
フィールドにデータがある場合、それはおそらくプロジェクトの他の場所で使用していることを意味します。したがって、この解決策では別の名前のフィールドが残され、新しいフィールドを使用するようにプロジェクトをリファクタリングするか、古いフィールドを削除して、新しい名前を変更します。
このプロセスはコードのリファクタリングを妨げるものではないことに注意してください。 CHOICESでCharFieldを使用している場合、たとえばget_filename_display()を使用してコンテンツにアクセスしていました。
移行を行うためにフィールドを削除しようとした場合、他のフィールドの名前を変更してからもう一度移行すると、Djangoで問題が発生します。これは、事業。
Joeyが説明したように空のマイグレーションを作成し、これを操作に入れます。
operations = [
migrations.RemoveField(
model_name='app_name',
name='old_field_name',
),
migrations.RenameField(
model_name='app_name',
old_name='old_field_name_link',
new_name='old_field_name',
),
]
次に、migrateを実行すると、データベースに変更が加えられますが、モデルには明らかにありません。今度は、古いフィールドを削除し、新しいForeignKeyフィールドの名前を元の名前に変更します。
これを行うことは特にハックだとは思わないが、それでも、何をいじっているのかを完全に理解している場合にのみ、この種のことを行う。
Joeyの回答に加えて、Django 2.2.11。
Company
モデルとEmployee
モデルで構成される、私のユースケースのモデルを以下に示します。 designation
を外部キーフィールドに変換する必要があります。アプリ名はcore
です
class Company(CommonFields):
name = models.CharField(max_length=255, blank=True, null=True
class Employee(CommonFields):
company = models.ForeignKey("Company", on_delete=models.CASCADE, blank=True, null=True)
designation = models.CharField(max_length=100, blank=True, null=True)
ステップ1
外部キーを作成するdesignation_link
をEmployeeに追加し、null = Trueとしてマークします
class Designation(CommonFields):
name = models.CharField(max_length=255)
company = models.ForeignKey("Company", on_delete=models.CASCADE, blank=True, null=True)
class Employee(CommonFields):
company = models.ForeignKey("Company", on_delete=models.CASCADE, blank=True, null=True)
designation = models.CharField(max_length=100, blank=True, null=True)
designation_link = models.ForeignKey("Designation", on_delete=models.CASCADE, blank=True, null=True)
ステップ2
空の移行を作成します。次のコマンドを使用します。
python app_code/manage.py makemigrations --empty --name transfer_designations core
これにより、migrations
ディレクトリに次のファイルが作成されます。
# Generated by Django 2.2.11 on 2020-04-02 05:56
from Django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20200402_1119'),
]
operations = [
]
ステップ
すべてのEmployees
をループし、Designation
を作成してEmployee
にリンクする関数を空のマイグレーションに入力します。
私の使用例では、各Designation
もCompany
にリンクされています。つまり、Designation
には「マネージャー」の2つの行が含まれる可能性があります。1つは会社Aの行で、もう1つは会社Bの行です。
最終的な移行は次のようになります。
# core/migrations/0007_transfer_designations.py
# Generated by Django 2.2.11 on 2020-04-02 05:56
from Django.db import migrations
def link_designation(apps, schema_editor):
Employee = apps.get_model('core', 'Employee')
Designation = apps.get_model('core', 'Designation')
for emp in Employee.objects.all():
if(emp.designation is not None and emp.company is not None):
desig, created = Designation.objects.get_or_create(name=emp.designation, company=emp.company)
emp.designation_link = desig
emp.save()
class Migration(migrations.Migration):
dependencies = [
('core', '0006_auto_20200402_1119'),
]
operations = [
migrations.RunPython(link_designation),
]
ステップ4
最後に、次を使用してこの移行を実行します。
python app_code/manage.py migrate core 0007