web-dev-qa-db-fra.com

Django Migrations Ajouter un champ avec la valeur par défaut en fonction du modèle

J'ai ajouté un nouveau champ non nullable à mon modèle Django et j'essaie d'utiliser les migrations pour déployer ce changement. Comment pourrais-je définir la valeur par défaut à utiliser pour que les modèles existants soient une fonction de ceux-ci modèles plutôt qu'une constante?

Par exemple, disons que j'avais précédemment un created_on champ et je viens d'ajouter un updated_on champ dont je veux définir initialement la valeur created_on. Comment procéder dans une migration?

Voici ce que j'essaie de commencer:

    migrations.AddField(
        model_name='series',
        name='updated_as',
        field=models.DateTimeField(default=????, auto_now=True),
        preserve_default=False,
    ),
40
Alex Rothberg

Je viens d'apprendre à le faire avec une seule migration!

lors de l'exécution de makemigrations Django devrait vous demander de définir une valeur unique par défaut. Définissez tout ce que vous pouvez ici pour le satisfaire, et vous vous retrouverez avec la migration AddField vous avez mentionné.

migrations.AddField(
    model_name='series',
    name='updated_as',
    field=models.DateTimeField(default=????, auto_now=True),
),

Modifiez cette opération en 3 opérations:

  1. initialement rendre le champ nullable, donc la colonne sera ajoutée.
  2. appeler une fonction pour remplir le champ selon les besoins.
  3. modifier le champ (avec AlterField) pour le rendre non nullable (comme ci-dessus, sans valeur par défaut).

donc vous vous retrouvez avec quelque chose comme.

migrations.AddField(
    model_name='series',
    name='updated_as',
    field=models.DateTimeField(null=True, auto_now=True),
),
migrations.RunPython(set_my_defaults, reverse_func),
migrations.AlterField(
    model_name='series',
    name='updated_as',
    field=models.DateTimeField(auto_now=True),
),

avec vos fonctions définies comme quelque chose comme:

def set_my_defaults(apps, schema_editor):
    Series = apps.get_model('myapp', 'Series')
    for series in Series.objects.all().iterator():
        series.updated_as = datetime.now() + timedelta(days=series.some_other_field)
        series.save()

def reverse_func(apps, schema_editor):
    pass  # code for reverting migration, if any

Sauf, vous savez, pas terrible; pensez à utiliser expressions F et/ou fonctions de base de données pour augmenter les performances de migration pour les grandes bases de données.

70
FraggaMuffin

Vous devez le faire en deux migrations. Tout d'abord, ajoutez votre champ, mais rendez nullable. Créez un fichier de migration comme d'habitude. Après cela, définissez votre champ sur non-nullable et exécutez à nouveau makemigrations, mais ne lancez pas encore la migration. Ouvrez la deuxième migration et définissez une fonction en haut:

def set_field_values(apps, schema_editor):
    # use apps.get_model("app_name", "model_name") and set the defualt values

puis, dans votre fichier de migration, il y a une liste d'opérations. Avant l'ajout d'une opération de modification de champ

RunPython(set_field_values)

et ça devrait le faire

19
Karolis Ryselis

Vous devez également définir un inverse pour votre fonction set_my_defaults(), au cas où vous annuleriez la migration à l'avenir.

def reverse_set_default(apps, schema_editor):
    pass

Dans ce cas, la fonction inverse ne doit rien faire, car vous supprimez le champ.

Et ajoutez-le à RunPython:

migrations.RunPython(set_my_defaults, reverse_set_default),
5
bnznamco