باستخدام الجنوب إلى ريفاكتور على جانغو نموذج مع الميراث

StackOverflow https://stackoverflow.com/questions/1600129

سؤال

أنا أتساءل عما إذا كان بعد الهجرة هو ممكن مع جانغو جنوب وما زالت تحتفظ البيانات.

قبل:

لدي حاليا تطبيقات اثنين, واحد يسمى تليفزيون, واحد يسمى الأفلام ، مع كل VideoFile نموذج (المبسطة هنا):

tv/models.py:

class VideoFile(models.Model):
    show = models.ForeignKey(Show, blank=True, null=True)
    name = models.CharField(max_length=1024, blank=True)
    size = models.IntegerField(blank=True, null=True)
    ctime = models.DateTimeField(blank=True, null=True)

movies/models.py:

class VideoFile(models.Model):
    movie = models.ForeignKey(Movie, blank=True, null=True)
    name = models.CharField(max_length=1024, blank=True)
    size = models.IntegerField(blank=True, null=True)
    ctime = models.DateTimeField(blank=True, null=True)

وبعد:

لأن اثنين من videofile كائنات مشابهة لذلك أريد أن التخلص من الازدواجية إنشاء نموذج جديد في التطبيق منفصلة تسمى الوسائط التي تحتوي على عامة VideoFile الدرجة واستخدام الميراث لتمديده:

media/models.py:

class VideoFile(models.Model):
    name = models.CharField(max_length=1024, blank=True)
    size = models.IntegerField(blank=True, null=True)
    ctime = models.DateTimeField(blank=True, null=True)

tv/models.py:

class VideoFile(media.models.VideoFile):
    show = models.ForeignKey(Show, blank=True, null=True)

movies/models.py:

class VideoFile(media.models.VideoFile):
    movie = models.ForeignKey(Movie, blank=True, null=True)

لذلك سؤالي هو كيف يمكن تحقيق ذلك مع جانغو-الجنوب ولا يزال الحفاظ على البيانات الموجودة ؟

كل ثلاثة من هذه التطبيقات هي بالفعل تمكنت من جنوب الهجرات وفقا جنوب الوثائق هو ممارسة سيئة الجمع بين المخطط و نقل البيانات و يوصون ينبغي أن يتم ذلك في بضع خطوات.

أعتقد أنه يمكن القيام به باستخدام منفصلة الهجرات مثل هذا (على افتراض وسائل الإعلام.VideoFile تم إنشاؤه مسبقا)

  1. مخطط الهجرة إلى إعادة تسمية كافة الحقول في التلفزيون.VideoFile والأفلام.VideoFile التي سوف تتحرك إلى وسائل الإعلام الجديدة.VideoFile نموذج ربما شيء مثل old_name, old_size, الخ
  2. مخطط الهجرة إلى التلفزيون.VideoFile والأفلام.VideoFile أن ترث من وسائل الإعلام.VideoFile
  3. ترحيل البيانات إلى نسخة old_name إلى اسم ، old_size إلى حجم الخ
  4. مخطط الهجرة إلى إزالة old_ المجالات

قبل أن تذهب من خلال كل هذا العمل ، هل تعتقد أن هذا العمل ؟ هل هناك طريقة أفضل ؟

إذا كنت مهتما المشروع يتم استضافتها من هنا: http://code.google.com/p/medianav/

هل كانت مفيدة؟

المحلول

تحقق من استجابة أقل من بول لبعض الملاحظات على التوافق مع الإصدارات الأحدث من جانغو/جنوب.


