在Django中加载夹具时,内容类型出现问题

122

我无法将Django fixture加载到MySQL数据库中,因为存在contenttypes冲突。首先,我尝试仅从我的应用程序中转储数据,方法如下:

./manage.py dumpdata escola > fixture.json

但是我一直遇到缺失外键的问题,因为我的应用程序“escola”使用了其他应用程序的表。 我不断添加其他应用程序,直到我做到了这一点:

./manage.py dumpdata contenttypes auth escola > fixture.json

现在的问题是,当我尝试将数据作为测试夹具加载时出现以下约束违规:

IntegrityError: (1062, "Duplicate entry 'escola-t23aluno' for key 2")
似乎问题在于Django试图使用与fixture中主键值冲突的不同主键值动态重新创建contenttypes。这似乎与此处记录的错误相同:http://code.djangoproject.com/ticket/7052 问题在于建议的解决方法是转储contenttypes应用程序,而我已经在做了!怎么回事?如果有任何区别,我确实有一些自定义模型权限,如此处所述:http://docs.djangoproject.com/en/dev/ref/models/options/#permissions
17个回答

168

manage.py dumpdata --natural 会使用更耐久的外键表示方式。在Django中,它们被称为"自然键"。例如:

  • Permission.codename 被用于代替 Permission.id
  • User.username 被用于代替 User.id

了解更多: 在"序列化Django对象"中自然键一节

dumpdata 的其他有用参数:

  • --indent=4 让输出的内容可读性更高。
  • -e sessions 排除会话数据。
  • -e admin 排除在管理员站点执行操作的历史记录。
  • -e contenttypes -e auth.Permission 排除每次syncdb时从模式重新创建的对象。只有与--natural一起使用时才能排除,否则可能会导致ID编号不对齐。

我正在使用这个来获取content_type属性的自然键,但是当尝试加载回固定数据时,我遇到了这个错误。TypeError: string indices must be integers, not str。你有什么想法吗? - philgo20
1
@skyjur 为什么总是要在 --natural 选项中使用 -e contenttypes -e auth.permission?我刚试了一下不带 --natural 选项也可以。而且这里的文档说如果导出 auth.permissioncontenttypes,应该使用此选项。 - wlnirvana
7
@winirvana 因为从头开始并执行syncdb后,新创建的 ContentTypePermission 无法保证获得与之前相同的id。您的数据转储包含可能引用另一个数据库中不同对象的id。这可能因以下原因之一而适用于您: 1)您的数据没有任何对这些对象的引用。 2)Permission/ContentTypes的原始ID已被保留。 3)您的loaddata成功了,但实际上由于对象引用错误的对象而导致数据损坏,而您尚未意识到。 - Ski
17
--natural 标志现已弃用,推荐使用 --natural-foreign(和 --natural-primary)标志。 - frnhr
24
最终的命令可能是:manage.py dumpdata --natural-foreign --natural-primary -e contenttypes -e auth.Permission --indent 4 > project_dump.json - Paolo
5
"--natural"已经完全删除,不再仅仅是废弃。请改用"--natural-foreign"或"--natural-primary"。 - Code-Apprentice

50

这里的所有答案都已经过时...截至2017年,最好的答案是:

manage.py dumpdata --natural-foreign --natural-primary -e contenttypes -e auth.Permission --indent 4

36

是的,这真的很烦人。 有一段时间我通过在加载fixture之前对contenttypes应用程序执行"manage.py reset"(以消除与转储版本不同的自动生成contenttypes数据)来解决它。那样可以解决问题,但最终我厌倦了这些麻烦,并放弃了使用fixtures而改为直接使用SQL转储(当然,这样做就会失去DB可移植性)。

更新 - 最好的方法是使用--natural标志来dumpdata,如下面的答案中所示。 当我写这篇答案时,该标志尚不存在。


