51

ベータモードのアプリケーションがあります。このアプリのモデルには、明示的なprimary_keyを持ついくつかのクラスがあります。結果として、Djangoはフィールドを使用し、IDを自動的に作成しません。

class Something(models.Model):
    name = models.CharField(max_length=64, primary_key=True)

それは悪い考えだったと思います(django adminでオブジェクトを保存するときのUnicodeエラーを参照)。戻ってモデルのすべてのクラスのIDを取得したいと思います。

class Something(models.Model):
    name = models.CharField(max_length=64, db_index=True)

モデルに変更を加え(すべてのprimary_key=Trueをdb_index=Trueに置き換えます)、データベースをsouthに移行したいと思います。

残念ながら、移行は次のメッセージで失敗します。 ValueError: You cannot add a null=False column without a default value.

この問題のさまざまな回避策を評価しています。助言がありますか?

ご協力いただきありがとうございます

4

10 に答える 10

94

あなたのモデルはおそらく間違っています。

正式な主キーは、常に代理キーにする必要があります。他には決してありません。【強い言葉。1980 年代からデータベース デザイナーとして活動。学んだ重要な教訓は次のとおりです。ユーザーが値を変更できないと母親の墓で誓ったとしても、すべてが変更可能であり、それは本当にプライマリとして使用できる自然キーです。それはプライマリではありません。サロゲートのみがプライマリになることができます。]

あなたは開心術を行っています。スキーマの移行を台無しにしないでください。スキーマを置き換えています。

  1. データを JSON ファイルにアンロードします。これには、Django 独自の内部 django-admin.py ツールを使用します。変更される各テーブルと、作成されるキーに依存するテーブルごとに 1 つのアンロード ファイルを作成する必要があります。個別のファイルを使用すると、これが少し簡単になります。

  2. 古いスキーマから変更しようとしているテーブルを削除します。

    これらのテーブルに依存するテーブルは、FK が変更されます。行をその場で更新するか、より簡単な方法で、これらの行を削除して再挿入することもできます。

  3. 新しいスキーマを作成します。これにより、変更中のテーブルのみが作成されます。

  4. 新しいキーを使用してデータを読み取り、再ロードするスクリプトを記述します。これらは短く、非常に似ています。各スクリプトはjson.load()、ソース ファイルからオブジェクトを読み取るために使用します。次に、作成された JSON タプルライン オブジェクトからスキーマ オブジェクトを作成します。その後、それらをデータベースに挿入できます。

    2 つのケースがあります。

    • PK の変更が変更されたテーブルが挿入され、新しい PK が取得されます。他のテーブルの FK も確実に変更されるように、これらを他のテーブルに「カスケード」する必要があります。

    • 変更された FK を持つテーブルは、外部テーブル内の行を見つけて、それらの FK 参照を更新する必要があります。

別。

  1. すべての古いテーブルの名前を変更します。

  2. 新しいスキーマ全体を作成します。

  3. すべてのデータを古いスキーマから新しいスキーマに移行する SQL を記述します。これは、キーを巧みに再割り当てする必要があります。

  4. 名前が変更された古いテーブルを削除します。

 

于 2010-01-13T11:11:43.053 に答える
11

主キーをsouthに変更するには、datamigrationでsouth.db.create_primary_keyコマンドを使用できます。カスタムCharFieldpkを標準のAutoFieldに変更するには、次のようにする必要があります。

1)モデルに新しいフィールドを作成します

class MyModel(Model):
    id = models.AutoField(null=True)

1.1)このモデルの他のモデルに外部キーがある場合は、これらのモデルにも新しい偽のfkフィールドを作成します(IntegerFieldを使用すると、変換されます)

class MyRelatedModel(Model):
    fake_fk = models.IntegerField(null=True)

2)自動南移行を作成して移行します。

./manage.py schemamigration --auto
./manage.py migrate

3)新しいデータ移行を作成する

./manage.py datamigration <your_appname> fill_id

