使用 South 重构带有继承的 Django 模型

发布于 2024-08-08 15:41:34 字数 2157 浏览 4 评论 0原文

我想知道 Django south 是否可以进行以下迁移并仍然保留数据。

之前:

我目前有两个应用程序,一个称为电视,一个称为电影,每个应用程序都有一个 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)

因为这两个视频文件对象非常相似,所以我想消除重复并在一个名为 media 的单独应用程序中创建一个新模型,该应用程序包含通用 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)

所以我的问题是,如何使用 django-south 完成此任务并仍然保持现有数据?

所有这三个应用程序都已由南部迁移管理,根据南部文档,将架构和数据迁移结合起来是不好的做法,他们建议应该通过几个步骤来完成。

我认为可以使用像这样的单独迁移来完成(假设已经创建了 media.VideoFile)

  1. 架构迁移以重命名 tv.VideoFile 和 movie.VideoFile 中的所有字段,这些字段将移动到新的 media.VideoFile 模型,也许会移动到 old_name 之类的东西、old_size 等
  2. 架构迁移到 tv.VideoFile 和 movie.VideoFile 以从 media.VideoFile 继承
  3. 数据迁移将 old_name 复制到 name,old_size 复制到 size 等
  4. 架构迁移以删除 old_ 字段

在我完成所有这些工作之前,您认为那会起作用吗?有更好的办法吗?

如果您感兴趣,该项目托管在此处:http://code.google.com/p /中值/

I was wondering if the following migration is possible with Django south and still retain data.

Before:

I currently have two apps, one called tv, one called movies, each with a VideoFile model (simplified here):

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)

After:

Because the two videofile objects are so similar I want to get rid of duplication and create a new model in a separate app called media that contains a generic VideoFile class and use inheritance to extend it:

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)

So my question is, how can I accomplish this with django-south and still maintain existing data?

All three these apps are already managed by south migrations and according to the south documentation it is bad practice to combine a schema and data migration and they recommend it should be done in a few steps.

I think it could be done using separate migrations like this (assuming media.VideoFile is already created)

  1. Schema migration to rename all fields in tv.VideoFile and movies.VideoFile that will move to the new media.VideoFile model, maybe to something like old_name, old_size, etc
  2. Schema migration to tv.VideoFile and movies.VideoFile to inherit from media.VideoFile
  3. Data migration to copy old_name to name, old_size to size, etc
  4. Scheme migration to remove old_ fields

Before I go through all that work, do you think that will work? Is there a better way?

If you're interested, the project is hosted here: http://code.google.com/p/medianav/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

潜移默化 2024-08-15 15:41:34

查看下面 Paul 的回复,了解有关与较新版本 Django/South 的兼容性的一些说明。


这似乎是一个有趣的问题,而且我正在成为 South 的忠实粉丝,因此我决定研究一下这个有点。我根据您上面描述的摘要构建了一个测试项目,并成功使用 South 来执行您所询问的迁移。在我们开始编写代码之前,请注意以下几点:

  • South 文档建议分开进行架构迁移和数据迁移。我也这样做了。

  • 在后端,Django 通过在继承模型上自动创建 OneToOne 字段来表示继承表

  • 理解这一点,我们的 South 迁移需要手动正确处理 OneToOne 字段,但是,在对此进行实验时,South(或者可能是 Django 本身)似乎无法在具有相同名称的多个继承表上创建 OneToOne 字段。因此,我将电影/电视应用程序中的每个子表重命名为各自的应用程序(即 MovieVideoFile/ShowVideoFile)。

  • 在实际的数据迁移代码中,South 似乎更喜欢先创建 OneToOne 字段,然后向其分配数据。在创建过程中将数据分配给 OneToOne 字段会导致 South 感到窒息。 (对 South 的所有酷性的公平妥协)。

说了这么多,我尝试保留发出的控制台命令的日志。必要时我会插入评论。最终代码在底部。

命令历史

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

为了节省篇幅,并且由于模型最终看起来总是相同的,我将仅使用“电影”应用程序进行演示。

movie/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')

movie/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 来测试您的架构。在尝试任何操作之前务必先进行备份,并且通常要小心。

兼容性通知

我将保持原始消息不变,但 South 已将命令 manage.py startmigration 更改为 manage.py schemamigration

Check out response below by Paul for some notes on compatibility with newer versions of Django/South.


