【问题标题】:How to move a model between two Django apps (Django 1.7)如何在两个 Django 应用程序之间移动模型(Django 1.7)
【发布时间】:2014-10-28 04:40:12
【问题描述】:

所以大约一年前我开始了一个项目,和所有新开发人员一样,我并没有真正关注结构,但是现在我与 Django 一起更进一步,它开始出现我的项目布局主要是我的模型结构很糟糕。

我的模型主要保存在一个应用程序中,实际上这些模型中的大多数应该在它们自己的单独应用程序中,我确实尝试解决这个问题并将它们向南移动,但是由于外键等,我发现它很棘手而且非常困难.

但是由于 Django 1.7 并内置了对迁移的支持,现在有更好的方法吗?

【问题讨论】:

  • 您可能需要考虑更改接受的答案。
  • 对于将来遇到此问题的人:此处为 Django 3.x,realpython.com/move-django-model/… 中详述的方法对我有用。我在旧应用中的模型和新应用中的模型之间有多个外键。
  • 如果您要移动的模型是自定义用户模型(或在settings.py 中引用并与之相关的任何其他模型),则移动变得更加复杂。详情见stackoverflow.com/questions/69473228/…

标签: python mysql database django schema-migration


【解决方案1】:

这可以使用migrations.SeparateDatabaseAndState 轻松完成。基本上,我们使用数据库操作来重命名表,同时使用两个状态操作从一个应用的历史记录中删除模型并在另一个应用的历史记录中创建它。

从旧应用中删除

python manage.py makemigrations old_app --empty

在迁移中:

class Migration(migrations.Migration):

    dependencies = []

    database_operations = [
        migrations.AlterModelTable('TheModel', 'newapp_themodel')
    ]

    state_operations = [
        migrations.DeleteModel('TheModel')
    ]

    operations = [
        migrations.SeparateDatabaseAndState(
            database_operations=database_operations,
            state_operations=state_operations)
    ]

添加到新应用

首先,将模型复制到新应用的model.py中,然后:

python manage.py makemigrations new_app

这将生成一个以幼稚的CreateModel 操作作为唯一操作的迁移。将其包装在 SeparateDatabaseAndState 操作中,这样我们就不会尝试重新创建表。还包括先前的迁移作为依赖项:

class Migration(migrations.Migration):

    dependencies = [
        ('old_app', 'above_migration')
    ]

    state_operations = [
        migrations.CreateModel(
            name='TheModel',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
            ],
            options={
                'db_table': 'newapp_themodel',
            },
            bases=(models.Model,),
        )
    ]

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]

【讨论】:

  • 很好的解释。这应该是答案,重命名表可以避免丢失任何数据。
  • 这是最好的方法,它比我的要好得多。在我的答案顶部添加了注释。
  • 我这样做了,但是当我在 newapp 上运行“makemigrations”之后,它会生成一个 AlterModelTable 迁移,将其重命名为 None。
  • 根据这些说明找到了解决我的问题的方法。如果您有必填字段的外键引用,则问题会更加复杂。我必须添加几个步骤来移动参考。
  • 由于多个请求,我用一个 GitHub 示例创建了一个关于 FK 模型迁移的详细答案。 stackoverflow.com/questions/30601107/…
【解决方案2】:

我正在删除旧答案,因为可能会导致数据丢失。作为ozan mentioned,我们可以在每个应用程序中创建 2 个迁移。这篇文章下面的 cmets 指的是我的旧答案。

从第一个应用程序中删除模型的第一次迁移。

$ python manage.py makemigrations old_app --empty

编辑迁移文件以包含这些操作。

class Migration(migrations.Migration):

    database_operations = [migrations.AlterModelTable('TheModel', 'newapp_themodel')]

    state_operations = [migrations.DeleteModel('TheModel')]

    operations = [
      migrations.SeparateDatabaseAndState(
        database_operations=database_operations,
        state_operations=state_operations)
    ]

第二次迁移取决于第一次迁移并在第二个应用程序中创建新表。将模型代码移动到第二个应用程序后

$ python manage.py makemigrations new_app 

并将迁移文件编辑为类似的内容。

