3

I've created a Django/Tastypie application where multiple people could possibly be changing attributes of a row in the database at the exact same time or possibly PUT data which is stale.

For instance:

 # the Django model
 class Thing(models.Model):
      name = models.CharField(max_length=100)
      description = models.TextField()

 # the Tastypie Resource
 class ThingResource(ModelResource):
       class Meta:
           queryset = Thing.objects.all()

Now let's say any number of users could change the name or description of the Thing at any point in time. They could do it at the same time, or someone could leave the application open for a long time and come back and change it then.

What I'm trying to avoid is latent changes that are incorrect. Given the following situation:

#1 User 1: Opens app viewing Thing #1 with name = "my name", description = "my description"
#2 User 2: Opens app viewing Thing #1 (same state as User 1)
#3 User 2: Changes description of Thing #1 to "something"
#4 User 1: Changes name of Thing #1 to "some other name"

After line #4, the state of Thing #1 should be name = "some other name", description = "something" instead of name = "some other name", description = "my description".

Assuming the application does not know (in real time or via regularly updating the data on the page) ahead of time that the object on the server has changed, how can this situation be prevented?

I've considered adding a field sequence = models.PositiveIntegerField() in which I increment it every time an update is made and so I can tell if the object is out of date when the update happens, but is that the best way? Is there a better way? This seems like it would be a common pattern, right?

dlamotte
  • 6,145
  • 4
  • 31
  • 40
  • 1
    Keeping a version number is a widely-accepted good practice. You should of course be using a transactional database of some sort on the back end, or else no such scheme will ensure correct behavior anyway. – Pointy Jan 09 '12 at 22:11
  • Yep, I'm using PostgreSQL. Thanks for pointing that out though – dlamotte Jan 10 '12 at 14:22

2 Answers2

4

With REST, the state of resources is managed by the server, so your idea of a sequence field is spot on. The server keeps track of which version the resource is in, and the client says which version it thinks it’s creating when it sends a new version. If the client is mistaken, the server tells it so.

Ian’s suggestion of ETags or modified dates sounds like the right way to do this, and this answer to a similar question agrees. I don’t actually have much experience of this myself.

Community
  • 1
  • 1
Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270
  • I upvoted for confirming that my method will work. Thanks for linking the other answer, very informative. – dlamotte Jan 10 '12 at 14:40
  • @diamotte: you’re very welcome, I thought the bit about ETags potentially just being version numbers (as opposed to a hash of the content) was pretty interesting. – Paul D. Waite Jan 10 '12 at 17:09
3

The RESTful way to do it is to have the client keep track of either the Last-Modified header or the Etag header returned with the resource. Then, when it POSTs, add a header like

If-Unmodified-Since: <date-noted-from-initial-GET>

or

If-Match: <etag-noted-from-initial-GET>

If the resource has been modified on the server since the client originally downloaded it, then the server should return a "Precondition Failed" response. The client can then retrieve the current version and display something sensible to the user.

Getting tastypie to respond to those headers, though, might be a bit more difficult.

Ian Clelland
  • 43,011
  • 8
  • 86
  • 87
  • Very interesting. I read about Etags a long time ago, but now I understand how they should work. And you're right, it'd be a lot of work to get tastypie to properly respond to these and I'm not sure I'm up for that. Thanks for the answer. – dlamotte Jan 10 '12 at 14:40