This seemed like an interesting problem, and I'm becoming a big fan of South, so I decided to look into this a bit. I built a test project on the abstract of what you've described above, and have successfully used South to perform the migration you are asking about. Here's a couple of notes before we get to the code:

  • The South documentation recommends doing schema migrations and data migrations separate. I've followed suit in this.

  • On the backend, Django represents an inherited table by automatically creating a OneToOne field on the inheriting model

  • Understanding this, our South migration needs to properly handle the OneToOne field manually, however, in experimenting with this it seems that South (or perhaps Django itself) cannot create a OneToOne filed on multiple inherited tables with the same name. Because of this, I renamed each child-table in the movies/tv app to be respective to it's own app (ie. MovieVideoFile/ShowVideoFile).

  • In playing with the actual data migration code, it seems South prefers to create the OneToOne field first, and then assign data to it. Assigning data to the OneToOne field during creation cause South to choke. (A fair compromise for all the coolness that is South).

So having said all that, I tried to keep a log of the console commands being issued. I'll interject commentary where necessary. The final code is at the bottom.

Command History

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

For space sake, and since the models invariably look the same in the end, I'm only going to demonstrate with 'movies' app.

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 (schema migration)

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 (data migration)

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'

South is awesome!

Ok standard disclaimer: You're dealing with live data. I've given you working code here, but please use the --db-dry-run to test your schema. Always make a backup before trying anything, and generally be careful.

COMPATIBILITY NOTICE

I'm going to keep my original message intact, but South has since changed the command manage.py startmigration into manage.py schemamigration.

沉睡月亮 2024-08-15 15:41:34

我确实尝试过介绍 T Stone 概述的解决方案,虽然我认为这是一个极好的入门,并解释了应该如何完成工作,但我遇到了一些问题。

我认为大多数情况下您不需要需要再为父类创建表条目,即您不再需要了

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

。 Django 现在将自动为您执行此操作(如果您有非空字段,则上述内容对我不起作用并给我一个数据库错误)。

我认为这可能是由于 django 和 South 的变化造成的,这是一个在 ubuntu 10.10 上使用 django 1.2.3 和 South 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

我使用 $ 来表示 shell 提示符,所以不要输入它。)

接下来创建新类 SimplePostExtPost 在 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

应该就是这样!希望一切顺利,并且您已经重构了您的模型。

I did try to walk through the solution outlined by T Stone and while I think it's a superb starter and explains how things should be done I ran into a few problems.

I think mostly you don't need to create the table entry for the parent class anymore, i.e. you don't need

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

anymore. Django will now do this automatically for you (if you have non-null fields then the above did not work for me and gave me a database error).

I think it is probably due to changes in django and south, here is a version that worked for me on ubuntu 10.10 with django 1.2.3 and south 0.7.1. The models are a little different, but you will get the gist:

Initial setup

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)

There is obviously a lot of overlap, so I wanted to factor the commonalities
out into a general post model and only keep the differences in the other
model classes.

new setup:

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)

If you want to follow along you will first need to get these models into south:

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

Migrating the data

How to go about it? First write the new app genpost and do the initial
migrations with south:

$./manage.py schemamigration genpost --initial

(I am using $ to represent the shells prompt, so don't type that.)

Next create the new classes SimplePost and ExtPost in post1/models.py
and post2/models.py respectively (don't delete the rest of the classes yet).
Then create schemamigrations for these two as well:

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

Now we can apply all these migrations:

$./manage.py migrate

Let's get to the heart of the matter, migrating the data from post1 and post2 to genpost:

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

Then edit 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.")

Now apply these migrations:

$./manage.py migrate

Next you can delete the now redundant parts from post1/models.py and post2/models.py and then create schemamigrations to update the tables to the new state:

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

And that should be it! Hopefully it all works and you have refactored your models.

怪我鬧 2024-08-15 15:41:34

抽象模型

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

可能是通用关系对您也很有用。

Abstract Model

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

May be generic relation will be useful for you too.

战皆罪 2024-08-15 15:41:34

我进行了类似的迁移,并且选择分多个步骤进行。除了创建多个迁移之外,我还创建了向后迁移,以便在出现问题时提供后备。然后,我抓取了一些测试数据并将其向前和向后迁移,直到我确信向前迁移时它能够正确输出。最后我迁移了生产站点。

I did a similar migration and I chose to do it in multiple steps. In addition to creating the multiple migrations, I also created the backward migration to provide a fallback if things went wrong. Then, I grabbed some test data and migrated it forward and backwards until I was sure it was coming out correctly when I migrated forwards. Finally, I migrated the production site.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文