class Migration(migrations.Migration):

    dependencies = [
        ('old_app', 'above_migration')
    ]

    state_operations = [
        migrations.CreateModel(
            name='TheModel',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
            ],
            options={
                'db_table': 'newapp_themodel',
            },
            bases=(models.Model,),
        )
    ]

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]

【讨论】:

  • 我确实有现有的数据,而且很多数据我根本无法丢失,有可能这样做吗?
  • @KevinChristopherHenry 修改了代码。这会保留现有数据。
  • @SamBuckingham 是的,您可以尝试使用修改后的代码进行迁移而不会丢失数据。
  • 我认为这确实是最好的方法,感谢所有帮助的人,这真是太棒了。
  • IMO 这是一个错误的解决方案,迁移的基本假设是,如果您运行./manage.py migrate,一切都会以良好状态结束。手动伪造迁移是 IMO 错误的方式。
【解决方案3】:

我遇到了同样的问题。 Ozan's answer 帮了我很多,但不幸的是还不够。事实上,我有几个 ForeignKey 链接到我想要移动的模型。经过一番头痛后,我找到了解决方案,因此决定将其发布以解决人们的时间。

您还需要 2 个步骤:

  1. 在做任何事情之前,将所有链接到TheModelForeignKey 更改为Integerfield。然后运行python manage.py makemigrations
  2. 完成 Ozan 的步骤后,重新转换您的外键:放回 ForeignKey(TheModel) 而不是 IntegerField()。然后再次进行迁移 (python manage.py makemigrations)。然后您可以迁移,它应该可以工作 (python manage.py migrate)

希望对您有所帮助。当然,在尝试生产之前先在本地进行测试,以避免意外:)

【讨论】:

  • ManyToManyField 关系呢??
  • @tomcounsell 很棒的评论,我会假设仅出于迁移的目的添加特定的直通模型。保持数据完整需要大量工作...
  • 由于多对多关系通常只是一个具有两个外键的表,因此从 SQL 的角度来看,您可以应用此答案的技巧。但是为了仅通过 Django 实现这一点,我能想到的一种方法是按照 @ozan 的回答,除了第一步是复制 MTM 关系中涉及的表(每个应用程序中的一个版本的骗子) ,将所有外键迁移到新应用程序,然后才删除旧应用程序中的欺骗。免责声明:我没有测试过:)
【解决方案4】:

我是如何做到的(在 Django==1.8 上测试,使用 postgres,所以可能也是 1.7)

情况

app1.YourModel

但您希望它转到: app2.YourModel

  1. 将 YourModel(代码)从 app1 复制到 app2。
  2. 将此添加到 app2.YourModel:

    Class Meta:
        db_table = 'app1_yourmodel'
    
  3. $ python manage.py makemigrations app2

  4. 一个新的迁移(例如 0009_auto_something.py)在 app2 中使用 migrations.CreateModel() 语句进行,将此语句移动到 app2 的初始迁移(例如 0001_initial.py)(就像它一样一直都在)。现在删除创建的迁移 = 0009_auto_something.py

  5. 就像你的行为一样,就像 app2.YourModel 一直存在一样,现在从你的迁移中删除 app1.YourModel 的存在。含义:注释掉 CreateModel 语句,以及之后您使用的每个调整或数据迁移。

  6. 当然,每个对 app1.YourModel 的引用都必须通过您的项目更改为 app2.YourModel。另外,不要忘记迁移中 app1.YourModel 的所有可能外键都必须更改为 app2.YourModel

  7. 现在,如果您执行 $ python manage.py migrate,没有任何变化,当您执行 $ python manage.py makemigrations 时,也没有检测到任何新内容。

  8. 现在画龙点睛:从 app2.YourModel 中删除 Class Meta 并执行 $ python manage.py makemigrations app2 && python manage.py migrate app2(如果您查看此迁移,您会看到类似这样的内容:)

        migrations.AlterModelTable(
        name='yourmodel',
        table=None,
    ),
    

table=None,表示它将采用默认表名,在本例中为 app2_yourmodel。

  1. 完成,数据已保存。

