Index ¦ Archives  ¦ Atom  ¦ RSS

Real Offline Django

Over the past few years, one of the projects I use Django for has come across this same conundrum every few months. They want an offline version of the website to show future and current users features of the website and get them to either signup or update their profile.

This feature is very much possible, but requires a lot more time than I can provide so I'm throwing this out there as a challenge. I'm pretty sure I know all the parts that need to be made to allow for this, but first some high-level specifics:

  • Multiple PC OS's (Windows, OSX, Linux), no niche or mobile OS's.
  • Multiple offline versions will be made at the same time and will synchronize at the same time.

The parts that I foresee as being necessary are as follows:

  1. A script that creates a single python file that contains the whole system. (PyInstaller-like)
  2. Code that listens to database signals (init, save, delete) and outputs any changes made to a file.
  3. A second command that takes the output created by those files and synchronizes with the website.

The first part could output two files instead (a python script that runs Django's debug server and then opens that url in the default browser and an egg with the project in it), but mostly it is meant for non-technical people to be able to take the website with them to their events and other gatherings where WiFi is spotty at best.

This may seem similar to django-offline or even djangokit, but this is a different use-case. I want to be able to synchronize the offline versions, which means some wrapper around the sqlite3 database (or any database that can be created from a MySQL one) must be created to output modifications to be synchronized.

Some further thought about each part:

Part 2 Signal Listener:

Update 12/6/10: One similar implementation is available here, but I would personally go with a cleaner implementation using metaclasses for what their use case is. This use case would require a global listener, but the same approach (seen in has_changed, whats_changed, etc) can be used for the global listener.

This turns out to not be a database wrapper since we can instead listen to signals sent by all the models in the project, assuming the project doesn't contain any non-managed models. This page shows the different signals available, and I'm pretty sure that listening to post_init, post_save, and post_delete are sufficient to handle any database-changing code.

Post_init would be used to store the class's field values and post_save would compare its current values to those saved (assuming its a changed model) and store the changes. However, if its a new model, it would ignore the values saved and simply store all the field values (except the pk). And in post_delete we'd just store the deletion of the instance.

Each instance edit/deletion storage would need to be accompanied by a datetime so that they can all be replayed in order when finally synchronized, so here's an initial output file specification:

First line specifies the computer's (ethernet) MAC address to make sure duplicates don't get synchronized.

The second line is just a {

Further lines are all separate statements:

[datetime, action (new/edit/delete), model name, table name, {field name: new value, ...}]

Of course, the inner {} wouldn't be included for delete actions

The last line is a final } (can be omitted by output script and added by input script)

Making it similar to JSON is on purpose, to allow the awesome power of the JSON parsers to be used. Of course, this current specification can be combined into a single line, but I want the output to be human-readable and this is also a more convenient way for outputting each edit as they happen.

For the datetimes, I'm going to arbitrarily say it should be UTC/GMT with this format "%Y-%m-%d %H:%M:%S". Model names can be just the last part where the table name is used as a tie-breaker.

Here is a sample output file:



  ['2010-12-1 12:14:00','new','Blog','blog_blog',{title:'Fahhem\'s Blog',description:'Misc.',date_created:'2010-12-1 12:14:00'}]


© Fahrzin Hemmati. Built using Pelican. Theme by Giulio Fidente on github.