django-vff 0.2b3
Versioned file field for django models
License: BSD
Keywords: django versioned file field vcs model git

django-vff
==========

Introduction
------------

This package offers a file field for django models, that stores the file contents under a VCS (version control system). Everytime a field of this type is changed for a particular model instance, the new content will be commited as a new version in the repository. Thus, there will be one file in the repository for each vff field and instance. The repository will be at ``settings.VFF_REPO_ROOT``, or, if that is unset, at a ``vf_repo`` subdirectory of django's ``settings.MEDIA_ROOT``.

Different VCSs can be used to manage the repository, using pluggable backends. The package only provides a `GIT <http://git-scm.com>`_ backend out of the box.

Installation
------------

Install django-vff like you would install any other pypi package::

  $ pip install django-vff

You do not need to add anything into Django's ``INSTALLED_APPS``.

Configuration
-------------

You have to set the following variables in django's ``settings.py``:

``VFF_BACKEND``
    A dotted name leading to a backend class, e.g. ``"vff.git_backend.GitBackend"``. This setting is required.

For the git backend:

``VFF_REPO_ROOT``
    Absolute path to the location of the git repository. This repository may or may not exist before setting up django-vff.
``VFF_REPO_PATH``
    Relative path within the git repository to the directory where django-vff keeps its managed files.

If these two settings for the git backend are not set, ``VFF_REPO_ROOT`` will assume a value of ``os.path.join(settings.MEDIA_ROOT, 'vf_repo')``, and ``VFF_REPO_PATH`` will assume a value of ``''``.

Usage
-----

You use it like you would use ``django.db.models.FileField``::

  from django.db import models
  from vff.field import VersionedFileField


  class MyModel(models.Model):
      name = models.CharField('Name', max_length=128)
      content = VersionedFileField(name='content', verbose_name='file content')


Once you have an instance of the ``MyModel`` class, you can use three special methods to list available versions, to get specific versions, and to get diffs between versions:

 * list revisions::

    >>> revs = instance.content.list_revisions()
    >>> from pprint import pprint
    >>> pprint(revs)
    [{'author': u'John Smith',
      'date': datetime.datetime(2011, 6, 16, 13, 25, 30),
      'message': u'second version',
      'versionid': 'a64ea785e195bbf4b3064e6701adbdbf4b5d13be'},
     {'author': u'Martha Brown',
      'date': datetime.datetime(2011, 6, 16, 8, 24, 36),
      'message': u'first version',
      'versionid': '048848a70205d0e18d836f403e2a02830492cbf9'}]

 * get the string content of a specific revision::

    >>> rev1_id = revs[-1]['versionid']
    >>> instance.content.get_revision(rev1_id)
    u'These are the contents of the first version of the file'

 * get the diff between two revisions::

    >>> rev2_id = revs[-2]['versionid']
    >>> print instance.content.get_diff(rev1_id, rev2_id)
    --- 048848a70205d0e18d836f403e2a02830492cbf9
    
    +++ a64ea785e195bbf4b3064e6701adbdbf4b5d13be
    
    @@ -1,1 +1,1 @@
  
    -These are the contents of the first version of the file
    +These are the contents of the second version of the file

Saving and deleting
+++++++++++++++++++

At the moment, saving and deleting has to be explicitly done for this field. So, for example, if you have a model instance with a ``content`` vff field, and a view that uses an edit form with a ``forms.FileField`` for it, after validating the form you would have to do something like::

    name = instance.content.name
    content = form['content'].data
    username = request.user.username
    commit_msg = form['commit_msg'].data.encode('utf8')
    instance.content.save(name, content, username, commit_msg)
    instance.save()

Likewise, when removing an instance, you would::

    username = request.user.username
    commit_msg = u'entity removed'
    instance.content.delete(username, commit_msg)
    instance.delete()

In the future, if there is interest, the package could include a special widget with input space for the necessary data (commit message, etc) so that saving and deleting would be transparent.

Providing new backends
----------------------

To develop a new backend for django-vff, you have to subclass the abstract base class ``vff.abcs.VFFBackend``. The methods that need to be implemented are documented in the docstrings of the class.


CHANGES
=======

0.2b2 (2012-01-25)
------------------

 - Fix typo on README.txt
 - Fix uncaught exception during initialization of GIT repository.

0.2b1 (2012-01-24)
------------------
 - The (git) repo can now be anywhere in the filesystem (not necessarily inside the MEDIA_ROOT), and django-vff can be told to keep its files in a subdirectory iof the repo.

0.1b4 (2011-10-01)
------------------
 - Better fix for error when deleting objects with no file in the repo.

0.1b3 (2011-10-01)
------------------
 - Do not fail deleting objects with no file in the repo.

0.1b2 (2011-09-20)
------------------
 - Remove the files from the repository when deleting objects.
 - Change the signature of VersionedFieldFile's save and delete so they are compatible with django's FieldFile.

0.1b1 (2011-09-02)
------------------
 - Initial version which includes a VersionedFileField and a git backend.