P.S 在迁移过程中,它会看到 content_type app1.yourmodel 已被删除并且可以删除。您可以对此说是,但前提是您不使用它。如果您严重依赖它来使该内容类型的 FK 完好无损,请不要回答“是”或“否”,而是手动进入数据库,然后删除内容类型 app2.yourmodel,然后重命名内容类型 app1。 yourmodel 到 app2.yourmodel,然后回答 no 继续。

【讨论】:

  • 虽然这个解决方案绝对比@ozan 的“hackier”,而且它肯定需要更多的编辑,但它对我来说效果很好(编辑迁移是可以的 - 根据文档,它们应该是可编辑的)。
  • 可能还使用app_label = 'app1' 元选项。
  • 天才!这对我来说非常适合 ForeignKey 关系。我想这也适用于 ManyToMany 字段。
  • 我按照您的步骤进行操作,但是属于 app1 的某个模型中的字段包含一个外键,该外键与要移动的模型(myModel)具有递归关系。就像field1 = models.ForeignKey('app1.myModel'). 当我迁移时,我收到一个ValueError 指出field1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
【解决方案5】:

我遇到了紧张的手工编码迁移(正如Ozan's 答案所要求的那样),因此以下结合了 Ozan 和 Michael's 的策略,以尽量减少所需的手工编码量:

  1. 在移动任何模型之前,请通过运行 makemigrations 确保使用干净的基线。
  2. 将模型的代码从 app1 移动到 app2
  3. 按照@Michael 的建议,我们使用“新”模型上的db_table Meta 选项将新模型指向旧数据库表:

    class Meta:
        db_table = 'app1_yourmodel'
    
  4. 运行makemigrations。这将在app2 中生成CreateModel,在app1 中生成DeleteModel。从技术上讲,这些迁移指的是完全相同的表,并且会删除(包括所有数据)并重新创建表。

  5. 实际上,我们不想(或不需要)对桌子做任何事情。我们只需要 Django 相信已经做出了改变。根据@Ozan 的回答,SeparateDatabaseAndState 中的state_operations 标志就是这样做的。所以我们用SeparateDatabaseAndState(state_operations=[...]) 包装所有migrations 条目IN BOTH MIGRATIONS FILES。例如,

    operations = [
        ...
        migrations.DeleteModel(
            name='YourModel',
        ),
        ...
    ]
    

    变成

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=[
            ...
            migrations.DeleteModel(
                name='YourModel',
            ),
            ...
        ])
    ]
    
  6. 您还需要确保新的“虚拟”CreateModel 迁移依赖于实际创建或更改原始表的任何迁移。例如,如果您的新迁移是app2.migrations.0004_auto_<date>(对于Create)和app1.migrations.0007_auto_<date>(对于Delete),最简单的做法是:

    • 打开app1.migrations.0007_auto_<date> 并复制其app1 依赖项(例如('app1', '0006...'),)。这是app1 中的“紧接在前”的迁移,应该包括对所有实际模型构建逻辑的依赖。
    • 打开 app2.migrations.0004_auto_<date> 并将您刚刚复制的依赖项添加到其 dependencies 列表中。

如果您与要移动的模型有ForeignKey 关系,则上述方法可能不起作用。发生这种情况是因为:

  • 不会为 ForeignKey 更改自动创建依赖关系
  • 我们不想将 ForeignKey 更改包装在 state_operations 中,因此我们需要确保它们与表操作分开。

注意:Django 2.2 添加了一个警告 (models.E028) 会破坏此方法。您也许可以使用 managed=False 解决它,但我尚未对其进行测试。