هذا يبدو وكأنه مشكلة مثيرة للاهتمام ، وأنا أصبحت مروحة كبيرة من الجنوب ، لذلك قررت أن ننظر إلى هذا قليلا.لقد بنيت اختبار المشروع على مجردة من ما كنت قد الموضحة أعلاه, و قد استخدمت بنجاح في جنوب لأداء الهجرة تسأل عنه.وهنا بعض الملاحظات قبل أن نصل إلى المدونة:

  • جنوب الوثائق يوصي به مخطط الهجرة و البيانات الهجرات منفصلة.لقد حذت حذوها في هذا.

  • على الخلفية, جانغو يمثل ورثت الجدول تلقائيا إنشاء ثنائي الميدانية على وراثة نموذج

  • فهم هذا لدينا في جنوب الهجرة يحتاج إلى التعامل بشكل صحيح ثنائي الحقل يدويا ، ومع ذلك ، في تجريب مع هذا يبدو أن الجنوب (أو ربما جانغو نفسها) لا يمكن إنشاء ثنائي المقدمة على عدة ورثت الجداول مع نفس الاسم.بسبب هذا, أنا سميت كل طفل-الجدول في السينما/التلفزيون التطبيق أن يكون كل منهما إلى نفسه التطبيق (أي.MovieVideoFile/ShowVideoFile).

  • في اللعب مع البيانات الفعلية الهجرة رمز ، يبدو جنوب يفضل إنشاء ثنائي الميدانية أولا ثم تعيين البيانات.تعيين البيانات إلى ثنائي الملعب خلال خلق قضية الجنوب إلى الاختناق.(تسوية عادلة لجميع البرودة التي هي جنوب).

لذا وبعد كل ذلك, حاولت الحفاظ على سجل أوامر وحدة التحكم التي يتم إصدارها.سوف تقحم التعليق عند الضرورة.رمز النهائية في الجزء السفلي.

التاريخ القيادة

django-admin.py startproject southtest
manage.py startapp movies
manage.py startapp tv
manage.py syncdb
manage.py startmigration movies --initial
manage.py startmigration tv --initial
manage.py migrate
manage.py shell          # added some fake data...
manage.py startapp media
manage.py startmigration media --initial
manage.py migrate
# edited code, wrote new models, but left old ones intact
manage.py startmigration movies unified-videofile --auto
# create a new (blank) migration to hand-write data migration
manage.py startmigration movies videofile-to-movievideofile-data 
manage.py migrate
# edited code, wrote new models, but left old ones intact
manage.py startmigration tv unified-videofile --auto
# create a new (blank) migration to hand-write data migration
manage.py startmigration tv videofile-to-movievideofile-data
manage.py migrate
# removed old VideoFile model from apps
manage.py startmigration movies removed-videofile --auto
manage.py startmigration tv removed-videofile --auto
manage.py migrate

الفضاء ساكي ، ومنذ نماذج دائما ننظر نفسه في النهاية, أنا فقط أريد أن تثبت مع "الأفلام" التطبيق.

movies/models.py

from django.db import models
from media.models import VideoFile as BaseVideoFile

# This model remains until the last migration, which deletes 
# it from the schema.  Note the name conflict with media.models
class VideoFile(models.Model):
    movie = models.ForeignKey(Movie, blank=True, null=True)
    name = models.CharField(max_length=1024, blank=True)
    size = models.IntegerField(blank=True, null=True)
    ctime = models.DateTimeField(blank=True, null=True)

class MovieVideoFile(BaseVideoFile):
    movie = models.ForeignKey(Movie, blank=True, null=True, related_name='shows')

movies/migrations/0002_unified-videofile.py (مخطط الهجرة)

from south.db import db
from django.db import models
from movies.models import *

class Migration:

    def forwards(self, orm):

        # Adding model 'MovieVideoFile'
        db.create_table('movies_movievideofile', (
            ('videofile_ptr', orm['movies.movievideofile:videofile_ptr']),
            ('movie', orm['movies.movievideofile:movie']),
        ))
        db.send_create_signal('movies', ['MovieVideoFile'])

    def backwards(self, orm):

        # Deleting model 'MovieVideoFile'
        db.delete_table('movies_movievideofile')

movies/migration/0003_videofile-to-movievideofile-data.py (نقل البيانات)

from south.db import db
from django.db import models
from movies.models import *

class Migration:

    def forwards(self, orm):
        for movie in orm['movies.videofile'].objects.all():
            new_movie = orm.MovieVideoFile.objects.create(movie = movie.movie,)
            new_movie.videofile_ptr = orm['media.VideoFile'].objects.create()

            # videofile_ptr must be created first before values can be assigned
            new_movie.videofile_ptr.name = movie.name
            new_movie.videofile_ptr.size = movie.size
            new_movie.videofile_ptr.ctime = movie.ctime
            new_movie.videofile_ptr.save()

    def backwards(self, orm):
        print 'No Backwards'

الجنوب هو رهيبة!

