2010-02-08 6 views
30

J'ai une application Django dans laquelle je veux changer un champ d'un ForeignKey à un ManyToManyField. Je veux préserver mes anciennes données. Quel est le processus le plus simple/le meilleur à suivre pour cela? Si c'est important, j'utilise sqlite3 comme back-end de ma base de données.Migration de données Django lors de la modification d'un champ à ManyToMany

Si mon résumé du problème n'est pas clair, voici un exemple. Dire que j'ai deux modèles:

class Author(models.Model): 
    author = models.CharField(max_length=100) 

class Book(models.Model): 
    author = models.ForeignKey(Author) 
    title = models.CharField(max_length=100) 

Dites que j'ai beaucoup de données dans ma base de données. Maintenant, je veux changer le modèle du livre comme suit:

class Book(models.Model): 
    author = models.ManyToManyField(Author) 
    title = models.CharField(max_length=100) 

Je ne veux pas « perdre » toutes mes données antérieures.

Quel est le meilleur/le plus simple pour y parvenir?

Ken

+1

Si cela n'est pas déjà évident, assurez-vous de sauvegarder vos données avant d'effectuer des migrations. heureusement, copier sqlite est aussi facile qu'une commande 'cp' –

+0

Découvrez [sud] (http://south.aeracode.org/). – Zach

+0

Plus spécifiquement, consultez la section "migrations de données" du tutoriel: http://south.aeracode.org/wiki/Tutorial3 C'est une bonne habitude d'utiliser South pour toutes vos migrations de toute façon. –

Répondre

40

je me rends compte de cette question est vieux et au moment où la meilleure option pour les données utilisait les migrations Sud. Maintenant Django a sa propre commande migrate, et le processus est légèrement différent.

J'ai ajouté ces modèles à une application appelée books - ajuster en conséquence si ce n'est pas votre cas.

D'abord, ajoutez le champ à Book et un related_name au moins un, ou les deux (ou ils entrent en conflit):

class Book(models.Model): 
    author = models.ForeignKey(Author, related_name='book') 
    authors = models.ManyToManyField(Author, related_name='books') 
    title = models.CharField(max_length=100) 

Generate la migration:

$ ./manage.py makemigrations 
Migrations for 'books': 
    0002_auto_20151222_1457.py: 
    - Add field authors to book 
    - Alter field author on book 

maintenant , créez une migration vide pour conserver la migration des données proprement dites:

./manage.py makemigrations books --empty 
    Migrations for 'books': 
0003_auto_20151222_1459.py: 

Et ajoutez le contenu suivant à celui-ci. Pour comprendre exactement comment cela fonctionne, consultez la documentation sur Data Migrations. Veillez à ne pas remplacer la dépendance de migration.

# -*- coding: utf-8 -*- 
from __future__ import unicode_literals 

from django.db import models, migrations 


def make_many_authors(apps, schema_editor): 
    """ 
     Adds the Author object in Book.author to the 
     many-to-many relationship in Book.authors 
    """ 
    Book = apps.get_model('books', 'Book') 

    for book in Book.objects.all(): 
     book.authors.add(book.author) 


class Migration(migrations.Migration): 

    dependencies = [ 
     ('books', '0002_auto_20151222_1457'), 
    ] 

    operations = [ 
     migrations.RunPython(make_many_authors), 
    ] 

maintenant supprimer le champ author du modèle - il devrait ressembler à ceci:

class Book(models.Model): 
    authors = models.ManyToManyField(Author, related_name='books') 
    title = models.CharField(max_length=100) 

Créer une nouvelle migration pour cela, et de les exécuter tous:

$ ./manage.py makemigrations 
Migrations for 'books': 
    0004_remove_book_author.py: 
    - Remove field author from book 

$ ./manage.py migrate 
Operations to perform: 
    Synchronize unmigrated apps: messages, staticfiles 
    Apply all migrations: admin, auth, sessions, books, contenttypes 
Synchronizing apps without migrations: 
    Creating tables... 
    Running deferred SQL... 
    Installing custom SQL... 
Running migrations: 
    Rendering model states... DONE 
    Applying books.0002_auto_20151222_1457... OK 
    Applying books.0003_auto_20151222_1459... OK 
    Applying books.0004_remove_book_author... OK 

Et c'est tout. Les auteurs précédemment disponibles à book.author devraient maintenant être dans le jeu de requête que vous obtenez de book.authors.all().

+1

Très bien !!! C'est une façon intéressante ☺ –

+2

Bonne réponse, merci! – Ward

+0

Vous n'avez pas 'book.save()' dans 'make_many_authors'? –

2

Probablement le meilleur et le plus facile chose que vous devriez faire serait:

Créer le grand nombre de champ beaucoup avec un nom différent disent

authors = models.ManyToManyField(Author) 

écrire une petite fonction pour convertir les valeurs ForeignKey aux valeurs M2M: Une fois exécuté, vous pouvez supprimer le champ d'auteur de la table et réexécuter la migration.

+0

dans django 1.10 je reçois un 'attribut AttributeError: 'ManyRelatedManager' n'a aucun attribut 'append'' j'ai supprimé .id et avait' book.authors = li' et cela a fonctionné. Je crois que vous pouvez aussi faire 'book.authors.add (li)' et cela devrait résoudre le problème. – hachacha

+0

Vous pouvez ajouter et enregistrer dans les modèles comme vous le souhaitez. La question et la réponse s'occupent plus spécifiquement des migrations. – sprksh

Questions connexes