3
我也遇到了这个问题,重置contenttypes应用程序对我也有用。谢谢你的提示! - Beau
你是如何重置它们的?在测试用例类中吗?请给我一个例子。 - Oleg Tarasenko
4
在我的单元测试中,我不使用固定装置(fixtures),通常会在setup()方法中使用ORM创建测试数据,因为这样更容易与测试保持同步。所以我从来没有在TestCase类中做过这个操作,但我相信如果你在Django的TestCase类的代码中搜索一下,你可以找到如何在同步数据库后、加载固定装置前使重置发生的方法。对我来说,只需要在"./manage.py loaddata my_fixture"之前在bash脚本中运行"./manage.py reset contenttypes"即可。 - Carl Meyer

34

尝试在创建fixture时跳过contenttypes:

./manage.py dumpdata --exclude contenttypes > fixture.json

在我的单元测试类似情况下,这对我有用,关于contenttypes的见解真的很有帮助!


16

我没有使用MySQL,而是从一个活动服务器中导入一些数据到sqlite中。在执行loaddata之前清除contenttypes应用程序的数据就解决了问题:

from django.contrib.contenttypes.models import ContentType
ContentType.objects.all().delete()
quit()

然后

python manage.py loaddata data.json

django.core.exceptions.ImproperlyConfigured: 请求设置INSTALLED_APPS,但未配置设置。您必须在访问设置之前定义环境变量DJANGO_SETTINGS_MODULE或调用settings.configure()。 - Barney Szabolcs
最好在自定义管理命令的处理程序中实现它。 - Barney Szabolcs

10

在我的测试用例中,我通过在加载转储文件之前从单元测试中重置contenttypes应用程序来解决了这个问题。Carl已经建议使用manage.py命令来解决问题,我也是用call_command方法来实现同样的事情:

>>> from django.core import management
>>> management.call_command("flush", verbosity=0, interactive=False)
>>> management.call_command("reset", "contenttypes", verbosity=0, interactive=False)
>>> management.call_command("loaddata", "full_test_data.json", verbosity=0)

我的full_test_data.json夹具包含与其余测试数据相对应的contenttypes应用程序转储。通过在加载之前重置应用程序,可以防止重复键IntegrityError


8
你需要使用自然键来表示任何外键和多对多关系。此外,在sessions应用程序中排除session表和在admin应用程序中排除logentry表可能是一个好主意。
Django 1.7+
python manage.py dumpdata --natural-foreign --exclude contenttypes --exclude auth.permission --exclude admin.logentry --exclude sessions.session --indent 4 > fixture.json

Django <1.7

python manage.py dumpdata --natural --exclude contenttypes --exclude auth.permission --exclude admin.logentry --exclude sessions.session --indent 4 > fixture.json

根据Django文档,版本1.7已弃用--natural选项,应改用--natural-foreign选项。 此对象的序列化数据中也可以省略主键,因为可以通过传递--natural-primary标志在反序列化期间计算。
python manage.py dumpdata --natural-foreign --natural-primary --exclude contenttypes --exclude auth.permission --exclude admin.logentry --exclude sessions.session --indent 4 > fixture.json

7
python manage.py dumpdata --natural-primary --exclude=contenttypes --exclude=auth.Permission --exclude=admin.logentry --exclude=sessions.session --indent 4 > initial_data.json

这对我很有效。在此,我排除了除实际模型以外的所有内容。

  • 如果您看到除您创建的模型之外的任何其他模型,则可以安全地将其排除在外。该方法的一个缺点是您会失去日志数据和授权数据。

4
./manage.py dumpdata app.Model --natural-foreign

将会改变

  "content_type": 123

to

  "content_type": [
    "app_label",
    "model"
  ],

现在,fixture可以为TestCase工作了。


3

Django 2.2.5

python manage.py dumpdata --exclude=contenttypes > datadump.json

它帮助了我


2
当 loaddata 时,可能会出现问题,可能与新数据库中的 contenttype 不匹配。 - yang zhou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接