データ移行では、これらの新しいidフィールドとfkフィールドに数字を入力します(列挙するだけです)

    for n, obj in enumerate(orm.MyModel.objects.all()):
        obj.id = n
        # update objects with foreign keys
        obj.myrelatedmodel_set.all().update(fake_fk = n)
        obj.save()

    db.delete_primary_key('my_app_mymodel')
    db.create_primary_key('my_app_mymodel', ['id'])

4)モデルで、新しいpkフィールドにprimary_key=Trueを設定します

id = models.AutoField(primary_key=True)

5)古い主キーフィールドを削除し(不要な場合)、自動移行を作成して移行します。

5.1)外部キーがある場合-古い外部キーフィールドも削除します(移行)

6)最後のステップ-fireignキーの関係を復元します。実際のfkフィールドを再度作成し、fake_fkフィールドを削除して、自動移行を作成します。ただし、移行しないでください(!)-作成した自動移行を変更する必要があります。新しいfkを作成してfake_fkを削除する代わりに、列の名前をfake_fkに変更します。

# in your models
class MyRelatedModel(Model):
    # delete fake_fk
    # fake_fk = models.InegerField(null=True)
    # create real fk
    mymodel = models.FoeignKey('MyModel', null=True)

# in migration
    def forwards(self, orm):
        # left this without change - create fk field
        db.add_column('my_app_myrelatedmodel', 'mymodel',
                  self.gf('django.db.models.fields.related.ForeignKey')(default=1, related_name='lots', to=orm['my_app.MyModel']),keep_default=False)

        # remove fk column and rename fake_fk
        db.delete_column('my_app_myrelatedmodel', 'mymodel_id')
        db.rename_column('my_app_myrelatedmodel', 'fake_fk', 'mymodel_id')

したがって、以前に入力されたfake_fkは、実際の関係データを含む列になり、上記のすべての手順の後で失われることはありません。

于 2012-04-20T17:42:38.003 に答える
6

現在、NOT NULL および UNIQUE の要件を破る pk 列を追加しているため、失敗しています。

スキーマの移行とデータの移行を分けて、移行をいくつかのステップに分割する必要があります。

  • デフォルト値を使用して、インデックス付けされているが主キーではない新しい列を追加します(ddl移行)
  • データの移行: 新しい列に正しい値を入力します (データの移行)
  • 新しい列の主キーをマークし、不要になった場合は以前の pk 列を削除します (ddl 移行)
于 2010-01-13T10:25:20.950 に答える
6

私は今日も同じ問題を抱えており、上記の回答に触発された解決策にたどり着きました。

私のモデルには「場所」テーブルがあります。「unique_id」という CharField があり、昨年、愚かにもそれを主キーにしました。もちろん、当時は予想されていたほどユニークではありませんでした。「Location」への外部キーを持つ「ScheduledMeasurement」モデルもあります。

ここで、その間違いを修正し、Location に通常の自動インクリメントの主キーを与えたいと思います。

実行した手順:

  1. CharField ScheduledMeasurement.temp_location_unique_id とモデル TempLocation を作成し、それらを作成するための移行を行います。TempLocation には、Location に必要な構造があります。

  2. 外部キーを使用してすべての temp_location_unique_id を設定し、Location から TempLocation にすべてのデータをコピーするデータ移行を作成します

  3. 移行で外部キーと Location テーブルを削除する

  4. Location モデルを希望どおりに再作成し、null=True で外部キーを再作成します。名前が「unique_id」から「location_code」に変更されました...

  5. TempLocation を使用して Location にデータを入力し、temp_location を使用して ScheduledMeasurement に外部キーを入力するデータ移行を作成します。

  6. 外部キーの temp_location、TempLocation、および null=True を削除します

そして、unique_id が一意であると仮定したすべてのコード (すべての objects.get(unique_id=...) のもの) を編集し、それ以外の場合は unique_id を使用しました...

于 2012-09-03T12:08:00.520 に答える