datos - ¿Cómo agregar un nuevo campo a un modelo con nuevas migraciones de Django?
django+base de datos (3)
Estoy utilizando el método contribute_to_class
clase pero no sé cómo crear el campo en la base de datos con nuevas migraciones.
Para poder hacer eso y tener el archivo de migración ubicado en la aplicación donde realmente agrego el campo en lugar de tener la migración ubicada dentro de la aplicación a la que pertenece el modelo, tuve que escribir mi propia clase base de Migración.
Si utiliza contribute_to_class
dentro de la misma aplicación que el modelo original, la respuesta de @nima funciona a la perfección, aunque no veo el punto de utilizar contrib_contro_clase.
Aquí está el código. Es el código original de Django adaptado para migrar un modelo de self.migrated_app
lugar de self.app_label
:
from django.db import migrations
class Migration(migrations.Migration):
migrated_app = None
def __init__(self, name, app_label):
super(Migration,self).__init__(name, app_label)
if self.migrated_app is None:
self.migrated_app = self.app_label
def mutate_state(self, project_state):
new_state = project_state.clone()
for operation in self.operations:
operation.state_forwards(self.migrated_app, new_state)
return new_state
def apply(self, project_state, schema_editor, collect_sql=False):
for operation in self.operations:
if collect_sql and not operation.reduces_to_sql:
schema_editor.collected_sql.append("--")
schema_editor.collected_sql.append("-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS SQL:")
schema_editor.collected_sql.append("-- %s" % operation.describe())
schema_editor.collected_sql.append("--")
continue
new_state = project_state.clone()
operation.state_forwards(self.migrated_app, new_state)
if not schema_editor.connection.features.can_rollback_ddl and operation.atomic:
with atomic(schema_editor.connection.alias):
operation.database_forwards(self.migrated_app, schema_editor, project_state, new_state)
else:
operation.database_forwards(self.migrated_app, schema_editor, project_state, new_state)
project_state = new_state
return project_state
def unapply(self, project_state, schema_editor, collect_sql=False):
to_run = []
for operation in self.operations:
if collect_sql and not operation.reduces_to_sql:
schema_editor.collected_sql.append("--")
schema_editor.collected_sql.append("-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE WRITTEN AS SQL:")
schema_editor.collected_sql.append("-- %s" % operation.describe())
schema_editor.collected_sql.append("--")
continue
if not operation.reversible:
raise Migration.IrreversibleError("Operation %s in %s is not reversible" % (operation, self))
new_state = project_state.clone()
operation.state_forwards(self.migrated_app, new_state)
to_run.append((operation, project_state, new_state))
project_state = new_state
to_run.reverse()
for operation, to_state, from_state in to_run:
if not schema_editor.connection.features.can_rollback_ddl and operation.atomic:
with atomic(schema_editor.connection.alias):
operation.database_backwards(self.migrated_app, schema_editor, from_state, to_state)
else:
operation.database_backwards(self.migrated_app, schema_editor, from_state, to_state)
return project_state
Con esta nueva clase de Migración ubicada en base.utils
una migración escrita a mano se vería así. También puede dejar que Django escriba la migración por usted dentro de la aplicación "incorrecta", mueva el archivo y actualícelo para usar la clase de migración personalizada:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from base.utils import Migration
import dynamicsites.fields
class Migration(Migration):
dependencies = [
(''sites'', ''0001_initial''),
(''base'', ''0001_initial''),
]
migrated_app = ''sites''
operations = [
migrations.AddField(
model_name=''site'',
name=''folder_name'',
field=dynamicsites.fields.FolderNameField(default='''', help_text=b"Folder name for this site''s files. The name may only consist of lowercase characters, numbers (0-9), and/or underscores", max_length=64, blank=True),
preserve_default=False,
),
migrations.AddField(
model_name=''site'',
name=''subdomains'',
field=dynamicsites.fields.SubdomainListField(default=(), help_text=b''Comma separated list of subdomains this site supports. Leave blank to support all subdomains'', blank=True),
preserve_default=False,
),
]
Clase de migración personalizada para Django 1.8
from django.db import migrations
class Migration(migrations.Migration):
migrated_app = None
def __init__(self, name, app_label):
super(Migration,self).__init__(name, app_label)
if self.migrated_app is None:
self.migrated_app = self.app_label
def __eq__(self, other):
if not isinstance(other, Migration):
if not isinstance(other, migrations.Migration):
return False
return (self.name == other.name) and (self.migrated_app == other.app_label)
return (self.name == other.name) and (self.migrated_app == other.migrated_app)
def __hash__(self):
return hash("%s.%s" % (self.app_label, self.name))
def mutate_state(self, project_state, preserve=True):
new_state = project_state
if preserve:
new_state = project_state.clone()
for operation in self.operations:
operation.state_forwards(self.migrated_app, new_state)
return new_state
def apply(self, project_state, schema_editor, collect_sql=False):
for operation in self.operations:
if collect_sql and not operation.reduces_to_sql:
schema_editor.collected_sql.append("--")
schema_editor.collected_sql.append("-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE "
"WRITTEN AS SQL:")
schema_editor.collected_sql.append("-- %s" % operation.describe())
schema_editor.collected_sql.append("--")
continue
old_state = project_state.clone()
operation.state_forwards(self.migrated_app, project_state)
if not schema_editor.connection.features.can_rollback_ddl and operation.atomic:
with atomic(schema_editor.connection.alias):
operation.database_forwards(self.migrated_app, schema_editor, old_state, project_state)
else:
operation.database_forwards(self.migrated_app, schema_editor, old_state, project_state)
return project_state
def unapply(self, project_state, schema_editor, collect_sql=False):
to_run = []
new_state = project_state
for operation in self.operations:
if not operation.reversible:
raise Migration.IrreversibleError("Operation %s in %s is not reversible" % (operation, self))
new_state = new_state.clone()
old_state = new_state.clone()
operation.state_forwards(self.migrated_app, new_state)
to_run.insert(0, (operation, old_state, new_state))
for operation, to_state, from_state in to_run:
if collect_sql:
if not operation.reduces_to_sql:
schema_editor.collected_sql.append("--")
schema_editor.collected_sql.append("-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE "
"WRITTEN AS SQL:")
schema_editor.collected_sql.append("-- %s" % operation.describe())
schema_editor.collected_sql.append("--")
continue
if not schema_editor.connection.features.can_rollback_ddl and operation.atomic:
with atomic(schema_editor.connection.alias):
operation.database_backwards(self.migrated_app, schema_editor, from_state, to_state)
else:
operation.database_backwards(self.migrated_app, schema_editor, from_state, to_state)
return project_state
Para responder a su pregunta, con la nueva migración introducida en Django 1.7 , para agregar un nuevo campo a un modelo, simplemente puede agregar ese campo a su modelo e iniciar migraciones con ./manage.py makemigrations
y luego ejecutar ./manage.py migrate
y el nuevo campo se agregará a tu base de datos.
Sin embargo, para evitar lidiar con los errores de sus modelos existentes, puede usar --fake
:
Inicialice las migraciones para sus modelos existentes:
./manage.py makemigrations myapp
Migraciones falsas para modelos existentes:
./manage.py migrate --fake myapp
Agregue el nuevo campo a myapp.models:
from django.db import models class MyModel(models.Model): ... #existing fields newfield = models.CharField(max_length=100) #new field
Ejecute makemigrations de nuevo (esto agregará un nuevo archivo de migración en la carpeta de migraciones que agregará el campo nuevo a db):
./manage.py makemigrations myapp
Ejecutar migrar de nuevo:
./manage.py migrate myapp
Puedes crear así:
from django.db.models import CharField
from django.db.models.signals import class_prepared
def add_field(sender, **kwargs):
"""
class_prepared signal handler that checks for the model named
MyModel as the sender, and adds a CharField
to it.
"""
if sender.__name__ == "MyModel":
field = CharField("New field", max_length=100)
field.contribute_to_class(sender, "new_field")
class_prepared.connect(add_field)
Consulte " Inyección de campo del modelo Django " para obtener más información.