Search code examples
pythonmysqldjangomysql-workbenchtastypie

MySQL On Update not triggering for Django/TastyPie REST API


We have a resource table which has a field last_updated which we setup with mysql-workbench to have the following properties:

Datatype: TIMESTAMP

NN (NotNull) is checked

Default: CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

When I modify a row through the workbench and apply it, the last_updated field properly updates.

When I use the REST api we've setup, and issue a 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)

I can properly change any of the values (including status and timeout, and receive a 204 response), but last_updated does not update.

Django's model documentation says in this case it should be sending an UPDATE.

Anyone have and ideas on why it's missing these updates?

I can provide further details regarding our specific Django/tastypie setup, but as long as they are issuing an UPDATE, they should be triggering the databases ON UPDATE.


Solution

  • With the added information from spencer7593's answer, I was able to track down how to do this through tastypie:

    The BaseModelResource.save() (from 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
    

    Needs to be overridden in your class, so that you can change the Django save(), which has the update_fields parameter we want to modify:

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

    To, for example:

    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)
            # ...
    

    This properly excludes the last_updated column from the sql UPDATE.