如何将数据(即特定模型字段值)从 Mezzanine Displayable 模型迁移到新的自定义 Mezzanine Page 模型?

How do I migrate data (i.e. specific model field values) from a Mezzanine Displayable model to a new custom Mezzanine Page model?

我正在升级遗留 Django/Mezzanine 项目,需要将旧 Displayable 模型 (Job) 转换为 Page 模型 运行 (JobPage)。

旧的 Job 模型如下所示:

class Job(Displayable):
    job_title = models.CharField(max_length=300)
    job_type = models.IntegerField(choices=job_values.TYPES)
    location = models.IntegerField(choices=job_values.LOCATION)
    job_group = models.IntegerField(choices=job_values.GROUPS)

    job_description = RichTextField(blank=True, null=True)
    job_qualifications = RichTextField(blank=True, null=True)

    contact_name = models.CharField(max_length=200, blank=True, null=True)
    contact_attach = models.CharField(max_length=500, blank=True, null=True)
    contact_email = models.EmailField()

    def __unicode__(self):
        return self.job_title

    @property
    def job_id_on_page(self):
        return slugify(self.job_title)

    def get_page_slug(self):
        return "/%s#%s" % (job_values.URLS[self.job_group],     self.job_id_on_page)

    def get_absolute_url(self):
        return self.get_page_slug()

   def save(self, *args, **kwargs):
        self.title = self.job_title
        self.slug = "job-listing/%s" % slugify(self.job_title)

我这样创建了 Job Page 模型:

class JobPage(Page):
    job_title = models.CharField(max_length=300)
    job_type = models.IntegerField(choices=job_values.TYPES)
    location = models.IntegerField(choices=job_values.LOCATION)
    job_group = models.IntegerField(choices=job_values.GROUPS)

    job_description = RichTextField(blank=True, null=True)
    job_qualifications = RichTextField(blank=True, null=True)

    contact_name = models.CharField(max_length=200, blank=True, null=True)
    contact_attach = models.CharField(max_length=500, blank=True, null=True)
    contact_email = models.EmailField()

    def save(self, *args, **kwargs):
        self.title = self.job_title
        self.slug = "job-listing/%s" % slugify(self.job_title)

        super(JobPage, self).save(*args, **kwargs)

    def __unicode__(self):
        return self.job_title

和运行:

python manage.py makemigratons
python manage.py migrate

我的 运行 传输数据的策略是进行 Django 数据迁移(因为我使用的是 Mezzanine 4+ 和 Django 1.8,并且固定装置正在被弃用):

python manage.py makemigrations --empty jobapp

我进入 jobapp/migrations,打开空迁移,然后输入以下内容:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

from mezzanine.utils.urls import slugify

def dump_into_jobpage(apps, schema_editor):
    Job = apps.get_model('jobapp','Job')
    JobPage = apps.get_model('jobapp','JobPage')

    fields = ['job_title', 'job_type', 'location', 'job_group',
              'job_description', 'job_qualifications', 'contact_name',
              'contact_attach', 'contact_email']
    site_id = JobPage._meta.get_field('site_id')
    data_dict = {"site_id":1}

    for j in Job.objects.all():
        for field in fields:
            try:
                value = getattr(j, field)
                data_dict[field] = value
            except:
                pass

        data_dict['title'] = data_dict['job_title']
        data_dict['slug'] = 'job-listing/%s' %     slugify(data_dict['job_title'])

        JobPage(**data_dict).save()

class Migration(migrations.Migration):

dependencies = [
        ('jobapp', '0028_jobpage'),
    ]

    operations = [
        migrations.RunPython(dump_into_jobpage)
    ]

然后我运行:

python manage.py migrate

并进入:

python manage.py shell

检查所有正确迁移的数据,包括本机 Page slug 字段(稍后会导致问题)。我成功打印了 Job Page 模型的所有 slug 字段。

当我 runserver 进入管理页面并尝试单击 'Pages' 时,问题就出现了,这样我就可以看到一切 运行 是否顺利。当然没有运行顺利。我收到以下错误:

Error during template rendering

**In template /home/vagrant/.virtualenvs/newcorpsite/local/lib/python2.7/site-packages/mezzanine/pages/templates/admin/pages/page/change_list.html, error at line 40**
'NoneType' object has no attribute 'replace'
30                  <option value="{{ model.add_url }}">{{ model.meta_verbose_name|capfirst }}</option>
31                  {% endif %}
32              {% endfor %}
33          </select> 
34      </div>
35      {% endif %}
36  
37      {% if cl.result_count == 0 %}
38      <p class="paginator">0 {{ cl.opts.verbose_name_plural }}</p>
39      {% else %}
40  
          **<div id="tree">
             {% page_menu "pages/menus/admin.html" %}
          </div>**

41      {% endif %}
42  
43  </div>
44  {% endblock %}

回溯到:

self.html_id = self.slug.replace("/","-")

所以基本上 JobPage 中的一个或多个没有 slug 字段,即使我刚刚在 PYTHON SHELL 中看到 SLUG 属性。

我对这个有点疯狂。任何人都知道为什么这些字段值不会坚持?

我在上面发布的代码是在尝试弄清楚如何执行此操作的多次失败迭代后创建的。在那些失败的迭代中,我将 JobPage 模型添加到数据库中,然后又多次删除它,因为我遇到了不同的错误并尝试了不同的字段和字段类型。显然,当您删除页面模型时,它的对象不会从 <your_app>.pages_page 数据库中消失(pages_page 是夹层 Page 模型数据库)。相反,它会留下旧条目。如果您然后像这样直接删除其中一个对象:

Page.objects.get(field=value).delete()

或delete/don不填写字段,对象实际上并没有消失,而是留下了一个null/None数据库条目。然而,Page 对象确实保留了它的 id 字段。我不知道我是否在某个地方有奇怪的设置导致了这个,如果它是一个夹层的东西,或者它是否是一个 Django 的东西,但这就是失败的原因。我收到 NoneType 错误,因为 Mezzanine 在 pages_page 数据库中看到了一个 Page 的条目,并试图在管理菜单中为它创建一个项目。很好,除了该页面的所有条目都是 None。我无法删除 python shell 中的 None 个对象,因为我会得到一个错误 Cannot use None as a query value,但我可以打印单个 id,写下它们,然后使用 idid 手动删除 mysql shell 中的 Page 个对象。

那时,我能够使用上面的所有代码成功创建新的 JobPage

另一个警告,我必须将此迁移添加到 JobPage 模型,因为通过 Django 数据迁移创建它们不会填充 content_model 字段:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

def update_jobpage(apps, schema_editor):
    JobPage = apps.get_model('jobapp','JobPage')
    JobPage.objects.all().update(content_model='jobpage')

class Migration(migrations.Migration):

    dependencies = [
        ('jobapp', '0029_auto_20150911_1624'),
    ]

    operations = [
        migrations.RunPython(update_jobpage)
    ]

如果没有 content_model 字段,Mezzanine 将无法使用其内置的 get_content_model() 函数来确定自定义内容类型(在本例中为 JobPage,转换为 jobpage) 它试图放在管理菜单上的 Page 是基于的。因此它会抛出错误并阻止您使用该页面。

我在这里错过了什么吗?为什么删除模型后旧的 JobPage 条目没有消失?为什么在 Python 中直接删除它们会留下一个 NoneType 对象,所以我必须在 MySQL 中手动删除它们?