【问题标题】:Maximum recursion depth trying to create Django migration尝试创建 Django 迁移的最大递归深度
【发布时间】:2016-11-14 19:57:34
【问题描述】:

当我尝试为我的 Django 应用程序生成初始迁移时:

python manage.py makemigrations myapp

我得到如下回溯:

  File "/myproject/.env/local/lib/python2.7/site-packages/localflavor/us/models.py", line 21, in deconstruct
    name, path, args, kwargs = super(USStateField, self).deconstruct()
  File "/myproject/.env/local/lib/python2.7/site-packages/localflavor/us/models.py", line 21, in deconstruct
    name, path, args, kwargs = super(USStateField, self).deconstruct()
  File "/myproject/.env/local/lib/python2.7/site-packages/localflavor/us/models.py", line 21, in deconstruct
    name, path, args, kwargs = super(USStateField, self).deconstruct()
  File "/myproject/.env/local/lib/python2.7/site-packages/localflavor/us/models.py", line 21, in deconstruct
    name, path, args, kwargs = super(USStateField, self).deconstruct()
RuntimeError: maximum recursion depth exceeded while calling a Python object

我正在做的唯一奇怪的事情可能是我试图从 localflavor 包中对 USStateField 进行猴子补丁。具体来说,我需要从字段的状态列表中设置自定义状态选择,所以我正在这样做:

from localflavor.us import models as us_models

_USStateField = us_models.USStateField

class USStateField(_USStateField):

    description = _("U.S. state (two uppercase letters)")

    def __init__(self, *args, **kwargs):
        kwargs['choices'] = MY_STATE_CHOICES
        kwargs['max_length'] = 2
        super(_USStateField, self).__init__(*args, **kwargs)

us_models.USStateField = USStateField

这在 Django 1.7 中完美运行,但现在在 Django 1.10 中给了我这个错误。有没有更好的方法来做到这一点?

【问题讨论】:

  • 为什么不直接在应用中继承所需的模型字段而不用猴子修补呢?有什么让这变得复杂的原因吗?
  • 刚刚在apps.py 中测试了猴子补丁 - 它适用于这种特定情况。

标签: python django django-models django-migrations


【解决方案1】:

我发现了关于“猴子修补 django 模型和迁移”主题的少量信息on github - 但看起来不太有希望。

恐怕 django 迁移的行为会使模型与猴子修补方法不兼容。当您需要处理它们时,您会在不同的地方 (How to add custom migrations to external Django apps) 遇到一些副作用。

如果您直接依赖localflavor - 是什么阻止您使用继承而不是猴子修补外部依赖项?对于来自localflavor.us 的自定义字段,您可能只需将fields.py 添加到您的自定义应用程序或项目中,然后使用您的代码:

from localflavor.us import models as us_models

class USStateField(us_models.USStateField):

    description = _("U.S. state (two uppercase letters)")

    def __init__(self, *args, **kwargs):
        kwargs['choices'] = MY_STATE_CHOICES
        kwargs['max_length'] = 2
        super(USStateField, self).__init__(*args, **kwargs)

之后,您只需在模型中使用那个from yourapp.fields import USStateField

如果你有其他依赖localfavor.us 的第三方应用程序,这使得这种方法相当复杂,因为你需要为这个小补丁重写一堆模型,我目前看到两种方法:

  • fork localflavor 并将其作为直接 git URL 或通过本地 pypi 存储库在需求中提供。
  • localflavor 本身做出贡献,并使其可以在那里传递选项 - 这避免了针对这种特定情况的猴子修补。

最重要的是,在 django 邮件列表上讨论猴子修补和迁移可能是值得的 - 但过去对该主题的意见是not that positive

更新:无需仔细查看 localflavor,但在这种情况下,使用它时只需将 choices=MY_STATE_CHOICES 传递给该字段就足够了 - 如果您直接依赖该字段。尽管如此,上述内容总体上解决了猴子修补和 django 迁移。

UPDATE2:我今天和一位同事谈过,他告诉我apps.py 可能是猴子修补工作的地方。那里的代码在其他任何事情发生之前执行 - 也许你可以在那里保存猴子补丁。知道它是否有效会很有趣。

UPDATE3:我最近有一段时间,因为我很好奇,我自己尝试了apps.py 的方法。我的同事完全正确。当您将猴子补丁代码移至 apps.py 迁移时,运行没有您描述的错误。

这就是我的apps.py 的样子:

   from django.apps import AppConfig
   from localflavor.us import models as us_models

   _USStateField = us_models.USStateField

   MY_STATE_CHOICES = [('foo', 'BAR',)]

   class USStateField(_USStateField):

       description = "U.S. state (two uppercase letters)"

       def __init__(self, *args, **kwargs):
           kwargs['choices'] = MY_STATE_CHOICES
           kwargs['max_length'] = 2
           super(_USStateField, self).__init__(*args, **kwargs)

   us_models.USStateField = USStateField

   class FoobarConfig(AppConfig):
       name = 'foobar'

【讨论】:

    猜你喜欢
    • 2013-07-10
    • 2017-08-09
    • 2011-03-31
    • 2013-03-03
    • 2013-02-20
    • 2017-12-26
    • 2018-06-08
    • 1970-01-01
    • 2015-06-08
    相关资源
    最近更新 更多