= django-reversion-compare =

**django-reversion-compare** is an extension to [[https://github.com/etianen/django-reversion/|django-reversion]] that provides a history compare view to compare two versions of a model which is under reversion.

Comparing model versions is not a easy task. Maybe there are different view how this should looks like.
This project will gives you a generic way to see whats has been changed.

Many parts are customizable by overwrite methods or subclassing, see above.

| {{https://coveralls.io/repos/jedie/django-reversion-compare/badge.svg|Coverage Status on coveralls.io}} | [[https://coveralls.io/r/jedie/django-reversion-compare|coveralls.io/r/jedie/django-reversion-compare]] |
| {{https://travis-ci.org/jedie/django-reversion-compare.svg|Build Status on travis-ci.org}} | [[https://travis-ci.org/jedie/django-reversion-compare/|travis-ci.org/jedie/django-reversion-compare]] |


== Requirements ==

* Django 1.7.x
* [[https://github.com/jedie/django-tools/|django-tools]] (**only** for running the unittests!)

== installation ==

Just use:
{{{
pip install django-reversion-compare
}}}

Optional you can install [[https://code.google.com/p/google-diff-match-patch/|google-diff-match-patch]] otherwise difflib would be used.
The easiest way it to use the unofficial package [[http://pypi.python.org/pypi/diff-match-patch/|diff-match-patch]], e.g.:
{{{
pip install diff-match-patch
}}}

=== setup ===

Add **reversion_compare** to **INSTALLED_APPS** in your settings.py, e.g.:
{{{
INSTALLED_APPS = (
    'django...',
    ...
    'reversion', # https://github.com/etianen/django-reversion
    'reversion_compare', # https://github.com/jedie/django-reversion-compare
    ...
)

# Add reversion models to admin interface:
# ADD_REVERSION_ADMIN=True
}}}

=== usage ===

Inherit from **CompareVersionAdmin** instead of **VersionAdmin** to get the compare feature.

admin.py e.g.:
{{{
from django.contrib import admin
from reversion_compare.admin import CompareVersionAdmin

from my_app.models import ExampleModel

class ExampleModelAdmin(CompareVersionAdmin):
    pass

admin.site.register(ExampleModel, ExampleModelAdmin)
}}}

If you're using an existing third party app, then you can add patch django-reversion-compare into 
its admin class by using the **reversion_compare.helpers.patch_admin()** method. For example, to add 
version control to the built-in User model:

{{{
from reversion_compare.helpers import patch_admin

patch_admin(User)
}}}

e.g.: Add django-cms Page model:
{{{
from cms.models.pagemodel import Page
from reversion_compare.helpers import patch_admin


# Patch django-cms Page Model to add reversion-compare functionality:
patch_admin(Page)
}}}


=== customize ===

It's possible to change the look for every field or for a entire field type.
You must only define a methods to your admin class with this name scheme:

* {{{ "compare_%s" % field_name }}}
* {{{ "compare_%s" % field.get_internal_type() }}}

If there exist no method with this name scheme, the {{{ fallback_compare() }}} method would be used.

example for specify a compare method for a model field by name:

{{{
class YourAdmin(CompareVersionAdmin):
    def compare_foo_bar(self, obj_compare):
        """ compare the foo_bar model field """
        return "%r <-> %r" % (obj_compare.value1, obj_compare.value2)
}}}

and example using **patch_admin** with custom version admin class:
{{{
patch_admin(User, AdminClass=YourAdmin)
}}}


== screenshots ==

Here some screenshots of django-reversion-compare: 

----

How to select the versions to compare:

{{http://www.pylucid.org/static/pylucid.org/screenshots_PyLucid/django-reversion/django-reversion-compare_v0_1_0-01.png|django-reversion-compare_v0_1_0-01.png}}

----

from **v0.1.0**: DateTimeField compare (last update), TextField compare (content) with small changes and a ForeignKey compare (child model instance was added):

{{http://www.pylucid.org/static/pylucid.org/screenshots_PyLucid/django-reversion/django-reversion-compare_v0_1_0-02.png|django-reversion-compare_v0_1_0-02.png}}

----

from **v0.1.0**: Same as above, but the are more lines changed in TextField and the ForeignKey relation was removed:

{{http://www.pylucid.org/static/pylucid.org/screenshots_PyLucid/django-reversion/django-reversion-compare_v0_1_0-03.png|django-reversion-compare_v0_1_0-03.png}}

----

Example screenshot from **v0.3.0**: a many-to-many field compare (friends, hobbies): 

{{http://www.pylucid.org/static/pylucid.org/screenshots_PyLucid/django-reversion/django-reversion-compare_v0_3_0-01.png|django-reversion-compare_v0_3_0-01.png}}

* In the first line, the m2m object has been changed.
* line 2: A m2m object was deleted
* line 3: A m2m object was removed from this entry (but not deleted)
* line 4: This m2m object has not changed

== unittests ==

=== run unittests ===

via setup.py:
{{{
$ cd path/to/django-reversion-compare
django-reversion-compare$ ./setup.py test
}}}

via runtests.py:
{{{
$ cd path/to/django-reversion-compare
django-reversion-compare$ ./runtests.py
}}}


Helpfull for writing and debugging unittests is to run a local test server with the same data.
e.g.:
{{{
~$ cd path/to/django-reversion-compare/
/django-reversion-compare$ ./tests/manage.py run_testserver
}}}
**migration** will be run and a superuser will be created. Username: **test** Password: **12345678**


== Version compatibility

|= Reversion-Compare |= Django |= Python
|>=v0.4              | v1.7    | v2.7, v3.4
|<v0.4               | v1.4

(These are the unittests variants. Maybe other versions are compatible, too.)

== changelog ==

* v0.5.0 - 27.02.2015:
** refactory unittests, test with Django v1.7 and Python 2.7 & 3.4
* v0.4.0 - 02.02.2015:
** Updates for django 1.7 support
** Add {{{settings.ADD_REVERSION_ADMIN}}}
* v0.3.5 - 03.01.2013:
** Remove date from version string. [[https://github.com/jedie/django-reversion-compare/issues/9|issues 9]]
* v0.3.4 - 20.06.2012:
** Use VersionAdmin.revision_manager rather than default_revision_manager, contributed by Mark Lavin - see: [[https://github.com/jedie/django-reversion-compare/pull/7|pull request 7]]
** Use logging for all debug prints, contributed by Bojan Mihelac - see: [[https://github.com/jedie/django-reversion-compare/pull/8|pull request 8]]
* v0.3.3 - 11.06.2012:
** Bugfix "ValueError: zero length field name in format" with Python 2.6 [[https://github.com/jedie/django-reversion-compare/issues/5|issues 5]]
* v0.3.2 - 04.06.2012:
** Bugfix for Python 2.6 in unified_diff(), see: [[https://github.com/jedie/django-reversion-compare/issues/5|AttributeError: 'module' object has no attribute '_format_range_unified']]
* v0.3.1 - 01.06.2012:
** Bugfix: force unicode in html diff
** Bugfix in unittests
* v0.3.0 - 16.05.2012:
** Enhanced handling of m2m changes with follow and non-follow relations.
* v0.2.2 - 15.05.2012:
** Compare many-to-many in the right way.
* v0.2.1 - 10.05.2012:
** Bugfix for models which has no m2m field: https://github.com/jedie/django-reversion-compare/commit/c8e042945a6e78e5540b6ae27666f9b0cfc94880
* v0.2.0 - 09.05.2012:
** many-to-many compare works, too.
* v0.1.0 - 08.05.2012:
** First release
* v0.0.1 - 08.05.2012:
** collect all compare stuff from old "diff" branch
** see also:  https://github.com/etianen/django-reversion/issues/147


== Links ==

| IRC                 | [[http://www.pylucid.org/permalink/304/irc-channel|#pylucid on freenode.net]]
| Github              | [[http://github.com/jedie/django-reversion-compare]]
| Python Packages     | [[http://pypi.python.org/pypi/django-reversion-compare/]]

=== contact ===

Come into the conversation, besides the github communication features:

| Forum   | [[http://www.pylucid.org/en/forum/13/|official 'django-reversion-compare' Forum]]
| IRC     | #pylucid on freenode.net (Yes, the PyLucid channel...)
| webchat | http://webchat.freenode.net/?channels=pylucid
