如何使用新的Django迁移向模型添加新字段?

ped*_*toi 26 django django-models django-1.7 django-migrations

我正在使用该contribute_to_class方法,但我不知道如何使用新的迁移在数据库中创建字段.

Nim*_*ima 63

为了回答您的问题,使用Django 1.7中引入的新迁移,为了向模型添加新字段,您只需将该字段添加到模型中并使用./manage.py makemigrations然后运行初始化迁移,./manage.py migrate新字段将添加到您的数据库中.

但是,为了避免处理现有模型的错误,您可以使用--fake:

  1. 初始化现有模型的迁移:

    ./manage.py makemigrations myapp
    
    Run Code Online (Sandbox Code Playgroud)
  2. 现有模型的虚假迁移:

    ./manage.py migrate --fake myapp
    
    Run Code Online (Sandbox Code Playgroud)
  3. 将新字段添加到myapp.models:

    from django.db import models
    
    class MyModel(models.Model):
        ... #existing fields
        newfield = models.CharField(max_length=100) #new field
    
    Run Code Online (Sandbox Code Playgroud)
  4. 再次运行makemigrations(这将在迁移文件夹中添加一个新的迁移文件,将新字段添加到db):

    ./manage.py makemigrations myapp
    
    Run Code Online (Sandbox Code Playgroud)
  5. 再次运行迁移:

    ./manage.py migrate myapp
    
    Run Code Online (Sandbox Code Playgroud)


Emm*_*mma 6

为了能够做到这一点,让迁移文件位于我实际添加字段的应用程序中,而不是让迁移位于模型所属的应用程序中,我必须编写自己的Migration基类.

如果你contribute_to_class在与原始模型相同的应用程序内部使用,@ nima的答案是完美的,尽管我没有看到使用它的意义contribute_to_class.

这是代码.这是Django的适应迁移从模型的原代码self.migrated_app,而不是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
Run Code Online (Sandbox Code Playgroud)

这个新的Migration类位于base.utils手写迁移中,如下所示.你也可以让Django在"错误的"应用程序中为你编写迁移,移动文件并更新它以使用自定义的Migration类:

# -*- 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,
        ),
    ]
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


dha*_*ana 4

你可以这样创建:

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)
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅“ Django 模型字段注入”。