मैं एक django ऐप से बाहर और एक नए मॉडल में माइग्रेट कैसे करूं?


126

मेरे पास एक django ऐप है जिसमें चार मॉडल हैं। मुझे अब एहसास हुआ कि इनमें से एक मॉडल एक अलग ऐप में होना चाहिए। मेरे पास माइग्रेशन के लिए दक्षिण स्थापित है, लेकिन मुझे नहीं लगता कि यह कुछ ऐसा है जो इसे स्वचालित रूप से संभाल सकता है। मैं पुराने ऐप में से किसी एक मॉडल को नए में कैसे स्थानांतरित कर सकता हूं?

इसके अलावा, ध्यान रखें कि मुझे एक दोहराने योग्य प्रक्रिया होने की आवश्यकता है, ताकि मैं उत्पादन प्रणाली और इस तरह से माइग्रेट कर सकूं।


6
Django के लिए 1.7 और इसके बाद के संस्करण stackoverflow.com/questions/25648393/…
रिक वेस्टेरा

जवाबों:


184

दक्षिण का उपयोग करके पलायन कैसे करें।

कहते हैं कि हमें दो ऐप मिले हैं: सामान्य और विशिष्ट:

myproject/
|-- common
|   |-- migrations
|   |   |-- 0001_initial.py
|   |   `-- 0002_create_cat.py
|   `-- models.py
`-- specific
    |-- migrations
    |   |-- 0001_initial.py
    |   `-- 0002_create_dog.py
    `-- models.py

अब हम मॉडल को सामान्य ऐप में स्थानांतरित करना चाहते हैं। पहले स्रोत कोड में परिवर्तन करें और फिर चलाएं:

$ python manage.py schemamigration specific create_cat --auto
 + Added model 'specific.cat'
$ python manage.py schemamigration common drop_cat --auto
 - Deleted model 'common.cat'

myproject/
|-- common
|   |-- migrations
|   |   |-- 0001_initial.py
|   |   |-- 0002_create_cat.py
|   |   `-- 0003_drop_cat.py
|   `-- models.py
`-- specific
    |-- migrations
    |   |-- 0001_initial.py
    |   |-- 0002_create_dog.py
    |   `-- 0003_create_cat.py
    `-- models.py

अब हमें दोनों माइग्रेशन फ़ाइलों को संपादित करने की आवश्यकता है:

#0003_create_cat: replace existing forward and backward code
#to use just one sentence:

def forwards(self, orm):
    db.rename_table('common_cat', 'specific_cat') 

    if not db.dry_run:
        # For permissions to work properly after migrating
        orm['contenttypes.contenttype'].objects.filter(
            app_label='common',
            model='cat',
        ).update(app_label='specific')

def backwards(self, orm):
    db.rename_table('specific_cat', 'common_cat')

    if not db.dry_run:
        # For permissions to work properly after migrating
        orm['contenttypes.contenttype'].objects.filter(
            app_label='specific',
            model='cat',
        ).update(app_label='common')

#0003_drop_cat:replace existing forward and backward code
#to use just one sentence; add dependency:

depends_on = (
    ('specific', '0003_create_cat'),
)
def forwards(self, orm):
    pass
def backwards(self, orm):
    pass

अब दोनों ऐप माइग्रेशन परिवर्तन के बारे में जानते हैं और जीवन थोड़ा कम चूसता है :-) माइग्रेशन के बीच इस संबंध को स्थापित करना सफलता की कुंजी है। अब अगर तुम करते हो:

python manage.py migrate common
 > specific: 0003_create_cat
 > common: 0003_drop_cat

दोनों प्रवास करेंगे, और

python manage.py migrate specific 0002_create_dog
 < common: 0003_drop_cat
 < specific: 0003_create_cat

चीजों को नीचे ले जाएगा।

ध्यान दें कि स्कीमा के उन्नयन के लिए मैंने सामान्य ऐप का उपयोग किया और डाउनग्रेडिंग के लिए, मैंने विशिष्ट ऐप का उपयोग किया। ऐसा इसलिए क्योंकि यहां निर्भरता कैसे काम करती है।


1
वाह धन्यवाद। मैंने यह सवाल पूछने के बाद से अपने दम पर दक्षिण की शिक्षा ली, लेकिन मुझे यकीन है कि इससे दूसरों को बहुत मदद मिलेगी।
बजे

11
आपको django_content_type तालिका में डेटा का माइग्रेशन करने की भी आवश्यकता हो सकती है।
स्पूकीलुक्ये

