webentwicklung-frage-antwort-db.com.de

Wie füge ich einem Modell mit neuen Django Migrationen ein neues Feld hinzu?

Ich benutze die contribute_to_class Methode, aber ich weiß nicht, wie ich das Feld in der Datenbank mit neuen Migrationen erstellen soll.

25
pedro_witoi

Um Ihre Frage zu beantworten, können Sie mit der in Django 1.7 eingeführten neuen Migration ein neues Feld zu einem Modell hinzufügen. Fügen Sie dieses Feld einfach zu Ihrem Modell hinzu und initialisieren Sie die Migrationen mit ./manage.py makemigrations und dann ./manage.py migrate und das neue Feld wird zu Ihrer Datenbank hinzugefügt.

Um Fehler für Ihre vorhandenen Modelle zu vermeiden, können Sie jedoch das --fake:

  1. Initialisieren Sie Migrationen für Ihre vorhandenen Modelle:

    ./manage.py makemigrations myapp
    
  2. Gefälschte Migrationen für vorhandene Modelle:

    ./manage.py migrate --fake myapp
    
  3. Fügen Sie das neue Feld zu myapp.models hinzu:

    from Django.db import models
    
    class MyModel(models.Model):
        ... #existing fields
        newfield = models.CharField(max_length=100) #new field
    
  4. Führen Sie makemigrations erneut aus (dies fügt eine neue Migrationsdatei in den Migrationsordner ein, die das neue Feld zu db hinzufügt):

    ./manage.py makemigrations myapp
    
  5. Führen Sie die Migration erneut aus:

    ./manage.py migrate myapp
    
62
Nima

Um dies zu tun und die Migrationsdatei in der Anwendung zu haben, in der ich das Feld tatsächlich hinzufüge, anstatt die Migration in der Anwendung zu haben, zu der das Modell gehört, musste ich meine eigene Migrationsbasisklasse schreiben.

Wenn Sie contribute_to_class In derselben Anwendung wie das ursprüngliche Modell verwenden, funktioniert die Antwort von @ nima einwandfrei, obwohl ich den Sinn der Verwendung von contribute_to_class Dann nicht sehe.

Hier ist der Code. Es ist Djangos Originalcode, der angepasst wurde, um ein Modell von self.migrated_app Anstelle von self.app_label Zu migrieren:

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

Mit dieser neuen Migrationsklasse in base.utils Würde eine handschriftliche Migration so aussehen. Sie können auch Django die Migration für Sie in die "falsche" Anwendung schreiben, die Datei verschieben und aktualisieren, um die benutzerdefinierte Migrationsklasse zu verwenden:

# -*- 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,
        ),
    ]

Benutzerdefinierte Migrationsklasse für 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
6
Emma

Sie können wie folgt erstellen:

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)

Weitere Informationen finden Sie unter " Django Model Field Injection ".

3
dhana