MySQL 更新时未触发 Django/TastyPie REST API

MySQL On Update not triggering for Django/TastyPie REST API

我们有一个资源 table,它有一个字段 last_updated,我们用 mysql-workbench 设置它具有以下属性:

数据类型:TIMESTAMP

NN (NotNull) 是 checked

默认:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

当我通过 workbench 修改一行并应用它时,last_updated 字段会正确更新。

当我使用 REST api 时,我们已经设置并发出一个 put:

update = requests.put('http://127.0.0.1:8000/api/resources/16',
            data=json.dumps(dict(status="/api/status/4", timeout=timeout_time)),
            headers=HEADER)

我可以正确更改任何值(包括状态和超时,并收到 204 响应),但 last_updated 没有更新。

Django's model documentation 表示在这种情况下它应该发送 UPDATE.

有人知道为什么缺少这些更新吗?

我可以提供有关我们特定 Django/tastypie 设置的更多详细信息,但只要他们发出 UPDATE,他们就应该触发数据库 ON UPDATE.

我怀疑 Django 发出的 UPDATE 语句可能包含对 last_updated 列的赋值。这只是一个猜测,提供的信息不足。

但是如果 Django 模型包含 last_updated 列,并且该列是从数据库中提取到模型中的,我相信 save() 将分配一个UPDATE 语句中 last_updated 列的值。

https://docs.djangoproject.com/en/1.9/ref/models/instances/#specifying-which-fields-to-save


考虑我们发出如下 UPDATE 语句时的行为:

 UPDATE mytable
    SET last_updated = last_updated
      , some_col = 'some_value'
  WHERE id = 42 

因为 UPDATE 语句正在为 last_updated 列赋值,所以不会自动赋值给 timestamp 列。语句中指定的值优先。

要自动分配给 last_updated,必须从 SET 子句中省略该列,例如

 UPDATE mytable
    SET some_col = 'some_value'
  WHERE id = 42 

要调试它,您需要检查实际的 SQL 语句。

通过 spencer7593 的 添加的信息,我能够通过 tastypie 找到如何做到这一点:

BaseModelResource.save()(来自tastypie/resources.py):

def save(self, bundle, skip_errors=False):
    if bundle.via_uri:
        return bundle

    self.is_valid(bundle)

    if bundle.errors and not skip_errors:
        raise ImmediateHttpResponse(response=self.error_response(bundle.request, bundle.errors))

    # Check if they're authorized.
    if bundle.obj.pk:
        self.authorized_update_detail(self.get_object_list(bundle.request), bundle)
    else:
        self.authorized_create_detail(self.get_object_list(bundle.request), bundle)

    # Save FKs just in case.
    self.save_related(bundle)

    # Save the main object.
    obj_id = self.create_identifier(bundle.obj)

    if obj_id not in bundle.objects_saved or bundle.obj._state.adding:
        bundle.obj.save()
        bundle.objects_saved.add(obj_id)

    # Now pick up the M2M bits.
    m2m_bundle = self.hydrate_m2m(bundle)
    self.save_m2m(m2m_bundle)
    return bundle

需要在您的 class 中覆盖,以便您可以更改 Django save(),其中包含我们要修改的 update_fields 参数:

    if obj_id not in bundle.objects_saved or bundle.obj._state.adding:
        bundle.obj.save()

例如:

class ResourcesResource(ModelResource):
    # ...
    def save(self, bundle, skip_errors=False):
        # ...
        if obj_id not in bundle.objects_saved or bundle.obj._state.adding:
            resource_fields = [field.name for field in Resources._meta.get_fields() 
                               if not field.name in ['id', 'last_updated']]
            bundle.obj.save(update_fields=resource_fields)
        # ...

这正确地从 sql UPDATE 中排除了 last_updated 列。