1
वास्तव में महान गाइड @Potr। मैं उत्सुक हूं, क्या orm['contenttypes.contenttype'].objects.filter पीछे के हिस्से में भी लाइन नहीं होनी चाहिए 0003_create_cat? इसके अलावा मैं एक टिप साझा करना चाहते हैं। यदि आपके पास अनुक्रमित हैं, तो उन्हें भी संशोधित करने की आवश्यकता होगी। मेरे मामले में वे अद्वितीय अनुक्रमणिकाएँ थीं, इसलिए मेरा फॉरवर्ड थिएस जैसा दिखता है:db.delete_unique('common_cat', ['col1']) db.rename_table('common_cat', 'specific_cat') db.delete_unique('specific_cat', ['col1'])
ब्रैड पिचर

2
एक्सेस करने के लिए orm['contenttypes.contenttype'], आपको --freeze contenttypesअपने schemamigrationकमांड में विकल्प को भी जोड़ना होगा ।
गैरी

1
मेरे मामले में (Django 1.5.7 और दक्षिण 1.0) .. मुझे python manage.py schemamigration specific create_cat --auto --freeze commonसामान्य ऐप से कैट मॉडल तक पहुंचने के लिए टाइप करना था ।
जियोम

35

पोटर काज़चूर के उत्तर पर निर्माण करने के लिए, विदेशीकेयर को शामिल करने वाली परिस्थितियाँ अधिक जटिल हैं और इसे थोड़ा अलग तरीके से संभाला जाना चाहिए।

(निम्न उदाहरण वर्तमान उत्तर में उल्लिखित commonऔर specificऐप्स पर बनता है)।

# common/models.py

class Cat(models.Model):
    # ...

class Toy(models.Model):
    belongs_to = models.ForeignKey(Cat)
    # ...

फिर बदल जाएगा

# common/models.py

from specific.models import Cat

class Toy(models.Model):
    belongs_to = models.ForeignKey(Cat)
    # ...

# specific/models.py

class Cat(models.Model):
    # ...

चल रहा है

./manage.py schemamigration common --auto
./manage.py schemamigration specific --auto # or --initial

निम्नलिखित माइग्रेशन उत्पन्न कर सकते हैं (मैं जानबूझकर Django ContentType परिवर्तनों की अनदेखी कर रहा हूं - पहले से संदर्भित उत्तर देखें कि इसे कैसे संभालना है):

# common/migrations/0009_auto__del_cat.py

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.delete_table('common_cat')
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['specific.Cat']))

    def backwards(self, orm):
        db.create_table('common_cat', (
            # ...
        ))
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['common.Cat']))

# specific/migrations/0004_auto__add_cat.py

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.create_table('specific_cat', (
            # ...
        ))

    def backwards(self, orm):
        db.delete_table('specific_cat')

जैसा कि आप देख सकते हैं, नई तालिका को संदर्भित करने के लिए FK को बदलना होगा। हम इतना है कि हम जिस क्रम में माइग्रेशन लागू किया जाएगा (और इस प्रकार उस तालिका इससे पहले कि हम यह करने के लिए एक FK जोड़ने की कोशिश मौजूद रहेंगे) पता निर्भरता जोड़ने की जरूरत है, लेकिन हम यह भी सुनिश्चित करना भी है क्योंकि पीछे की ओर रोलिंग काम करता है बनाने की जरूरत है निर्भरता रिवर्स दिशा में लागू होती है

# common/migrations/0009_auto__del_cat.py

class Migration(SchemaMigration):

    depends_on = (
        ('specific', '0004_auto__add_cat'),
    )

    def forwards(self, orm):
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['specific.Cat']))

    def backwards(self, orm):
        db.rename_table('specific_cat', 'common_cat')
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['common.Cat']))

# specific/migrations/0004_auto__add_cat.py

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.rename_table('common_cat', 'specific_cat')

    def backwards(self, orm):
        pass

प्रति दक्षिण प्रलेखन , depends_onयह सुनिश्चित करेंगे कि 0004_auto__add_catपहले रन 0009_auto__del_cat जब आगे पलायन लेकिन में विपरीत क्रम जब पीछे की ओर पलायन । यदि हम रोलबैक db.rename_table('specific_cat', 'common_cat')में छोड़ देते हैं specific, तो commonफॉरेनके को माइग्रेट करने का प्रयास करते समय रोलबैक विफल हो जाएगा क्योंकि तालिका संदर्भित तालिका मौजूद नहीं होगी।

उम्मीद है कि यह मौजूदा समाधानों की तुलना में "वास्तविक दुनिया" की स्थिति के करीब है और किसी को यह मददगार लगेगा। चीयर्स!