طيب القياسية تنويه:أنت تتعامل مع بيانات حية.لقد أعطيت لك عمل مدونة هنا ، ولكن يرجى استخدام --db-dry-run اختبار الخاص بك المخطط.دائما إجراء نسخة احتياطية قبل محاولة أي شيء, و عموما تكون حذرا.

التوافق إشعار

انا ذاهب للحفاظ على بلدي الرسالة الأصلية سليمة ، ولكن جنوب تغير منذ ذلك الأمر manage.py startmigration في manage.py schemamigration.

نصائح أخرى

حاولت المشي من خلال حل حددها T الحجر حين اعتقد انها رائعة كاتب يشرح كيف يجب أن تكون الأمور لقد واجهت بعض المشاكل.

أعتقد أن معظمهم لك لا تحتاج إلى إنشاء الجدول دخول الطبقة الأم بعد الآن ، أيلا تحتاج

new_movie.videofile_ptr = orm['media.VideoFile'].objects.create()

بعد الآن.جانغو الآن القيام بذلك تلقائيا بالنسبة لك (إذا كان لديك غير فارغة الحقول أعلاه لا تعمل بالنسبة لي و أعطاني خطأ في قاعدة البيانات).

أعتقد أنه ربما يكون بسبب التغيرات في جانغو و الجنوب هنا هو النسخة التي عملت بالنسبة لي على أوبونتو 10.10 مع جانغو 1.2.3 وجنوب 0.7.1.نماذج مختلفة قليلا ولكن سوف تحصل على جوهر:

الإعداد الأولي

post1/models.py:

class Author(models.Model):
    first = models.CharField(max_length=30)
    last = models.CharField(max_length=30)

class Tag(models.Model):
    name = models.CharField(max_length=30, primary_key=True)

class Post(models.Model):
    created_on = models.DateTimeField()
    author = models.ForeignKey(Author)
    tags = models.ManyToManyField(Tag)
    title = models.CharField(max_length=128, blank=True)
    content = models.TextField(blank=True)

post2/models.py:

class Author(models.Model):
    first = models.CharField(max_length=30)
    middle = models.CharField(max_length=30)
    last = models.CharField(max_length=30)

class Tag(models.Model):
    name = models.CharField(max_length=30)

class Category(models.Model):
    name = models.CharField(max_length=30)

class Post(models.Model):
    created_on = models.DateTimeField()
    author = models.ForeignKey(Author)
    tags = models.ManyToManyField(Tag)
    title = models.CharField(max_length=128, blank=True)
    content = models.TextField(blank=True)
    extra_content = models.TextField(blank=True)
    category = models.ForeignKey(Category)

من الواضح أن هناك الكثير من التداخل ، لذلك أردت أن عامل القواسم المشتركة إلى البريد العام نموذج و تبقي فقط على الاختلافات في الآخر نموذج الطبقات.

الإعداد الجديد:

genpost/models.py:

class Author(models.Model):
    first = models.CharField(max_length=30)
    middle = models.CharField(max_length=30, blank=True)
    last = models.CharField(max_length=30)

class Tag(models.Model):
    name = models.CharField(max_length=30, primary_key=True)

class Post(models.Model):
    created_on = models.DateTimeField()
    author = models.ForeignKey(Author)
    tags = models.ManyToManyField(Tag)
    title = models.CharField(max_length=128, blank=True)
    content = models.TextField(blank=True)

post1/models.py:

import genpost.models as gp

class SimplePost(gp.Post):
    class Meta:
        proxy = True

post2/models.py:

import genpost.models as gp

class Category(models.Model):
    name = models.CharField(max_length=30)

class ExtPost(gp.Post):
    extra_content = models.TextField(blank=True)
    category = models.ForeignKey(Category)

إذا كنت تريد أن تتبع على طول سوف تحتاج أولا إلى الحصول على هذه النماذج في الجنوب:

$./manage.py schemamigration post1 --initial
$./manage.py schemamigration post2 --initial
$./manage.py migrate

ترحيل البيانات

كيفية التوجه نحو ذلك ؟ أولا كتابة التطبيق الجديد genpost الأولية الهجرات مع الجنوب:

$./manage.py schemamigration genpost --initial

(أنا باستخدام $ لتمثيل قذائف موجه لذا لا نوع.)

بجانب إنشاء فصول جديدة SimplePost و ExtPost في post1/models.py و post2/models.py على التوالي (لا تحذف بقية الفصول حتى الآن).ثم خلق schemamigrations هذين وكذلك:

