【问题标题】:Issue using fixtures in tests with Django DirtyFields在使用 Django DirtyFields 的测试中使用固定装置的问题
【发布时间】:2013-04-15 23:29:21
【问题描述】:

如果您正在实现 Django DirtyFields,在测试中使用固定装置时是否存在已知问题?

我们将django_noseNoseTestSuiteRunner 用于固定装置和覆盖范围。我打算尝试实现django-dirtyfields 来做一些自定义的post_save() 活动。我所做的唯一更改如下:

from dirtyfields import DirtyFieldsMixin


class BaseModel(DirtyFieldsMixin, models.Model):
    """
    Base model
    """
    class Meta:
        abstract = True

我们使用这个BaseModel 来扩展我们的大多数其他模型。添加DirtyFieldsMixin 会导致我们的测试因以下重复错误而终止:

Problem installing fixture '/home/ricomoss/workspace/project/settings/../apps/contracts/fixtures/test_contracts_data.json': Traceback (most recent call last):
  File "/home/ricomoss/.virtualenvs/project/local/lib/python2.7/site-packages/django/core/management/commands/loaddata.py", line 193, in handle
    for obj in objects:
  File "/home/ricomoss/.virtualenvs/project/local/lib/python2.7/site-packages/django/core/serializers/json.py", line 47, in Deserializer
    raise DeserializationError(e)
DeserializationError: Field matching query does not exist.

据我所知,每当夹具尝试加载时,都会发生此错误。有什么想法吗? DirtyFieldsMixin 不包含任何会导致我的灯具需要更新的新字段(更不用说必填字段了)。

编辑 1: 看来我的问题与 Django 中的fixtures/signal 问题直接相关。如果我们查看DirtyFieldsMixin 定义,我们可以立即看到问题所在(我已经验证了这一点):

class DirtyFieldsMixin(object):
    def __init__(self, *args, **kwargs):
        super(DirtyFieldsMixin, self).__init__(*args, **kwargs)
        post_save.connect(
            self._reset_state, sender=self.__class__,
            dispatch_uid='{}-DirtyFieldsMixin-sweeper'.format(
                self.__class__.__name__))
        self._reset_state()

    def _reset_state(self, *args, **kwargs):
        self._original_state = self._as_dict()

    def _as_dict(self):
        return dict([(f.name, getattr(self, f.name)) for f in
                     self._meta.local_fields if not f.rel])

    def get_dirty_fields(self):
        new_state = self._as_dict()
        return dict([(key, value) for key, value in
                     self._original_state.iteritems()
                     if value != new_state[key]])

    def is_dirty(self):
        # in order to be dirty we need to have been saved at least once, so we
        # check for a primary key and we need our dirty fields to not be empty
        if not self.pk:
            return True
        return {} != self.get_dirty_fields()

具体来说,

post_save.connect(self._reset_state, sender=self.__class__,
                  dispatch_uid='{}-DirtyFieldsMixin-sweeper'.format(
                  self.__class__.__name__))

我尝试按照给定的solution 创建一个装饰器。我要么错误地实现了装饰器,要么在这种情况下不起作用。我对它在这种情况下的使用有点困惑。

我可以把装饰器放在__init__上吗:

@utils.disable_for_loaddata
def __init__(self, *args, **kwargs):
    super(DirtyFieldsMixin, self).__init__(*args, **kwargs)
    post_save.connect(
        self._reset_state, sender=self.__class__,
        dispatch_uid='{}-DirtyFieldsMixin-sweeper'.format(
            self.__class__.__name__))
    self._reset_state()

在这种情况下我仍然失败。有什么建议吗?

编辑 2: 我还尝试将装饰器添加到 _reset_state 方法中。这也不起作用。

@utils.disable_for_loaddata
def _reset_state(self, *args, **kwargs):
    self._original_state = self._as_dict()

编辑 3: 我觉得我在进步。我将装饰器的inspect.stack() 部分直接移动到__init__ 中(就在post_save 调用上方)。

虽然我的测试套件还没有正常运行,但我还有其他活动。我不再收到 loaddata 错误。

def __init__(self, *args, **kwargs):
    super(DirtyFieldsMixin, self).__init__(*args, **kwargs)

    import inspect
    for fr in inspect.stack():
        if inspect.getmodulename(fr[1]) == 'loaddata':
            return
    post_save.connect(
        self._reset_state, sender=self.__class__,
        dispatch_uid='{}-DirtyFieldsMixin-sweeper'.format(
            self.__class__.__name__))
    self._reset_state()

编辑 4: 我决定为这个过程编写自己的 mixin。我不相信我需要为我的目的使用信号。因此,我将覆盖 save() 方法并以特殊方式处理它。

【问题讨论】:

    标签: django unit-testing fixtures django-signals


    【解决方案1】:

    我能够实现我不喜欢的 hack。如果有人提出更好的解决方案,我很乐意看到。

    基本上我做了建议解决方案的装饰器所做的事情,但在堆栈上查找任何测试用例。

    class DirtyFieldsMixin(object):
        def __init__(self, *args, **kwargs):
            super(DirtyFieldsMixin, self).__init__(*args, **kwargs)
    
            # Hack code used to avoid unittest failures due to the following
            # post_save.connect() call.
            import inspect
            for fr in inspect.stack():
                if inspect.getmodulename(fr[1]) == 'testcases':
                    return
            post_save.connect(
                self._reset_state, sender=self.__class__,
                dispatch_uid='{}-DirtyFieldsMixin-sweeper'.format(
                    self.__class__.__name__))
            self._reset_state()
    

    【讨论】:

      【解决方案2】:

      反转继承应该让您无需更改即可启动并运行。

      class BaseModel(models.Model, DirtyFieldsMixin):
      

      【讨论】:

        猜你喜欢
        • 2016-04-01
        • 2018-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-29
        • 2015-07-28
        • 1970-01-01
        相关资源
        最近更新 更多