इस उत्तर में निश्चित स्रोत कंटेंटटेप्स को अपडेट करने के लिए लाइनों को छोड़ देते हैं, जो पोट्र सीज़चूर के उत्तर में मौजूद हैं। यह भ्रामक हो सकता है।
Shai Berger

@ शायरबर्गर ने मुझे विशेष रूप से संबोधित किया: "मैं जानबूझकर Django ContentType परिवर्तनों को अनदेखा कर रहा हूं - पहले देखें कि कैसे इसका उत्तर दिया जाए।"
मैट ब्रायनकन

9

मॉडल बहुत ही आसानी से ऐप्स के साथ युग्मित नहीं होते हैं, इसलिए चलना काफी सरल है। Django डेटाबेस तालिका के नाम में ऐप नाम का उपयोग करता है, इसलिए यदि आप अपने ऐप को स्थानांतरित करना चाहते हैं, तो आप SQL ALTER TABLEकथन के माध्यम से या तो डेटाबेस तालिका का नाम बदल सकते हैं , या - और भी सरल - बस अपने मॉडल की कक्षा में db_tableपैरामीटर का उपयोग करके Metaदेखें। पुराना नाम।

यदि आपने अब तक अपने कोड में कहीं भी ContentTypes या जेनेरिक संबंधों का उपयोग किया है, तो आप शायद इसका नाम बदलना चाहेंगे app_label उस मॉडल को इंगित करने वाले contenttype का , जो आगे बढ़ रहा है, ताकि मौजूदा संबंध संरक्षित रहे।

बेशक, अगर आपके पास संरक्षित करने के लिए कोई डेटा नहीं है, तो सबसे आसान काम डेटाबेस तालिकाओं को पूरी तरह से छोड़ देना और ./manage.py syncdbफिर से चलाना है ।


2
मैं दक्षिण प्रवास के साथ ऐसा कैसे करूं?
अपराह्न

4

पोटर के उत्कृष्ट समाधान के लिए यहां एक और समाधान है। निम्नलिखित को विशिष्ट / 0003_create_cat में जोड़ें

depends_on = (
    ('common', '0002_create_cat'),
)

जब तक यह निर्भरता निर्धारित नहीं होती है दक्षिण यह गारंटी नहीं देगा कि common_catतालिका उस समय मौजूद है जब विशिष्ट / 0003_create_cat चलाया जाता है, एक फेंक रहा हैdjango.db.utils.OperationalError: no such table: common_cat तो आप पर त्रुटि है।

जब तक निर्भरता स्पष्ट रूप से निर्धारित नहीं होती है तब तक दक्षिण लेक्सोग्राफिक क्रम में पलायन करता है। के बाद से commonपहले आता है specificसब commonके प्रवास करते हैं, तालिका का नाम बदलने से चलाने जायेगा तो यह शायद Potr द्वारा दिखाए गए मूल उदाहरण में पुन: पेश नहीं होगा। लेकिन अगर आप का नाम बदलने commonके लिए app2और specificकरने के लिए app1आप इस समस्या में पड़ जाएगा।


यह वास्तव में पोटर के उदाहरण के साथ कोई समस्या नहीं है। यह नाम बदलने के लिए विशिष्ट प्रवासन का उपयोग करता है, और सामान्य प्रवासन विशिष्ट पर निर्भर करता है। यदि विशिष्ट को पहले चलाया जाता है, तो आप ठीक हैं। यदि आम पहले चलाया जाता है, तो निर्भरता उसके पहले विशिष्ट रन बनाएगी। उस ने कहा, मैंने ऐसा करते समय आदेश की अदला-बदली की, इसलिए नामकरण सामान्य रूप से हुआ, और विशिष्ट में निर्भरता, और फिर आपको निर्भरता को बदलने की आवश्यकता है जैसे कि आप ऊपर वर्णित हैं।
एमिल स्टेनस्ट्रॉम

1
मैं आपसे सहमत नहीं हो सकता। मेरे दृष्टिकोण से समाधान मजबूत होना चाहिए और इसे खुश करने की कोशिश किए बिना काम करना चाहिए। मूल समाधान तब काम नहीं करता है जब आप ताज़ा db और syncdb / migrate से शुरू करते हैं। मेरा प्रस्ताव इसे ठीक करता है। किसी भी तरह से या किसी अन्य, पोर्ट के जवाब ने मुझे बहुत समय बचा लिया, उसे
कुदोस