$./manage.py schemamigration post1 --auto
$./manage.py schemamigration post2 --auto

الآن نحن يمكن أن تنطبق كل هذه الهجرات:

$./manage.py migrate

دعونا ندخل في صلب الموضوع, ترحيل البيانات من post1 و post2 إلى genpost:

$./manage.py datamigration genpost post1_and_post2_to_genpost --freeze post1 --freeze post2

ثم تحرير genpost/migrations/0002_post1_and_post2_to_genpost.py:

class Migration(DataMigration):

    def forwards(self, orm):

        # 
        # Migrate common data into the new genpost models
        #
        for auth1 in orm['post1.author'].objects.all():
            new_auth = orm.Author()
            new_auth.first = auth1.first
            new_auth.last = auth1.last
            new_auth.save()

        for auth2 in orm['post2.author'].objects.all():
            new_auth = orm.Author()
            new_auth.first = auth2.first
            new_auth.middle = auth2.middle
            new_auth.last = auth2.last
            new_auth.save()

        for tag in orm['post1.tag'].objects.all():
            new_tag = orm.Tag()
            new_tag.name = tag.name
            new_tag.save()

        for tag in orm['post2.tag'].objects.all():
            new_tag = orm.Tag()
            new_tag.name = tag.name
            new_tag.save()

        for post1 in orm['post1.post'].objects.all():
            new_genpost = orm.Post()

            # Content
            new_genpost.created_on = post1.created_on
            new_genpost.title = post1.title
            new_genpost.content = post1.content

            # Foreign keys
            new_genpost.author = orm['genpost.author'].objects.filter(\
                    first=post1.author.first,last=post1.author.last)[0]

            new_genpost.save() # Needed for M2M updates
            for tag in post1.tags.all():
                new_genpost.tags.add(\
                        orm['genpost.tag'].objects.get(name=tag.name))

            new_genpost.save()
            post1.delete()

        for post2 in orm['post2.post'].objects.all():
            new_extpost = p2.ExtPost() 
            new_extpost.created_on = post2.created_on
            new_extpost.title = post2.title
            new_extpost.content = post2.content

            # Foreign keys
            new_extpost.author_id = orm['genpost.author'].objects.filter(\
                    first=post2.author.first,\
                    middle=post2.author.middle,\
                    last=post2.author.last)[0].id

            new_extpost.extra_content = post2.extra_content
            new_extpost.category_id = post2.category_id

            # M2M fields
            new_extpost.save()
            for tag in post2.tags.all():
                new_extpost.tags.add(tag.name) # name is primary key

            new_extpost.save()
            post2.delete()

        # Get rid of author and tags in post1 and post2
        orm['post1.author'].objects.all().delete()
        orm['post1.tag'].objects.all().delete()
        orm['post2.author'].objects.all().delete()
        orm['post2.tag'].objects.all().delete()


    def backwards(self, orm):
        raise RuntimeError("No backwards.")

الآن تطبيق هذه الهجرات:

$./manage.py migrate

القادم يمكنك حذف الآن زائدة أجزاء من post1/models.py و post2/models.py ثم خلق schemamigrations تحديث الجداول إلى الدولة الجديدة:

$./manage.py schemamigration post1 --auto
$./manage.py schemamigration post2 --auto
$./manage.py migrate

وينبغي أن يكون ذلك!نأمل أن يعمل كل شيء و لديك ريفاكتوريد النماذج الخاصة بك.

نموذج مجردة

class VideoFile(models.Model):
    name = models.CharField(max_length=1024, blank=True)
    size = models.IntegerField(blank=True, null=True)
    ctime = models.DateTimeField(blank=True, null=True)
    class Meta:
        abstract = True

قد يكون عامة بالنسبة سوف تكون مفيدة لك أيضا.

لم مماثل الهجرة و اخترت أن تفعل ذلك في خطوات متعددة.بالإضافة إلى خلق هجرات متعددة, أنا أيضا إنشاء المتخلفة الهجرة لتوفير تراجع إذا ساءت الأمور.ثم أمسكت بعض بيانات الاختبار و هاجر إلى الأمام وإلى الوراء حتى كنت على يقين أنها تخرج بشكل صحيح عندما هاجروا إلى الأمام.أخيرا, لقد هاجرت موقع الإنتاج.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top