“最小”操作集因情况而异,但以下过程应适用于大多数/所有 ForeignKey 迁移:

  1. 复制模型从app1app2,设置db_table,但不要更改任何FK 引用。
  2. 运行makemigrations 并将所有app2 迁移包装在state_operations 中(见上文)
    • 如上,在app2CreateTable中添加一个依赖到最新的app1迁移
  3. 将所有 FK 引用指向新模型。如果您不使用字符串引用,请将旧模型移动到 models.py 的底部(不要删除它),这样它就不会与导入的类竞争。
  4. 运行makemigrations,但不要在state_operations 中包含任何内容(实际上应该发生FK 更改)。将所有 ForeignKey 迁移(即AlterField)中的依赖项添加到app2 中的CreateTable 迁移(下一步需要此列表,因此请跟踪它们)。例如:

    • 查找包含 CreateModel 的迁移,例如app2.migrations.0002_auto_<date> 并复制该迁移的名称。
    • 查找具有该模型的 ForeignKey 的所有迁移(例如,通过搜索 app2.YourModel 来查找迁移,例如:

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
          ]
      
          operations = [
              migrations.AlterField(
                  model_name='relatedmodel',
                  name='fieldname',
                  field=models.ForeignKey(... to='app2.YourModel'),
              ),
          ]
      
    • CreateModel 迁移添加为依赖项:

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
              ('app2', '0002_auto_<date>'),
          ]  
      
  5. app1删除模型

  6. 运行makemigrations 并将app1 迁移包装在state_operations 中。
    • 为上一步中的所有ForeignKey 迁移(即AlterField)添加依赖项(可能包括app1app2 中的迁移)。
    • 当我构建这些迁移时,DeleteTable 已经依赖于 AlterField 迁移,因此我不需要手动强制执行它(即 AlterDelete 之前)。

在这一点上,Django 很好。新模型指向旧表,Django 的迁移已经说服它所有内容都已适当地重新定位。最大的警告(来自@Michael 的回答)是为新模型创建了一个新的ContentType。如果您链接(例如通过ForeignKey)到内容类型,则需要创建迁移以更新ContentType 表。

我想自己清理(元选项和表名),所以我使用了以下过程(来自@Michael):

  1. 删除db_table 元条目
  2. 再次运行makemigrations生成数据库重命名
  3. 编辑最后一次迁移并确保它依赖于DeleteTable 迁移。似乎没有必要,因为Delete 应该是纯逻辑的,但如果我不这样做,我就会遇到错误(例如app1_yourmodel 不存在)。

【讨论】:

  • 效果很好,谢谢!我认为编辑最后一次迁移并不重要,因为它无论如何都位于依赖关系树的底部。
  • 好答案!我认为您需要在 migrations.SeparateDatabaseAndState 中添加右括号,对吗?
  • 这对我有用。我也没有像@JamesMeakin 那样编辑最后一次迁移(第 3 步,整个答案的最后一行),它仍然可以正常工作
  • 在第二种情况下,使用 FK 的情况,第二步对我来说失败了,出现了一个有意义的错误:table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
  • 我已经使用了几次该程序。如果您比较 2.2 (docs.djangoproject.com/en/2.2/ref/checks) 和 2.1 (docs.djangoproject.com/en/2.1/ref/checks) 的文档,您会发现它是在 2.2 中添加的。可以使用managed=False 解决,但我无处可查。
【解决方案6】:

如果数据不大或太复杂,但维护仍然很重要,另一种方法是:

  • 使用manage.py dumpdata 获取数据夹具
  • 继续正确建模更改和迁移,而不关联更改
  • 将旧模型和应用名称中的固定装置全局替换为新的
  • 使用manage.py loaddata加载数据