ऐसा नहीं करने से परीक्षण भी विफल हो सकते हैं (वे हमेशा अपना परीक्षण डेटाबेस बनाते समय पूर्ण दक्षिण प्रवास चलाते हैं)। मैंने पहले भी कुछ ऐसा ही किया है। अच्छी पकड़ Ihor :)
ओडिन्हो - वेलमॉन्ट

4

इस प्रक्रिया को मैंने वर्तमान में सुलझा लिया है क्योंकि मैं कुछ समय बाद यहां आया हूं और इसे औपचारिक रूप देने का फैसला किया है।

यह मूल रूप से Potr Czachur के उत्तर और मैट ब्रायनकॉन के उत्तर पर बनाया गया था , जिसमें दक्षिण 0.8.4 का उपयोग किया गया था

चरण 1. डिस्कवर बच्चे विदेशी कुंजी रिश्ते

# Caution: This finds OneToOneField and ForeignKey.
# I don't know if this finds all the ways of specifying ManyToManyField.
# Hopefully Django or South throw errors if you have a situation like that.
>>> Cat._meta.get_all_related_objects()
[<RelatedObject: common:toy related to cat>,
 <RelatedObject: identity:microchip related to cat>]

तो इस विस्तारित मामले में, हमने एक और संबंधित मॉडल की खोज की है:

# Inside the "identity" app...
class Microchip(models.Model):

    # In reality we'd probably want a ForeignKey, but to show the OneToOneField
    identifies = models.OneToOneField(Cat)

    ...

चरण 2. माइग्रेशन बनाएं

# Create the "new"-ly renamed model
# Yes I'm changing the model name in my refactoring too.
python manage.py schemamigration specific create_kittycat --auto

# Drop the old model
python manage.py schemamigration common drop_cat --auto

# Update downstream apps, so South thinks their ForeignKey(s) are correct.
# Can skip models like Toy if the app is already covered
python manage.py schemamigration identity update_microchip_fk --auto

चरण 3. स्रोत नियंत्रण: अब तक किए गए परिवर्तनों को कमिट करें।

यदि आप अपडेट किए गए एप्लिकेशन पर माइग्रेशन लिखने वाली टीम के साथी की तरह मर्ज में चले जाते हैं, तो यह एक अधिक दोहराव वाली प्रक्रिया बनाता है।

चरण 4. माइग्रेशन के बीच निर्भरता जोड़ें।

मूल रूप create_kittycatसे सब कुछ की वर्तमान स्थिति पर निर्भर करता है, और फिर सब कुछ निर्भर करता है create_kittycat

# create_kittycat
class Migration(SchemaMigration):

    depends_on = (
        # Original model location
        ('common', 'the_one_before_drop_cat'),

        # Foreign keys to models not in original location
        ('identity', 'the_one_before_update_microchip_fk'),
    )
    ...


# drop_cat
class Migration(SchemaMigration):

    depends_on = (
        ('specific', 'create_kittycat'),
    )
    ...


# update_microchip_fk
class Migration(SchemaMigration):

    depends_on = (
        ('specific', 'create_kittycat'),
    )
    ...

चरण 5. तालिका का नाम बदलना जिसे हम बनाना चाहते हैं।

# create_kittycat
class Migration(SchemaMigration):

    ...

    # Hopefully for create_kittycat you only need to change the following
    # 4 strings to go forward cleanly... backwards will need a bit more work.
    old_app = 'common'
    old_model = 'cat'
    new_app = 'specific'
    new_model = 'kittycat'

    # You may also wish to update the ContentType.name,
    # personally, I don't know what its for and
    # haven't seen any side effects from skipping it.

    def forwards(self, orm):

        db.rename_table(
            '%s_%s' % (self.old_app, self.old_model),
            '%s_%s' % (self.new_app, self.new_model),
        )

        if not db.dry_run:
            # For permissions, GenericForeignKeys, etc to work properly after migrating.
            orm['contenttypes.contenttype'].objects.filter(
                app_label=self.old_app,
                model=self.old_model,
            ).update(
                app_label=self.new_app,
                model=self.new_model,
            )

        # Going forwards, should be no problem just updating child foreign keys
        # with the --auto in the other new South migrations

    def backwards(self, orm):

        db.rename_table(
            '%s_%s' % (self.new_app, self.new_model),
            '%s_%s' % (self.old_app, self.old_model),
        )

        if not db.dry_run:
            # For permissions, GenericForeignKeys, etc to work properly after migrating.
            orm['contenttypes.contenttype'].objects.filter(
                app_label=self.new_app,
                model=self.new_model,
            ).update(
                app_label=self.old_app,
                model=self.old_model,
            )

        # Going backwards, you probably should copy the ForeignKey
        # db.alter_column() changes from the other new migrations in here
        # so they run in the correct order.
        #
        # Test it! See Step 6 for more details if you need to go backwards.
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['common.Cat']))
        db.alter_column('identity_microchip', 'identifies_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['common.Cat']))


# drop_cat
class Migration(SchemaMigration):

    ...

    def forwards(self, orm):
        # Remove the db.delete_table(), if you don't at Step 7 you'll likely get
        # "django.db.utils.ProgrammingError: table "common_cat" does not exist"

        # Leave existing db.alter_column() statements here
        db.alter_column('common_toy', 'belongs_to_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['specific.KittyCat']))

    def backwards(self, orm):
        # Copy/paste the auto-generated db.alter_column()
        # into the create_kittycat migration if you need backwards to work.
        pass


# update_microchip_fk
class Migration(SchemaMigration):

    ...

    def forwards(self, orm):
        # Leave existing db.alter_column() statements here
        db.alter_column('identity_microchip', 'identifies_id', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['specific.KittyCat']))

    def backwards(self, orm):
        # Copy/paste the auto-generated db.alter_column()
        # into the create_kittycat migration if you need backwards to work.
        pass

चरण 6. यदि आपको पीछे की ओर () काम करने और पीछे की ओर चलने वाला KeyError प्राप्त करने की आवश्यकता है।

# the_one_before_create_kittycat
class Migration(SchemaMigration):

    # You many also need to add more models to South's FakeORM if you run into
    # more KeyErrors, the trade-off chosen was to make going forward as easy as
    # possible, as that's what you'll probably want to do once in QA and once in
    # production, rather than running the following many times:
    #
    # python manage.py migrate specific <the_one_before_create_kittycat>

    models = {
        ...
        # Copied from 'identity' app, 'update_microchip_fk' migration
        u'identity.microchip': {
            'Meta': {'object_name': 'Microchip'},
            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
            'identifies': ('django.db.models.fields.related.OneToOneField', [], {to=orm['specific.KittyCat']})
        },
        ...
    }

चरण 7. इसका परीक्षण करें - मेरे लिए जो काम करता है वह आपके वास्तविक जीवन की स्थिति के लिए पर्याप्त नहीं हो सकता है :)

python manage.py migrate

# If you need backwards to work
python manage.py migrate specific <the_one_before_create_kittycat>

3

इसलिए @Potr की मूल प्रतिक्रिया का उपयोग करके मेरे लिए दक्षिण 0.8.1 और Django 1.5.1 पर काम नहीं किया गया। मैं पोस्ट कर रहा हूं कि मेरे लिए इस उम्मीद में नीचे क्या काम किया है कि यह दूसरों के लिए उपयोगी है।

from south.db import db
from south.v2 import SchemaMigration
from django.db import models

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('common_cat', 'specific_cat') 

        if not db.dry_run:
             db.execute(
                "update django_content_type set app_label = 'specific' where "
                " app_label = 'common' and model = 'cat';")

    def backwards(self, orm):
        db.rename_table('specific_cat', 'common_cat')
            db.execute(
                "update django_content_type set app_label = 'common' where "
                " app_label = 'specific' and model = 'cat';")

1

मैं उन चीजों में से एक का अधिक स्पष्ट संस्करण देने जा रहा हूं जो डैनियल रोजमैन ने अपने उत्तर में दी थी ...

यदि आप db_tableमौजूदा तालिका नाम की ओर संकेत करने के लिए चले गए मॉडल की मेटा विशेषता को बदलते हैं (नए नाम के बजाय Django इसे दे देंगे यदि आपने छोड़ दिया और कियाsyncdb तो) आप जटिल दक्षिण पलायन से बच सकते हैं। उदाहरण के लिए:

मूल:

# app1/models.py
class MyModel(models.Model):
    ...

आगे बढ़ने के बाद:

# app2/models.py
class MyModel(models.Model):
    class Meta:
        db_table = "app1_mymodel"

अब आपको तालिका में अपडेट करने के app_labelलिए केवल डेटा माइग्रेशन करने की आवश्यकता है और आपको जाना अच्छा होना चाहिए ...MyModeldjango_content_type

./manage.py datamigration django update_content_typeफिर चलाएं उस फ़ाइल को संपादित करें जिसे दक्षिण आपके लिए बनाता है:

def forwards(self, orm):
    moved = orm.ContentType.objects.get(app_label='app1', model='mymodel')
    moved.app_label = 'app2'
    moved.save()

def backwards(self, orm):
    moved = orm.ContentType.objects.get(app_label='app2', model='mymodel')
    moved.app_label = 'app1'
    moved.save()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.