【讨论】:

    【解决方案7】:

    复制自我在https://stackoverflow.com/a/47392970/8971048的回答

    如果您需要移动模型并且您不再有权访问该应用程序(或者您不希望访问),您可以创建一个新的操作并考虑仅在迁移后创建一个新模型模型不存在。

    在本例中,我将“MyModel”从 old_app 传递给 myapp。

    class MigrateOrCreateTable(migrations.CreateModel):
        def __init__(self, source_table, dst_table, *args, **kwargs):
            super(MigrateOrCreateTable, self).__init__(*args, **kwargs)
            self.source_table = source_table
            self.dst_table = dst_table
    
        def database_forwards(self, app_label, schema_editor, from_state, to_state):
            table_exists = self.source_table in schema_editor.connection.introspection.table_names()
            if table_exists:
                with schema_editor.connection.cursor() as cursor:
                    cursor.execute("RENAME TABLE {} TO {};".format(self.source_table, self.dst_table))
            else:
                return super(MigrateOrCreateTable, self).database_forwards(app_label, schema_editor, from_state, to_state)
    
    
    class Migration(migrations.Migration):
    
        dependencies = [
            ('myapp', '0002_some_migration'),
        ]
    
        operations = [
            MigrateOrCreateTable(
                source_table='old_app_mymodel',
                dst_table='myapp_mymodel',
                name='MyModel',
                fields=[
                    ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                    ('name', models.CharField(max_length=18))
                ],
            ),
        ]
    

    【讨论】:

    【解决方案8】:

    这是经过粗略测试的,所以不要忘记备份你的数据库!!!

    例如,有两个应用程序:src_appdst_app,我们要将模型 MoveMesrc_app 移动到 dst_app

    为两个应用创建空迁移:

    python manage.py makemigrations --empty src_app
    python manage.py makemigrations --empty dst_app
    

    假设新的迁移是XXX1_src_app_newXXX1_dst_app_new,之前的顶级迁移是XXX0_src_app_oldXXX0_dst_app_old

    添加一个重命名MoveMe模型的表并将其在ProjectState中的app_label重命名为XXX1_dst_app_new的操作。不要忘记添加对 XXX0_src_app_old 迁移的依赖。生成的XXX1_dst_app_new 迁移是:

    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals
    
    from django.db import models, migrations
    
    # this operations is almost the same as RenameModel
    # https://github.com/django/django/blob/1.7/django/db/migrations/operations/models.py#L104
    class MoveModelFromOtherApp(migrations.operations.base.Operation):
    
        def __init__(self, name, old_app_label):
            self.name = name
            self.old_app_label = old_app_label
    
        def state_forwards(self, app_label, state):
    
            # Get all of the related objects we need to repoint
            apps = state.render(skip_cache=True)
            model = apps.get_model(self.old_app_label, self.name)
            related_objects = model._meta.get_all_related_objects()
            related_m2m_objects = model._meta.get_all_related_many_to_many_objects()
            # Rename the model
            state.models[app_label, self.name.lower()] = state.models.pop(
                (self.old_app_label, self.name.lower())
            )
            state.models[app_label, self.name.lower()].app_label = app_label
            for model_state in state.models.values():
                try:
                    i = model_state.bases.index("%s.%s" % (self.old_app_label, self.name.lower()))
                    model_state.bases = model_state.bases[:i] + ("%s.%s" % (app_label, self.name.lower()),) + model_state.bases[i+1:]
                except ValueError:
                    pass
            # Repoint the FKs and M2Ms pointing to us
            for related_object in (related_objects + related_m2m_objects):
                # Use the new related key for self referential related objects.
                if related_object.model == model:
                    related_key = (app_label, self.name.lower())
                else:
                    related_key = (
                        related_object.model._meta.app_label,
                        related_object.model._meta.object_name.lower(),
                    )
                new_fields = []
                for name, field in state.models[related_key].fields:
                    if name == related_object.field.name:
                        field = field.clone()
                        field.rel.to = "%s.%s" % (app_label, self.name)
                    new_fields.append((name, field))
                state.models[related_key].fields = new_fields
    
        def database_forwards(self, app_label, schema_editor, from_state, to_state):
            old_apps = from_state.render()
            new_apps = to_state.render()
            old_model = old_apps.get_model(self.old_app_label, self.name)
            new_model = new_apps.get_model(app_label, self.name)
            if self.allowed_to_migrate(schema_editor.connection.alias, new_model):
                # Move the main table
                schema_editor.alter_db_table(
                    new_model,
                    old_model._meta.db_table,
                    new_model._meta.db_table,
                )
                # Alter the fields pointing to us
                related_objects = old_model._meta.get_all_related_objects()
                related_m2m_objects = old_model._meta.get_all_related_many_to_many_objects()
                for related_object in (related_objects + related_m2m_objects):
                    if related_object.model == old_model:
                        model = new_model
                        related_key = (app_label, self.name.lower())
                    else:
                        model = related_object.model
                        related_key = (
                            related_object.model._meta.app_label,
                            related_object.model._meta.object_name.lower(),
                        )
                    to_field = new_apps.get_model(
                        *related_key
                    )._meta.get_field_by_name(related_object.field.name)[0]
                    schema_editor.alter_field(
                        model,
                        related_object.field,
                        to_field,
                    )
    
        def database_backwards(self, app_label, schema_editor, from_state, to_state):
            self.old_app_label, app_label = app_label, self.old_app_label
            self.database_forwards(app_label, schema_editor, from_state, to_state)
            app_label, self.old_app_label = self.old_app_label, app_label
    
        def describe(self):
            return "Move %s from %s" % (self.name, self.old_app_label)
    
    
    class Migration(migrations.Migration):
    
        dependencies = [
           ('dst_app', 'XXX0_dst_app_old'),
           ('src_app', 'XXX0_src_app_old'),
        ]
    
        operations = [
            MoveModelFromOtherApp('MoveMe', 'src_app'),
        ]
    

    XXX1_dst_app_new 的依赖添加到XXX1_src_app_newXXX1_src_app_new 是无操作迁移,需要确保将来的 src_app 迁移将在 XXX1_dst_app_new 之后执行。

    MoveMesrc_app/models.py 移动到dst_app/models.py。然后运行:

    python manage.py migrate
    

    就是这样!

    【讨论】:

    • 请注意,此代码可能仅对 django 1.7 有用。在 django 2.0 中尝试这个是行不通的。这也意味着使用这种机制来移动模型会增加升级 django 版本的维护开销。
    【解决方案9】:

    您可以尝试以下方法(未经测试):

    1. 将模型从src_app移动到dest_app
    2. 迁移dest_app;确保架构迁移依赖于最新的 src_app 迁移 (https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files)
    3. 添加到dest_app的数据迁移,从src_app复制所有数据
    4. 迁移src_app;确保架构迁移依赖于dest_app 的最新(数据)迁移——即:步骤 3 的迁移

    请注意,您将复制整个表格,而不是移动它,但这样两个应用程序都不必接触属于另一个的表格app,我觉得这个比较重要。

    【讨论】:

      【解决方案10】:

      假设您正在将模型 TheModel 从 app_a 移动到 app_b。

      另一种解决方案是手动更改现有迁移。这个想法是,每次您在 app_a 的迁移中看到更改 TheModel 的操作时,您都会将该操作复制到 app_b 初始迁移的末尾。每次您在 app_a 的迁移中看到引用“app_a.TheModel”时,都将其更改为“app_b.TheModel”。

      我只是为一个现有项目执行此操作,我想将某个模型提取到一个可重用的应用程序中。手续很顺利。我想如果有从 app_b 到 app_a 的引用,事情会变得更加困难。此外,我为我的模型手动定义了 Meta.db_table,这可能会有所帮助。

      值得注意的是,您最终会更改迁移历史记录。这无关紧要,即使您有一个应用了原始迁移的数据库。如果原始迁移和重写迁移最终都使用相同的数据库架构,那么这种重写应该是可以的。

      【讨论】:

        【解决方案11】:
        1. 将旧模型的名称更改为“model_name_old”
        2. 迁移
        3. 在相关模型上创建具有相同关系的名为“model_name_new”的新模型 (例如,用户模型现在有 user.blog_old 和 user.blog_new)
        4. 迁移
        5. 编写自定义迁移,将所有数据迁移到新模型表中
        6. 通过在运行迁移之前和之后将备份与新数据库副本进行比较来测试这些迁移的效果
        7. 一切满意后,删除旧模型
        8. 迁移
        9. 将新模型更改为正确的名称“model_name_new”->“model_name”
        10. 在暂存服务器上测试全部迁移
        11. 将您的生产站点关闭几分钟,以便在不受用户干扰的情况下运行所有​​迁移

        为每个需要移动的模型单独执行此操作。 我不建议通过更改为整数并返回外键来做其他答案所说的 迁移后新的外键可能会有所不同,并且行可能具有不同的 ID,我不想在切换回外键时冒任何 ID 不匹配的风险。

        【讨论】:

          猜你喜欢
          • 2016-04-17
          • 2011-05-07
          • 1970-01-01
          • 2015-08-16
          • 2016-07-16
          • 2014-12-29
          • 2015-10-09
          • 1970-01-01
          相关资源
          最近更新 更多