Import django-reversion_2.0.12.orig.tar.gz

[dgit import orig django-reversion_2.0.12.orig.tar.gz]
This commit is contained in:
Michael Fladischer 2017-12-08 19:55:32 +01:00
commit 7ddda91ed2
115 changed files with 8281 additions and 0 deletions

21
.coveragerc Normal file
View File

@ -0,0 +1,21 @@
[run]
source =
reversion
test_app
test_project
[report]
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
assert False
# Don't complain if tests don't hit model __str__ methods.
def __str__
show_missing = True
skip_covered = True

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
.DS_Store
*.db
.project
.pydevproject
.settings
*.pyc
*.pyo
dist
build
MANIFEST
*.egg-info/
docs/_build
.coverage
*.sqlite3
.tox

39
.travis.yml Normal file
View File

@ -0,0 +1,39 @@
sudo: false
language: python
python:
- 3.6
addons:
apt:
packages:
- libmysqlclient-dev
cache:
directories:
- "$HOME/.cache/pip"
env:
global:
- PYTHONWARNINGS=default,ignore::PendingDeprecationWarning,ignore::ResourceWarning
- DJANGO_DATABASE_USER_POSTGRES=postgres
- DJANGO_DATABASE_USER_MYSQL=travis
matrix:
fast_finish: true
services:
- postgresql
- mysql
install:
- pyenv shell 2.7 3.5 3.6
- pip install 'tox>=2.3.1'
before_script:
- mysql -e 'create database test_project'
- psql -c 'create database test_project;' -U postgres;
script: tox
deploy:
provider: pypi
user: etianen
password:
secure: XW4/9HiChbPJSJe4d/MRcO+ViPGhW1iQ8kVi814KJh7mCxOAKijpW5hfdc9oSKB6d8iYB3OzZ7naIUU9GMce40bpeTgPDLVBLCSYKRNLuVoJdh+Q6ItGUiFf8kAJz5jgopG80QnCpLA9JvYxKVJ4amfYWWm204eQmIEnRRAd+Jk=
on:
tags: true
distributions: sdist bdist_wheel
repo: etianen/django-reversion
notifications:
email: false

686
CHANGELOG.rst Normal file
View File

@ -0,0 +1,686 @@
.. _changelog:
django-reversion changelog
==========================
2.0.12 - 05/12/2017
-------------------
- Fixed MySQL error in ``get_deleted()``.
2.0.11 - 27/11/2017
-------------------
- Dramatically improved performance of ``get_deleted()`` over large datasets (@alexey-v-paramonov, @etianen).
- Ukranian translation (@illia-v).
- Bugfixes (@achidlow, @claudep, @etianen).
2.0.10 - 18/08/2017
-------------------
- Bugfix: Handling case of `None` user in request (@pawelad).
- Documentation corrections (@danielquinn).
- Bugfix: "invalid literal for int() with base 10: 'None'" for unversioned admin inline relations.
If, after updating, you still experience this issue, run the following in a Django shell:
.. code::
from reversion.models import Version
Version.objects.filter(object_id="None").delete()
**Important:** Ensure that none of your versioned models contain a string primary key where `"None"` is a valid value
before running this snippet!
2.0.9 - 19/06/2017
------------------
- Bugfix: Deleted inline admin instances no longer added to revision.
- Bugfix: M2M relations correctly added to revision (@etianen, @claudep).
- Improved performance of 0003 migration (@mkurek).
- Documentation improvements (@orlra, @guettli, @meilinger).
- Django 1.11 support (@claudep).
- Added ``atomic=True`` parameter to ``create_revision`` (Ernesto Ferro).
2.0.8 - 28/11/2016
------------------
- Setting ``revision.user`` in ``process_response`` for middleware (@etianen).
- Fixing localization of model primary keys in `recover_list.html` (@w4rri0r3k).
- Documentation tweaks (@jaywink).
2.0.7 - 31/10/2016
------------------
- Database migrations now db-aware (@alukach).
- Added "revert" and "recover" context variables to admin templates (@kezabelle).
- Added ``post_revision_commit`` and ``pre_revision_commit`` signals back in (@carlosxl).
- Fixing datetime in admin change message (@arogachev).
- Fixing performance bug in postgres (@st4lk).
- Fixing admin change messages in Django 1.10+ (@claudep).
- Fixing revision middleware behavior in Django 1.10+ (@etianen).
- Documentation tweaks (@jschneier).
- Deprecation fixes (@KhasanovBI, @zsiciarz, @claudep).
- Releasing as a universal wheel (@adamchainz).
2.0.6 - 21/07/2016
------------------
- Fixed ``RevisionMiddleware`` always rolling back transactions in gunicorn (@stebunovd, @etianen).
- Tweaks and minor bugfixes (@SahilMak).
2.0.5 - 29/06/2016
------------------
- Fixed LookupError when running migration 0003 with stale content types (@etianen).
2.0.4 - 20/06/2016
------------------
- Fixed LookupError when running migration 0003 (@etianen).
- Fixed duplicate versions using ``get_deleted()`` (@etianen).
- Fixed unexpected deletion of underflowing revisions when using ``--keep`` switch with ``deleterevisions`` (@etianen).
2.0.3 - 14/06/2016
------------------
- Added support for m2m fields with a custom ``through`` model (@etianen).
2.0.2 - 13/06/2016
------------------
- Fixing migration 0003 in MySQL (@etianen).
2.0.1 - 13/06/2016
------------------
- Improved performance of migration 0003 (@BertrandBordage).
- De-duplicating ``Version`` table before applying migration 0004 (@BertrandBordage, @etianen).
2.0.0 - 11/06/2016
------------------
django-reversion was first released in May 2008, and has been in active development ever since. Over this time it's developed a certain amount of cruft from legacy and unused features, resulting in needless complexity and multiple ways of achieving the same task.
This release substantially cleans and refactors the codebase. Much of the top-level functionality remains unchanged or is very similar. The release notes are divided into subsections to make it easier to find out where you need to update your code.
This release includes a migration for the ``Version`` model that may take some time to complete.
General improvements
^^^^^^^^^^^^^^^^^^^^
* Dramatically improved performance of version lookup for models with a non-integer primary key (@etianen, @mshannon1123).
* Documentation refactor (@etianen).
* Test refactor (@etianen).
* Minor tweaks and bugfixes (@etianen, @bmarika, @ticosax).
Admin
^^^^^
* Fixed issue with empty revisions being created in combination with ``RevisionMiddleware`` (@etianen).
* **Breaking:** Removed ``reversion_format`` property from ``VersionAdmin`` (@etianen).
Use ``VersionAdmin.reversion_register`` instead.
.. code::
class YourVersionAdmin(VersionAdmin):
def reversion_register(self, model, **options):
options["format"] = "yaml"
super(YourVersionAdmin, self).reversion_register(model, **options)
* **Breaking:** Removed ``ignore_duplicate_revisions`` property from ``VersionAdmin`` (@etianen).
Use ``VersionAdmin.reversion_register`` instead.
.. code::
class YourVersionAdmin(VersionAdmin):
def reversion_register(self, model, **options):
options["ignore_duplicate_revisions"] = True
super(YourVersionAdmin, self).reversion_register(model, **options)
Management commands
^^^^^^^^^^^^^^^^^^^
* **Breaking:** Refactored arguments to ``createinitialrevisions`` (@etianen).
All existing functionality should still be supported, but several parameter names have been updated to match Django coding conventions.
Check the command ``--help`` for details.
* **Breaking:** Refactored arguments to ``deleterevisions`` (@etianen).
All existing functionality should still be supported, but several parameter names have been updated to match Django coding conventions, and some duplicate parameters have been removed. The confirmation prompt has been removed entirely, and the command now always runs in the ``--force`` mode from the previous version.
Check the command ``--help`` for details.
Middleware
^^^^^^^^^^
* Added support for using ``RevisionMiddleware`` with new-style Django 1.10 ``MIDDLEWARE`` (@etianen).
* Middleware wraps entire request in ``transaction.atomic()`` to preserve transactional integrity of revision and models (@etianen).
View helpers
^^^^^^^^^^^^
* Added ``reversion.views.create_revision`` view decorator (@etianen).
* Added ``reversion.views.RevisionMixin`` class-based view mixin (@etianen).
Low-level API
^^^^^^^^^^^^^
* Restored many of the django-reversion API methods back to the top-level namespace (@etianen).
* Revision blocks are now automatically wrapped in ``transaction.atomic()`` (@etianen).
* Added ``for_concrete_model`` argument to ``reversion.register()`` (@etianen).
* Added ``Version.objects.get_for_model()`` lookup function (@etianen).
* Added ``reversion.add_to_revision()`` for manually adding model instances to an active revision (@etianen).
* Removed ``Version.object_id_int`` field, in favor of a unified ``Version.object_id`` field for all primary key types (@etianen).
* **Breaking:** ``reversion.get_for_object_reference()`` has been moved to ``Version.objects.get_for_object_reference()`` (@etianen).
* **Breaking:** ``reversion.get_for_object()`` has been moved to ``Version.objects.get_for_object()`` (@etianen).
* **Breaking:** ``reversion.get_deleted()`` has been moved to ``Version.objects.get_deleted()`` (@etianen).
* **Breaking:** ``Version.object_version`` has been renamed to ``Version._object_version`` (@etianen).
* **Breaking:** Refactored multi-db support (@etianen).
django-reversion now supports restoring model instances to their original database automatically. Several parameter names have also be updated to match Django coding conventions.
If you made use of the previous multi-db functionality, check the latest docs for details. Otherwise, everything should *just work*.
* **Breaking:** Removed ``get_ignore_duplicates`` and ``set_ignore_duplicates`` (@etianen).
``ignore_duplicates`` is now set in reversion.register() on a per-model basis.
* **Breaking:** Removed ``get_for_date()`` function (@etianen).
Use ``get_for_object().filter(revision__date_created__lte=date)`` instead.
* **Breaking:** Removed ``get_unique_for_object()`` function (@etianen).
Use ``get_for_object().get_unique()`` instead.
* **Breaking:** Removed ``signal`` and ``eager_signals`` argument from ``reversion.register()`` (@etianen).
To create revisions on signals other than ``post_save`` and ``m2m_changed``, call ``reversion.add_to_revision()`` in a signal handler for the appropriate signal.
.. code:: python
from django.dispatch import receiver
import reversion
from your_app import your_custom_signal
@reciever(your_custom_signal)
def your_custom_signal_handler(instance, **kwargs):
if reversion.is_active():
reversion.add_to_revision(instance)
This approach will work for both eager and non-eager signals.
* **Breaking:** Removed ``adapter_cls`` argument from ``reversion.register()`` (@etianen).
* **Breaking:** Removed ``reversion.save_revision()`` (@etianen).
Use reversion.add_to_revision() instead.
.. code:: python
import reversion
with reversion.create_revision():
reversion.add_to_revision(your_obj)
Signals
^^^^^^^
* **Breaking:** Removed ``pre_revision_commit`` signal (@etianen).
Use the Django standard ``pre_save`` signal for ``Revision`` instead.
* **Breaking:** Removed ``post_revision_commit`` signal (@etianen).
Use the Django standard ``post_save`` signal for ``Revision`` instead.
Helpers
^^^^^^^
* **Breaking:** Removed ``patch_admin`` function (@etianen).
Use ``VersionAdmin`` as a mixin to 3rd party ModelAdmins instead.
.. code::
@admin.register(SomeModel)
class YourModelAdmin(VersionAdmin, SomeModelAdmin):
pass
* **Breaking:** Removed ``generate_diffs`` function (@etianen).
django-reversion no supports an official diff helper. There are much better ways of achieving this now, such as `django-reversion-compare <https://github.com/jedie/django-reversion-compare>`_.
The old implementation is available for reference from the `previous release <https://github.com/etianen/django-reversion/blob/release-1.10.2/src/reversion/helpers.py>`_.
* **Breaking:** Removed ``generate_patch`` function (@etianen).
django-reversion no supports an official diff helper. There are much better ways of achieving this now, such as `django-reversion-compare <https://github.com/jedie/django-reversion-compare>`_.
The old implementation is available for reference from the `previous release <https://github.com/etianen/django-reversion/blob/release-1.10.2/src/reversion/helpers.py>`_.
* **Breaking:** Removed ``generate_patch_html`` function (@etianen).
django-reversion no supports an official diff helper. There are much better ways of achieving this now, such as `django-reversion-compare <https://github.com/jedie/django-reversion-compare>`_.
The old implementation is available for reference from the `previous release <https://github.com/etianen/django-reversion/blob/release-1.10.2/src/reversion/helpers.py>`_.
Models
^^^^^^
* **Breaking:** Ordering of ``-pk`` added to models ``Revision`` and ``Version``. Previous was the default ``pk``.
1.10.2 - 18/04/2016
-------------------
* Fixing deprecation warnings (@claudep).
* Minor tweaks and bug fixes (@fladi, @claudep, @etianen).
1.10.1 - 27/01/2016
-------------------
* Fixing some deprecation warnings (@ticosax).
* Minor tweaks (@claudep, @etianen).
1.10 - 02/12/2015
-----------------
* **Breaking:** Updated the location of ``VersionAdmin``.
Prior to this change, you could access the ``VersionAdmin`` class using the following import:
.. code:: python
# Old-style import for accessing the admin class.
import reversion
# Access admin class from the reversion namespace.
class YourModelAdmin(reversion.VersionAdmin):
pass
In order to support Django 1.9, the admin class has been moved to the following
import:
.. code:: python
# New-style import for accesssing admin class.
from reversion.admin import VersionAdmin
# Use the admin class directly.
class YourModelAdmin(VersionAdmin):
pass
* **Breaking:** Updated the location of low-level API methods.
Prior to this change, you could access the low-level API using the following import:
.. code:: python
# Old-style import for accessing the low-level API.
import reversion
# Use low-level API methods from the reversion namespace.
@reversion.register
class YourModel(models.Model):
pass
In order to support Django 1.9, the low-level API
methods have been moved to the following import:
.. code:: python
# New-style import for accesssing the low-level API.
from reversion import revisions as reversion
# Use low-level API methods from the revisions namespace.
@reversion.register
class YourModel(models.Model):
pass
* **Breaking:** Updated the location of http://django-reversion.readthedocs.org/en/latest/signals.html.
Prior to this change, you could access the reversion signals using the following import:
.. code:: python
# Old-style import for accessing the reversion signals
import reversion
# Use signals from the reversion namespace.
reversion.post_revision_commit.connect(...)
In order to support Django 1.9, the reversion signals have been moved to the following
import:
.. code:: python
# New-style import for accesssing the reversion signals.
from reversion.signals import pre_revision_commit, post_revision_commit
# Use reversion signals directly.
post_revision_commit.connect(...)
* Django 1.9 compatibility (@etianen).
* Added spanish (argentina) translation (@gonzalobustos).
* Minor bugfixes and tweaks (@Blitzstok, @IanLee1521, @lutoma, @siamalekpour, @etianen).
1.9.3 - 07/08/2015
------------------
* Fixing regression with admin redirects following save action (@etianen).
1.9.2 - 07/08/2015
------------------
* Fixing regression with "delete", "save as new" and "save and continue" button being shown in recover and revision admin views (@etianen).
* Fixing regression where VersionAdmin.ignore_duplicate_revisions was ignored (@etianen).
1.9.1 - 04/08/2015
------------------
* Fixing packaging error that rendered the 1.9.0 release unusable. No way to cover up the mistake, so here's a brand new bugfix release! (@etianen).
1.9.0 - 04/08/2015
------------------
* Using database transactions do render consistent views of past revisions in database admin, fixing a lot of lingering minor issues (@etianen).
* Correct handling of readonly fields in admin (@etianen).
* Updates to Czech translation (@cuchac).
* Arabic translation (@RamezIssac).
* Fixing deleterevisions to work with Python2 (@jmurty).
* Fixing edge-cases where an object does not have a PK (@johnfraney).
* Tweaks, code cleanups and documentation fixes (@claudep, @johnfraney, @podloucky-init, Drew Hubl, @JanMalte, @jmurty, @etianen).
1.8.7 - 21/05/2015
------------------
* Fixing deleterevisions command on Python 3 (@davidfsmith).
* Fixing Django 1.6 compatibility (@etianen).
* Removing some Django 1.9 deprecation warnings (@BATCOH, @niknokseyer).
* Minor tweaks (@nikolas, @etianen).
1.8.6 - 13/04/2015
------------------
* Support for MySQL utf8mb4 (@alexhayes).
* Fixing some Django deprecation warnings (Drew Hubl, @khakulov, @adonm).
* Versions passed through by reversion.post_revision_commit now contain a primary key (@joelarson).
1.8.5 - 31/10/2014
------------------
* Added support for proxy models (@AgDude, @bourivouh).
* Allowing registration of models with django-reversion using custom signals (@ErwinJunge).
* Fixing some Django deprecation warnings (@skipp, @narrowfail).
1.8.4 - 07/09/2014
------------------
* Fixing including legacy south migrations in PyPi package (@GeyseR).
1.8.3 - 06/09/2014
------------------
* Provisional Django 1.7 support (@etianen).
* Multi-db and multi-manager support to management commands (@marekmalek).
* Added index on reversion.date_created (@rkojedzinszky).
* Minor bugfixes and documentation improvements (@coagulant).
1.8.2 - 01/08/2014
------------------
* reversion.register() can now be used as a class decorator (@aquavitae).
* Danish translation (@Vandborg).
* Improvements to Travis CI integration (@thedrow).
* Simplified Chinese translation (@QuantumGhost).
* Minor bugfixes and documentation improvements (@marekmalek, @dhoffman34, @mauricioabreu, @mark0978).
1.8.1 - 29/05/2014
------------------
* Slovak translation (@jbub).
* Deleting a user no longer deletes the associated revisions (@daaray).
* Improving handling of inline models in admin integration (@blueyed).
* Improving error messages for proxy model registration (@blueyed).
* Improvements to using migrations with custom user model (@aivins).
* Removing sys.exit() in deleterevisions management command, allowing it to be used internally by Django projects (@tongwang).
* Fixing some backwards-compatible admin deprecation warnings (Thomas Schreiber).
* Fixing tests if RevisionMiddleware is used as a decorator in the parent project (@jmoldow).
* Derived models, such as those generated by deferred querysets, now work.
* Removed deprecated low-level API methods.
1.8.0 - 01/11/2013
------------------
* Django 1.6 compatibility (@niwibe & @meshy).
* Removing type flag from Version model.
* Using bulk_create to speed up revision creation.
* Including docs in source distribution (@pquentin & @fladi).
* Spanish translation (@alexander-ae).
* Fixing edge-case bugs in revision middleware (@pricem & @oppianmatt).
1.7.1 - 26/06/2013
------------------
* Bugfixes when using a custom User model.
* Minor bugfixes.
1.7 - 27/02/2013
----------------
* Django 1.5 compatibility.
* Experimantal Python 3.3 compatibility!
1.6.6 - 12/02/2013
------------------
* Removing version checking code. It's more trouble than it's worth.
* Dutch translation improvements.
1.6.5 - 12/12/2012
------------------
* Support for Django 1.4.3.
1.6.4 - 28/10/2012
------------------
* Support for Django 1.4.2.
1.6.3 - 05/09/2012
------------------
* Fixing issue with reverting models with unique constraints in the admin.
* Enforcing permissions in admin views.
1.6.2 - 31/07/2012
------------------
* Batch saving option in createinitialrevisions.
* Suppressing warning for Django 1.4.1.
1.6.1 - 20/06/2012
------------------
* Swedish translation.
* Fixing formating for PyPi readme and license.
* Minor features and bugfixes.
1.6 - 27/03/2012
----------------
* Django 1.4 compatibility.
1.5.2 - 27/03/2012
------------------
* Multi-db support.
* Brazillian Portuguese translation.
* New manage_manually revision mode.
1.5.1 - 20/10/2011
-------------------
* Polish translation.
* Minor bug fixes.
1.5 - 04/09/2011
----------------
* Added in simplified low level API methods, and deprecated old low level API methods.
* Added in support for multiple revision managers running in the same project.
* Added in significant speedups for models with integer primary keys.
* Added in cleanup improvements to patch generation helpers.
* Minor bug fixes.
1.4 - 27/04/2011
----------------
* Added in a version flag for add / change / delete annotations.
* Added experimental deleterevisions management command.
* Added a --comment option to createinitialrevisions management command.
* Django 1.3 compatibility.
1.3.3 - 05/03/2011
------------------
* Improved resilience of revert() to database integrity errors.
* Added in Czech translation.
* Added ability to only save revisions if there is no change.
* Fixed long-running bug with file fields in inline related admin models.
* Easier debugging for createinitialrevisions command.
* Improved compatibility with Oracle database backend.
* Fixed error in MySQL tests.
* Greatly improved performance of get_deleted() Version manager method.
* Fixed an edge-case UnicodeError.
1.3.2 - 22/10/2010
------------------
* Added Polish translation.
* Added French translation.
* Improved resilience of unit tests.
* Improved scaleability of Version.object.get_deleted() method.
* Improved scaleability of createinitialrevisions command.
* Removed post_syncdb hook.
* Added new createinitialrevisions management command.
* Fixed DoesNotExistError with OneToOneFields and follow.
1.3.1 - 31/05/2010
------------------
This release is compatible with Django 1.2.1.
* Django 1.2.1 admin compatibility.
1.2.1 - 03/03/2010
------------------
This release is compatible with Django 1.1.1.
* The django syncdb command will now automatically populate any
version-controlled models with an initial revision. This ensures existing
projects that integrate Reversion won't get caught out.
* Reversion now works with SQLite for tables over 999 rows.
* Added Hebrew translation.
1.2 - 12/10/2009
----------------
This release is compatible with Django 1.1.
* Django 1.1 admin compatibility.
1.1.2 - 23/07/2009
------------------
This release is compatible with Django 1.0.4.
* Doc tests.
* German translation update.
* Better compatibility with the Django trunk.
* The ability to specify a serialization format used by the ReversionAdmin
class when models are auto-registered.
* Reduction in the number of database queries performed by the Reversion
* admin interface.
1.1.1 - 25/03/2010
------------------
This release is compatible with Django 1.0.2.
* German and Italian translations.
* Helper functions for generating diffs.
* Improved handling of one-to-many relationships in the admin.

27
LICENSE Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009, David Hall.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of David Hall nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

9
MANIFEST.in Normal file
View File

@ -0,0 +1,9 @@
include reversion/templates/reversion/*.html
include reversion/locale/*/LC_MESSAGES/django.*
include LICENSE
include README.rst
include CHANGELOG.rst
include MANIFEST.in
recursive-include docs *
recursive-include tests *.py
prune docs/_build

60
README.rst Normal file
View File

@ -0,0 +1,60 @@
django-reversion
================
**django-reversion** is an extension to the Django web framework that provides
version control for model instances.
Features
--------
- Roll back to any point in a model instance's history.
- Recover deleted model instances.
- Simple admin integration.
Documentation
-------------
Please read the `Getting Started <https://django-reversion.readthedocs.io/>`_
guide for more information.
Issue tracking and source code can be found at the
`main project website <http://github.com/etianen/django-reversion>`_.
You can keep up to date with the latest announcements by joining the
`django-reversion discussion group <http://groups.google.com/group/django-reversion>`_.
Upgrading
---------
Please check the `Changelog <https://github.com/etianen/django-reversion/blob/master/CHANGELOG.rst>`_ before upgrading
your installation of django-reversion.
Contributing
------------
Bug reports, bug fixes, and new features are always welcome. Please raise issues on the
`django-reversion project site <http://github.com/etianen/django-reversion>`_, and submit
pull requests for any new code.
You can run the test suite yourself from within a virtual environment with the following
commands. The test suite requires that both MySQL and PostgreSQL be installed.
.. code:: bash
pip install 'tox>=2.3.1'
tox
The django-reversion project is built on every push with `Travis CI <https://travis-ci.org/etianen/django-reversion>`_.
.. image:: https://travis-ci.org/etianen/django-reversion.svg?branch=master
:target: https://travis-ci.org/etianen/django-reversion
Contributors
------------
The django-reversion project was developed by `Dave Hall <http://www.etianen.com/>`_ and contributed
to by `many other people <https://github.com/etianen/django-reversion/graphs/contributors>`_.

13
docs/_include/admin.rst Normal file
View File

@ -0,0 +1,13 @@
Register your models with a subclass of :ref:`VersionAdmin`.
.. code:: python
from django.contrib import admin
from reversion.admin import VersionAdmin
@admin.register(YourModel)
class YourModelAdmin(VersionAdmin):
pass
.. include:: /_include/post-register.rst

View File

@ -0,0 +1,8 @@
``manage_manually``
.. include:: /_include/create-revision-manage-manually.rst
``using``
.. include:: /_include/create-revision-using.rst
``atomic``
.. include:: /_include/create-revision-atomic.rst

View File

@ -0,0 +1 @@
If ``True``, the revision block will be wrapped in a ``transaction.atomic()``.

View File

@ -0,0 +1 @@
If ``True``, versions will not be saved when a model's ``save()`` method is called. This allows version control to be switched off for a given revision block.

View File

@ -0,0 +1 @@
The database to save the revision data. The revision block will be wrapped in a transaction using this database. If ``None``, the default database for :ref:`Revision` will be used.

View File

@ -0,0 +1,2 @@
``model_db``
The database where the model is saved. Defaults to the default database for the model.

View File

@ -0,0 +1,2 @@
.. Hint::
Whenever you register a model with django-reversion, run :ref:`createinitialrevisions`.

View File

@ -0,0 +1,8 @@
``sender``
The ``reversion.create_revision`` object.
``revision``
The :ref:`Revision` model.
``versions``
The :ref:`Version` models in the revision.

View File

@ -0,0 +1 @@
Throws :ref:`RegistrationError` if the model has not been registered with django-reversion.

View File

@ -0,0 +1 @@
Throws :ref:`RevertError` if the model could not be deserialized or reverted, e.g. the serialized data is not compatible with the current database schema.

View File

@ -0,0 +1 @@
Throws :ref:`RevisionManagementError` if there is no active revision block.

109
docs/admin.rst Normal file
View File

@ -0,0 +1,109 @@
.. _admin:
Admin integration
=================
django-reversion can be used to add rollback and recovery to your admin site.
.. Warning::
The admin integration requires that your database engine supports transactions. This is the case for PostgreSQL, SQLite and MySQL InnoDB. If you are using MySQL MyISAM, upgrade your database tables to InnoDB!
Overview
--------
Registering models
^^^^^^^^^^^^^^^^^^
.. include:: /_include/admin.rst
.. Note::
If you've registered your models using :ref:`reversion.register() <register>`, the admin class will use the configuration you specify there. Otherwise, the admin class will auto-register your model, following all inline model relations and parent superclasses. Customize the admin registration by overriding :ref:`VersionAdmin.register() <VersionAdmin_register>`.
Integration with 3rd party apps
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can use :ref:`VersionAdmin` as a mixin with a 3rd party admin class.
.. code:: python
@admin.register(SomeModel)
class YourModelAdmin(VersionAdmin, SomeModelAdmin):
pass
If the 3rd party model is already registered with the Django admin, you may have to unregister it first.
.. code:: python
admin.site.unregister(SomeModel)
@admin.register(SomeModel)
class YourModelAdmin(VersionAdmin, SomeModelAdmin):
pass
.. _VersionAdmin:
reversion.admin.VersionAdmin
----------------------------
A subclass of ``django.contrib.ModelAdmin`` providing rollback and recovery.
``revision_form_template = None``
A custom template to render the revision form.
Alternatively, create specially named templates to override the default templates on a per-model or per-app basis.
* ``'reversion/app_label/model_name/revision_form.html'``
* ``'reversion/app_label/revision_form.html'``
* ``'reversion/revision_form.html'``
``recover_list_template = None``
A custom template to render the recover list.
Alternatively, create specially named templates to override the default templates on a per-model or per-app basis.
* ``'reversion/app_label/model_name/recover_list.html'``
* ``'reversion/app_label/recover_list.html'``
* ``'reversion/recover_list.html'``
``recover_form_template = None``
A custom template to render the recover form.
* ``'reversion/app_label/model_name/recover_form.html'``
* ``'reversion/app_label/recover_form.html'``
* ``'reversion/recover_form.html'``
``history_latest_first = False``
If ``True``, revisions will be displayed with the most recent revision first.
.. _VersionAdmin_register:
``reversion_register(model, **options)``
Callback used by the auto-registration machinery to register the model with django-reversion. Override this to customize how models are registered.
.. code:: python
def reversion_register(self, model, **options):
options["exclude"] = ("some_field",)
super(YourModelAdmin, self).reversion_register(model, **options)
``model``
The model that will be registered with django-reversion.
``options``
Registeration options, see :ref:`reversion.register() <register>`.

479
docs/api.rst Normal file
View File

@ -0,0 +1,479 @@
.. _api:
django-reversion API
====================
Use the django-reversion API to build version-controlled apps. See also :ref:`Views` and :ref:`Middleware`.
Overview
--------
Registering models
^^^^^^^^^^^^^^^^^^
Models must be registered with django-reversion before they can be used with the API.
.. code:: python
from django.db import models
import reversion
@reversion.register()
class YourModel(models.Model):
pass
.. Hint::
If you're using the :ref:`admin`, model registration is automatic. If youre using django-reversion in a management command, make sure you call ``django.contrib.admin.autodiscover()`` to load the admin modules before using the django-reversion API.
.. include:: /_include/post-register.rst
Creating revisions
^^^^^^^^^^^^^^^^^^
A *revision* represents one or more changes made to your model instances, grouped together as a single unit. You create a revision by creating a *revision block*. When you call ``save()`` on a registered model inside a revision block, it will be added to that revision.
.. code:: python
# Declare a revision block.
with reversion.create_revision():
# Save a new model instance.
obj = YourModel()
obj.name = "obj v1"
obj.save()
# Store some meta-information.
reversion.set_user(request.user)
reversion.set_comment("Created revision 1")
# Declare a new revision block.
with reversion.create_revision():
# Update the model instance.
obj.name = "obj v2"
obj.save()
# Store some meta-information.
reversion.set_user(request.user)
reversion.set_comment("Created revision 2")
.. Important::
Bulk actions, such as ``Queryset.update()``, do not send signals, so won't be noticed by django-reversion.
Loading revisions
^^^^^^^^^^^^^^^^^
Each model instance saved in a revision block is serialized as a :ref:`Version`. All versions in a revision block are associated with a single :ref:`Revision`.
You can load a :ref:`VersionQuerySet` of versions from the database. Versions are loaded with the most recent version first.
.. code:: python
from reversion.models import Version
# Load a queryset of versions for a specific model instance.
versions = Version.objects.get_for_object(instance)
assert len(versions) == 2
# Check the serialized data for the first version.
assert versions[1].field_dict["name"] = "obj v1"
# Check the serialized data for the second version.
assert versions[0].field_dict["name"] = "obj v2"
Revision metadata
^^^^^^^^^^^^^^^^^
:ref:`Revision` stores meta-information about the revision.
.. code:: python
# Check the revision metadata for the first revision.
assert versions[1].revision.comment = "Created revision 1"
assert versions[1].revision.user = request.user
assert isinstance(versions[1].revision.date_created, datetime.datetime)
# Check the revision metadata for the second revision.
assert versions[0].revision.comment = "Created revision 2"
assert versions[0].revision.user = request.user
assert isinstance(versions[0].revision.date_created, datetime.datetime)
Reverting revisions
^^^^^^^^^^^^^^^^^^^
Revert a :ref:`Revision` to restore the serialized model instances.
.. code:: python
# Revert the first revision.
versions[1].revision.revert()
# Check the model instance has been reverted.
obj.refresh_from_db()
assert obj.name == "version 1"
# Revert the second revision.
versions[0].revision.revert()
# Check the model instance has been reverted.
obj.refresh_from_db()
assert obj.name == "version 2"
Restoring deleted model instances
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Reverting a :ref:`Revision` will restore any serialized model instances that have been deleted.
.. code:: python
# Delete the model instance, but store the pk.
pk = obj.pk
obj.delete()
# Revert the second revision.
versions[0].revision.revert()
# Check the model has been restored to the database.
obj = YourModel.objects.get(pk=obj.pk)
assert obj.name == "version 2"
.. _registration-api:
Registration API
----------------
.. _register:
``reversion.register(model, **options)``
Registers a model with django-reversion.
Throws :ref:`RegistrationError` if the model has already been registered.
``model``
The Django model to register.
``fields=None``
An iterable of field names to include in the serialized data. If ``None``, all fields will be included.
``exclude=()``
An iterable of field names to exclude from the serialized data.
``follow=()``
An iterable of model relationships to follow when saving a version of this model. ``ForeignKey``, ``ManyToManyField`` and reversion ``ForeignKey`` relationships are supported. Any property that returns a ``Model`` or ``QuerySet`` is also supported.
``format="json"``
The name of a Django serialization format to use when saving the model instance.
``for_concrete_model=True``
If ``True`` proxy models will be saved under the same content type as their concrete model. If ``False``, proxy models will be saved under their own content type, effectively giving proxy models their own distinct history.
``ignore_duplicates=False``
If ``True``, then an additional check is performed to avoid saving duplicate versions for this model.
Checking for duplicate revisions adds significant overhead to the process of creating a revision. Don't enable it unless you really need it!
.. Hint::
By default, django-reversion will not register any parent classes of a model that uses multi-table inheritance. If you wish to also add parent models to your revision, you must explicitly add their ``parent_ptr`` fields to the ``follow`` parameter when you register the model.
.. include:: /_include/post-register.rst
``reversion.is_registered(model)``
Returns whether the given model has been registered with django-reversion.
``model``
The Django model to check.
``reversion.unregister(model)``
Unregisters the given model from django-reversion.
.. include:: /_include/throws-registration-error.rst
``model``
The Django model to unregister.
``reversion.get_registered_models()``
Returns an iterable of all registered models.
.. _revision-api:
Revision API
------------
``reversion.create_revision(manage_manually=False, using=None, atomic=True)``
Marks a block of code as a *revision block*. Can also be used as a decorator.
.. include:: /_include/create-revision-args.rst
``reversion.is_active()``
Returns whether there is currently an active revision block.
``reversion.is_manage_manually()``
Returns whether the current revision block is in ``manage_manually`` mode.
``reversion.set_user(user)``
Sets the user for the current revision.
.. include:: /_include/throws-revision-error.rst
``user``
A ``User`` model instance (or whatever your ``settings.AUTH_USER_MODEL`` is).
``reversion.get_user()``
Returns the user for the current revision.
.. include:: /_include/throws-revision-error.rst
.. _set_comment:
``reversion.set_comment(comment)``
Sets the comment for the current revision.
.. include:: /_include/throws-revision-error.rst
``comment``
The text comment for the revision.
``reversion.get_comment()``
Returns the comment for the current revision.
.. include:: /_include/throws-revision-error.rst
``reversion.set_date_created(date_created)``
Sets the creation date for the current revision.
.. include:: /_include/throws-revision-error.rst
``date_created``
The creation date for the revision.
``reversion.get_date_created()``
Returns the creation date for the current revision.
.. include:: /_include/throws-revision-error.rst
``reversion.add_meta(model, **values)``
Adds custom metadata to a revision.
.. include:: /_include/throws-revision-error.rst
``model``
A Django model to store the custom metadata. The model must have a ``ForeignKey`` or ``OneToOneField`` to :ref:`Revision`.
``**values``
Values to be stored on ``model`` when it is saved.
``reversion.add_to_revision(obj, model_db=None)``
Adds a model instance to a revision.
.. include:: /_include/throws-revision-error.rst
``obj``
A model instance to add to the revision.
.. include:: /_include/model-db-arg.rst
.. _VersionQuerySet:
reversion.models.VersionQuerySet
--------------------------------
A ``QuerySet`` of :ref:`Version`. The results are ordered with the most recent :ref:`Version` first.
``Version.objects.get_for_model(model, model_db=None)``
Returns a :ref:`VersionQuerySet` for the given model.
.. include:: /_include/throws-registration-error.rst
``model``
A registered model.
.. include:: /_include/model-db-arg.rst
``Version.objects.get_for_object(obj, model_db=None)``
Returns a :ref:`VersionQuerySet` for the given model instance.
.. include:: /_include/throws-registration-error.rst
``obj``
An instance of a registered model.
.. include:: /_include/model-db-arg.rst
``Version.objects.get_for_object_reference(model, pk, model_db=None)``
Returns a :ref:`VersionQuerySet` for the given model and primary key.
.. include:: /_include/throws-registration-error.rst
``model``
A registered model.
``pk``
The database primary key of a model instance.
.. include:: /_include/model-db-arg.rst
``Version.objects.get_deleted(model, model_db=None)``
Returns a :ref:`VersionQuerySet` for the given model containing versions where the serialized model no longer exists in the database.
.. include:: /_include/throws-registration-error.rst
``model``
A registered model.
``db``
The database to load the versions from.
.. include:: /_include/model-db-arg.rst
``Version.objects.get_unique()``
Returns an iterable of :ref:`Version`, where each version is unique for a given database, model instance, and set of serialized fields.
.. _Version:
reversion.models.Version
------------------------
Represents a single model instance serialized in a revision.
``Version.id``
The database primary key of the :ref:`Version`.
``Version.revision``
A ``ForeignKey`` to a :ref:`Revision` instance.
``Version.content_type``
The ``ContentType`` of the serialized model instance.
``Version.object_id``
The string representation of the serialized model instance's primary key.
``Version.db``
The Django database alias where the serialized model was saved.
``Version.format``
The name of the Django serialization format used to serialize the model instance.
``Version.serialized_data``
The raw serialized data of the model instance.
``Version.object_repr``
The stored snapshot of the model instance's ``__str__`` method when the instance was serialized.
``Version.field_dict``
A dictionary of stored model fields. This includes fields from any parent models in the same revision.
.. include:: /_include/throws-revert-error.rst
``Version.revert()``
Restores the serialized model instance to the database. To restore the entire revision, use :ref:`Revision.revert() <Revision-revert>`.
.. include:: /_include/throws-revert-error.rst
.. _Revision:
reversion.models.Revision
-------------------------
Contains metadata about a revision, and groups together all :ref:`Version` instances created in that revision.
``Revision.id``
The database primary key of the :ref:`Revision`.
``Revision.date_created``
A ``datetime`` when the revision was created.
``Revision.user``
The ``User`` that created the revision, or None.
``Revision.comment``
A text comment on the revision.
.. _Revision-revert:
``Revision.revert(delete=False)``
Restores all contained serialized model instances to the database.
.. include:: /_include/throws-revert-error.rst
``delete``
If ``True``, any model instances which have been created and are reachable by the ``follow`` clause of any model instances in this revision will be deleted. This effectively restores a group of related models to the state they were in when the revision was created.

1
docs/changelog.rst Normal file
View File

@ -0,0 +1 @@
.. include:: ../CHANGELOG.rst

45
docs/commands.rst Normal file
View File

@ -0,0 +1,45 @@
.. _commands:
Management commands
===================
django-reversion includes a number of ``django-admin.py`` management commands.
.. _createinitialrevisions:
createinitialrevisions
----------------------
Creates an initial revision for all registered models in your project. It should be run after installing django-reversion, or registering a new model with django-reversion.
.. code:: bash
./manage.py createinitialrevisions
./manage.py createinitialrevisions your_app.YourModel --comment="Initial revision."
Run ``./manage.py createinitialrevisions --help`` for more information.
.. Warning::
For large databases, this command can take a long time to run.
deleterevisions
---------------
Deletes old revisions. It can be run regularly to keep revision history manageable.
.. code:: bash
./manage.py deleterevisions
# keep any changes from last 30 days
./manage.py deleterevisions your_app.YourModel --days=30
# keep 30 most recent changes for each item.
./manage.py deleterevisions your_app.YourModel --keep=30
# Keep anything from last 30 days and at least 3 from older changes.
./manage.py deleterevisions your_app.YourModel --keep=3 --days=30
Run ``./manage.py deleterevisions --help`` for more information.
.. Warning::
With no arguments, this command will delete your entire revision history! Read the command help for ways to limit which revisions should be deleted.

12
docs/common-problems.rst Normal file
View File

@ -0,0 +1,12 @@
.. _common-problems:
Common problems
===============
RegistrationError: class 'myapp.MyModel' has already been registered with Reversion
-----------------------------------------------------------------------------------
This is caused by your ``models.py`` file being imported twice, resulting in ``reversion.register()`` being called twice for the same model.
This problem is almost certainly due to relative import statements in your codebase. Try converting all your relative imports into absolute imports.

335
docs/conf.py Normal file
View File

@ -0,0 +1,335 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# django-reversion documentation build configuration file, created by
# sphinx-quickstart on Thu Jun 2 08:41:36 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
from reversion import __version__
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = []
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
#
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'django-reversion'
copyright = '2016, Dave Hall'
author = 'Dave Hall'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '.'.join(str(x) for x in __version__[:2])
# The full version, including alpha/beta/rc tags.
release = '.'.join(str(x) for x in __version__)
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#
# today = ''
#
# Else, today_fmt is used as the format for a strftime call.
#
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', '_include', 'Thumbs.db', '.DS_Store']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
# html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
# The name for this set of Sphinx documents.
# "<project> v<release> documentation" by default.
#
# html_title = 'django-reversion v1.10.3'
# A shorter title for the navigation bar. Default is the same as html_title.
#
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#
# html_logo = None
# The name of an image file (relative to this directory) to use as a favicon of
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#
# html_extra_path = []
# If not None, a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
# The empty string is equivalent to '%b %d, %Y'.
#
# html_last_updated_fmt = None
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#
# html_additional_pages = {}
# If false, no module index is generated.
#
# html_domain_indices = True
# If false, no index is generated.
#
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'
#
# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# 'ja' uses this config value.
# 'zh' user can custom change `jieba` dictionary path.
#
# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#
# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'django-reversiondoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'django-reversion.tex', 'django-reversion Documentation',
'Dave Hall', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#
# latex_use_parts = False
# If true, show page references after internal links.
#
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#
# latex_appendices = []
# If false, no module index is generated.
#
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'django-reversion', 'django-reversion Documentation',
[author], 1)
]
# If true, show URL addresses after external links.
#
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'django-reversion', 'django-reversion Documentation',
author, 'django-reversion', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#
# texinfo_appendices = []
# If false, no module index is generated.
#
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#
# texinfo_no_detailmenu = False

19
docs/django-versions.rst Normal file
View File

@ -0,0 +1,19 @@
.. _django-versions:
Compatible Django versions
==========================
django-reversion aims to stay compatible with the latest LTS release of Django, along with more recent releases. See :ref:`changelog`.
Older versions of Django require an older version of django-reversion to be installed.
============== =================
Django version Reversion release
============== =================
1.8 - current 2.0.0
1.7 1.10.x
1.6 1.8.x
============== =================
.. Warning::
Older versions of django-reversion receive very limited support. It's advised to upgrade your Django to remain compatible with the latest release of django-reversion.

30
docs/errors.rst Normal file
View File

@ -0,0 +1,30 @@
.. _errors:
Errors
======
django-reversion defines several custom errors.
.. _RegistrationError:
reversion.RegistrationError
---------------------------
Something went wrong with the :ref:`registration-api`.
.. _RevisionManagementError:
reversion.RevisionManagementError
---------------------------------
Something went wrong using the :ref:`revision-api`.
.. _RevertError:
reversion.RevertError
---------------------
Something went wrong reverting a revision.

73
docs/index.rst Normal file
View File

@ -0,0 +1,73 @@
.. _index:
django-reversion
================
**django-reversion** is an extension to the Django web framework that provides
version control for model instances.
Features
--------
- Roll back to any point in a model instance's history.
- Recover deleted model instances.
- Simple admin integration.
Installation
------------
To install django-reversion:
1. Install with pip: ``pip install django-reversion``.
2. Add ``'reversion'`` to ``INSTALLED_APPS``.
3. Run ``manage.py migrate``.
.. Important::
See :ref:`django-versions` if you're not using the latest release of Django.
Admin integration
-----------------
django-reversion can be used to add rollback and recovery to your admin site.
.. include:: /_include/admin.rst
For more information about admin integration, see :ref:`admin`.
Low-level API
-------------
You can use the django-reversion API to build version-controlled applications. See :ref:`api`.
More information
----------------
Installation
^^^^^^^^^^^^
.. toctree::
:maxdepth: 1
django-versions
common-problems
changelog
Usage
^^^^^
.. toctree::
:maxdepth: 2
admin
commands
api
views
middleware
errors
signals

34
docs/middleware.rst Normal file
View File

@ -0,0 +1,34 @@
.. _middleware:
Middleware
==========
Shortcuts when using django-reversion in views.
reversion.middleware.RevisionMiddleware
---------------------------------------
Wrap the every request that isn't ``GET``, ``HEAD`` or ``OPTIONS`` in a revision block.
The request user will also be added to the revision metadata.
To enable ``RevisionMiddleware``, add ``'reversion.middleware.RevisionMiddleware'`` to your ``MIDDLEWARE_CLASSES`` setting. For Django >= 1.10, add it to your ``MIDDLEWARE`` setting.
.. Warning::
This will wrap every request that isn't ``GET``, ``HEAD`` or ``OPTIONS`` in a database transaction. For best performance, consider marking individual views instead.
``RevisionMiddleware.manage_manually = False``
.. include:: /_include/create-revision-manage-manually.rst
``RevisionMiddleware.using = None``
.. include:: /_include/create-revision-using.rst
``RevisionMiddleware.atomic = True``
.. include:: /_include/create-revision-atomic.rst

22
docs/signals.rst Normal file
View File

@ -0,0 +1,22 @@
.. _signals:
Signals
=======
django-reversion provides two custom signals.
reversion.signals.pre_revision_commit
-------------------------------------
Sent just before a revision is saved to the database.
.. include:: /_include/signal-args.rst
reversion.signals.post_revision_commit
--------------------------------------
Sent just after a revision and its related versions are saved to the database.
.. include:: /_include/signal-args.rst

45
docs/views.rst Normal file
View File

@ -0,0 +1,45 @@
.. _views:
Views
=====
Shortcuts when using django-reversion in views.
Decorators
----------
``reversion.views.create_revision(manage_manually=False, using=None, atomic=True)``
Decorates a view to wrap every request that isn't ``GET``, ``HEAD`` or ``OPTIONS`` in a revision block.
The request user will also be added to the revision metadata. You can set the revision comment by calling :ref:`reversion.set_comment() <set_comment>` within your view.
.. include:: /_include/create-revision-args.rst
reversion.views.RevisionMixin
-----------------------------
Mixin a class-based view to wrap every request that isn't ``GET``, ``HEAD`` or ``OPTIONS`` in a revision block.
The request user will also be added to the revision metadata. You can set the revision comment by calling :ref:`reversion.set_comment() <set_comment>` within your view.
.. code:: python
from django.contrib.auth.views import FormView
from reversion.views import RevisionMixin
class RevisionFormView(RevisionMixin, FormView):
pass
``RevisionMixin.revision_manage_manually = False``
.. include:: /_include/create-revision-manage-manually.rst
``RevisionMixin.revision_using = None``
.. include:: /_include/create-revision-using.rst

39
reversion/__init__.py Normal file
View File

@ -0,0 +1,39 @@
"""
An extension to the Django web framework that provides version control for model instances.
Developed by Dave Hall.
<http://www.etianen.com/>
"""
try:
import django # noqa
except ImportError: # pragma: no cover
# The top-level API requires Django, which might not be present if setup.py
# is importing reversion to get __version__.
pass
else:
from reversion.errors import ( # noqa
RevertError,
RevisionManagementError,
RegistrationError,
)
from reversion.revisions import ( # noqa
is_active,
is_manage_manually,
get_user,
set_user,
get_comment,
set_comment,
get_date_created,
set_date_created,
add_meta,
add_to_revision,
create_revision,
register,
is_registered,
unregister,
get_registered_models,
)
__version__ = VERSION = (2, 0, 12)

305
reversion/admin.py Normal file
View File

@ -0,0 +1,305 @@
from __future__ import unicode_literals
import json
from contextlib import contextmanager
from django.db import models, transaction, connection
from django.conf.urls import url
from django.contrib import admin, messages
from django.contrib.admin import options
from django.contrib.admin.models import LogEntry
from django.contrib.admin.utils import unquote, quote
try:
from django.contrib.contenttypes.admin import GenericInlineModelAdmin
from django.contrib.contenttypes.fields import GenericRelation
except ImportError: # Django < 1.9 pragma: no cover
from django.contrib.contenttypes.generic import GenericInlineModelAdmin, GenericRelation
try:
from django.urls import reverse
except ImportError: # Django < 1.10 pragma: no cover
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied, ImproperlyConfigured
from django.shortcuts import get_object_or_404, render, redirect
from django.utils.text import capfirst
from django.utils.timezone import template_localtime
from django.utils.translation import ugettext as _
from django.utils.encoding import force_text
from django.utils.formats import localize
from reversion.compat import remote_field, remote_model
from reversion.errors import RevertError
from reversion.models import Version
from reversion.revisions import is_active, register, is_registered, set_comment, create_revision, set_user
from reversion.views import _RollBackRevisionView
def private_fields(meta):
try:
return meta.private_fields
except AttributeError: # Django < 1.10 pragma: no cover
return meta.virtual_fields
class VersionAdmin(admin.ModelAdmin):
object_history_template = "reversion/object_history.html"
change_list_template = "reversion/change_list.html"
revision_form_template = None
recover_list_template = None
recover_form_template = None
history_latest_first = False
def reversion_register(self, model, **kwargs):
"""Registers the model with reversion."""
register(model, **kwargs)
@contextmanager
def create_revision(self, request):
with create_revision():
set_user(request.user)
yield
# Revision helpers.
def _reversion_get_template_list(self, template_name):
opts = self.model._meta
return (
"reversion/%s/%s/%s" % (opts.app_label, opts.object_name.lower(), template_name),
"reversion/%s/%s" % (opts.app_label, template_name),
"reversion/%s" % template_name,
)
def _reversion_order_version_queryset(self, queryset):
"""Applies the correct ordering to the given version queryset."""
if not self.history_latest_first:
queryset = queryset.order_by("pk")
return queryset
# Messages.
def log_addition(self, request, object, change_message=None):
change_message = change_message or _("Initial version.")
if is_active():
# If https://code.djangoproject.com/ticket/27218 is implemented, we
# could first call super() and get the change_message from the returned
# LogEntry.
if isinstance(change_message, list):
set_comment(LogEntry(change_message=json.dumps(change_message)).get_change_message())
else:
set_comment(change_message)
try:
super(VersionAdmin, self).log_addition(request, object, change_message)
except TypeError: # Django < 1.9 pragma: no cover
super(VersionAdmin, self).log_addition(request, object)
def log_change(self, request, object, message):
if is_active():
if isinstance(message, list):
set_comment(LogEntry(change_message=json.dumps(message)).get_change_message())
else:
set_comment(message)
super(VersionAdmin, self).log_change(request, object, message)
# Auto-registration.
def _reversion_autoregister(self, model, follow):
if not is_registered(model):
for parent_model, field in model._meta.concrete_model._meta.parents.items():
follow += (field.name,)
self._reversion_autoregister(parent_model, ())
self.reversion_register(model, follow=follow)
def _reversion_introspect_inline_admin(self, inline):
inline_model = None
follow_field = None
fk_name = None
if issubclass(inline, GenericInlineModelAdmin):
inline_model = inline.model
ct_field = inline.ct_field
fk_name = inline.ct_fk_field
for field in private_fields(self.model._meta):
if (
isinstance(field, GenericRelation) and
remote_model(field) == inline_model and
field.object_id_field_name == fk_name and
field.content_type_field_name == ct_field
):
follow_field = field.name
break
elif issubclass(inline, options.InlineModelAdmin):
inline_model = inline.model
fk_name = inline.fk_name
if not fk_name:
for field in inline_model._meta.get_fields():
if (
isinstance(field, (models.ForeignKey, models.OneToOneField)) and
issubclass(self.model, remote_model(field))
):
fk_name = field.name
break
if fk_name and not remote_field(inline_model._meta.get_field(fk_name)).is_hidden():
field = inline_model._meta.get_field(fk_name)
accessor = remote_field(field).get_accessor_name()
follow_field = accessor
return inline_model, follow_field
def __init__(self, *args, **kwargs):
super(VersionAdmin, self).__init__(*args, **kwargs)
# Automatically register models if required.
if not is_registered(self.model):
inline_fields = ()
for inline in self.inlines:
inline_model, follow_field = self._reversion_introspect_inline_admin(inline)
if inline_model:
self._reversion_autoregister(inline_model, ())
if follow_field:
inline_fields += (follow_field,)
self._reversion_autoregister(self.model, inline_fields)
def get_urls(self):
urls = super(VersionAdmin, self).get_urls()
admin_site = self.admin_site
opts = self.model._meta
info = opts.app_label, opts.model_name,
reversion_urls = [
url("^recover/$", admin_site.admin_view(self.recoverlist_view), name='%s_%s_recoverlist' % info),
url("^recover/(\d+)/$", admin_site.admin_view(self.recover_view), name='%s_%s_recover' % info),
url("^([^/]+)/history/(\d+)/$", admin_site.admin_view(self.revision_view), name='%s_%s_revision' % info),
]
return reversion_urls + urls
# Views.
def add_view(self, request, form_url='', extra_context=None):
with self.create_revision(request):
return super(VersionAdmin, self).add_view(request, form_url, extra_context)
def change_view(self, request, object_id, form_url='', extra_context=None):
with self.create_revision(request):
return super(VersionAdmin, self).change_view(request, object_id, form_url, extra_context)
def _reversion_revisionform_view(self, request, version, template_name, extra_context=None):
# Check that database transactions are supported.
if not connection.features.uses_savepoints:
raise ImproperlyConfigured("Cannot use VersionAdmin with a database that does not support savepoints.")
# Run the view.
try:
with transaction.atomic(using=version.db):
# Revert the revision.
version.revision.revert(delete=True)
# Run the normal changeform view.
with self.create_revision(request):
response = self.changeform_view(request, quote(version.object_id), request.path, extra_context)
# Decide on whether the keep the changes.
if request.method == "POST" and response.status_code == 302:
set_comment(_("Reverted to previous version, saved on %(datetime)s") % {
"datetime": localize(template_localtime(version.revision.date_created)),
})
else:
response.template_name = template_name # Set the template name to the correct template.
response.render() # Eagerly render the response, so it's using the latest version.
raise _RollBackRevisionView(response) # Raise exception to undo the transaction and revision.
except RevertError as ex:
opts = self.model._meta
messages.error(request, force_text(ex))
return redirect("{}:{}_{}_changelist".format(self.admin_site.name, opts.app_label, opts.model_name))
except _RollBackRevisionView as ex:
return ex.response
return response
def recover_view(self, request, version_id, extra_context=None):
"""Displays a form that can recover a deleted model."""
# The revisionform view will check for change permission (via changeform_view),
# but we also need to check for add permissions here.
if not self.has_add_permission(request):
raise PermissionDenied
# Render the recover view.
version = get_object_or_404(Version, pk=version_id)
context = {
"title": _("Recover %(name)s") % {"name": version.object_repr},
"recover": True,
}
context.update(extra_context or {})
return self._reversion_revisionform_view(
request,
version,
self.recover_form_template or self._reversion_get_template_list("recover_form.html"),
context,
)
def revision_view(self, request, object_id, version_id, extra_context=None):
"""Displays the contents of the given revision."""
object_id = unquote(object_id) # Underscores in primary key get quoted to "_5F"
version = get_object_or_404(Version, pk=version_id, object_id=object_id)
context = {
"title": _("Revert %(name)s") % {"name": version.object_repr},
"revert": True,
}
context.update(extra_context or {})
return self._reversion_revisionform_view(
request,
version,
self.revision_form_template or self._reversion_get_template_list("revision_form.html"),
context,
)
def changelist_view(self, request, extra_context=None):
with self.create_revision(request):
context = {
"has_change_permission": self.has_change_permission(request),
}
context.update(extra_context or {})
return super(VersionAdmin, self).changelist_view(request, context)
def recoverlist_view(self, request, extra_context=None):
"""Displays a deleted model to allow recovery."""
# Check if user has change and add permissions for model
if not self.has_change_permission(request) or not self.has_add_permission(request):
raise PermissionDenied
model = self.model
opts = model._meta
deleted = self._reversion_order_version_queryset(Version.objects.get_deleted(self.model))
# Set the app name.
request.current_app = self.admin_site.name
# Get the rest of the context.
context = dict(
self.admin_site.each_context(request),
opts=opts,
app_label=opts.app_label,
module_name=capfirst(opts.verbose_name),
title=_("Recover deleted %(name)s") % {"name": force_text(opts.verbose_name_plural)},
deleted=deleted,
)
context.update(extra_context or {})
return render(
request,
self.recover_list_template or self._reversion_get_template_list("recover_list.html"),
context,
)
def history_view(self, request, object_id, extra_context=None):
"""Renders the history view."""
# Check if user has change permissions for model
if not self.has_change_permission(request):
raise PermissionDenied
opts = self.model._meta
action_list = [
{
"revision": version.revision,
"url": reverse(
"%s:%s_%s_revision" % (self.admin_site.name, opts.app_label, opts.model_name),
args=(quote(version.object_id), version.id)
),
}
for version
in self._reversion_order_version_queryset(Version.objects.get_for_object_reference(
self.model,
unquote(object_id), # Underscores in primary key get quoted to "_5F"
).select_related("revision__user"))
]
# Compile the context.
context = {"action_list": action_list}
context.update(extra_context or {})
return super(VersionAdmin, self).history_view(request, object_id, context)

17
reversion/compat.py Normal file
View File

@ -0,0 +1,17 @@
import django
def remote_field(field):
# remote_field is new in Django 1.9
return field.remote_field if hasattr(field, 'remote_field') else field.rel
def remote_model(field):
# remote_field is new in Django 1.9
return field.remote_field.model if hasattr(field, 'remote_field') else field.rel.to
def is_authenticated(user):
if django.VERSION < (1, 10):
return user.is_authenticated()
return user.is_authenticated

13
reversion/errors.py Normal file
View File

@ -0,0 +1,13 @@
class RevertError(Exception):
"""Exception thrown when something goes wrong with reverting a model."""
class RevisionManagementError(Exception):
"""Exception that is thrown when something goes wrong with revision managment."""
class RegistrationError(Exception):
"""Exception thrown when registration with django-reversion goes wrong."""

Binary file not shown.

View File

@ -0,0 +1,125 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-06-15 01:49+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
#: reversion/admin.py:161
msgid "Initial version."
msgstr "النسخة الأولية"
#: reversion/admin.py:195 reversion/templates/reversion/change_list.html:7
#: reversion/templates/reversion/recover_form.html:10
#: reversion/templates/reversion/recover_list.html:10
#, python-format
msgid "Recover deleted %(name)s"
msgstr "أستعيد المحذوف من %(name)s"
#: reversion/admin.py:312
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "اُعيد لنسخه سابقه، حُفظ في %(datetime)s"
#: reversion/admin.py:314
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"تم أعاده %(model)s \"%(name)s\" بنجاح ، يمكنك/ي التعديل مجددا"
""
#: reversion/admin.py:399
#, python-format
msgid "Recover %(name)s"
msgstr "إستعيد %(name)s"
#: reversion/admin.py:413
#, python-format
msgid "Revert %(name)s"
msgstr "أعد %(name)s"
#: reversion/models.py:59
msgid "date created"
msgstr "تاريخ الأنشاء"
#: reversion/models.py:66
msgid "user"
msgstr "المستخدم"
#: reversion/models.py:70
msgid "comment"
msgstr "التعليق"
#: reversion/templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr "أختر تاريخ من القائمه أدناه لأعاده نسخه سابقه من هذا الكيان"
#: reversion/templates/reversion/object_history.html:15
#: reversion/templates/reversion/recover_list.html:23
msgid "Date/time"
msgstr "التاريخ/ الوقت"
#: reversion/templates/reversion/object_history.html:16
msgid "User"
msgstr "المستخدم"
#: reversion/templates/reversion/object_history.html:17
msgid "Comment"
msgstr "التعليق"
#: reversion/templates/reversion/object_history.html:38
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr "لا يوجد تاريخ تعديل لهذا الكيان. ربما لم يُنشأ من موقع الإداره"
#: reversion/templates/reversion/recover_form.html:7
#: reversion/templates/reversion/recover_list.html:7
#: reversion/templates/reversion/revision_form.html:7
msgid "Home"
msgstr "الرئيسيه"
#: reversion/templates/reversion/recover_form.html:17
msgid "Press the save button below to recover this version of the object."
msgstr "أنقر على حفظ أدناه لأسترجاع هذه النسخه"
#: reversion/templates/reversion/recover_list.html:17
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr "أختر تاريخ من القائمه أدناه لإسترجاع نسخه سابقه من هذا الكيان"
#: reversion/templates/reversion/recover_list.html:37
msgid "There are no deleted objects to recover."
msgstr "لا يوجد كيانات محذوفه لإسترجاعها"
#: reversion/templates/reversion/revision_form.html:11
msgid "History"
msgstr "التاريخ"
#: reversion/templates/reversion/revision_form.html:12
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "إسترجع %(verbose_name)s"
#: reversion/templates/reversion/revision_form.html:25
msgid "Press the save button below to revert to this version of the object."
msgstr "أنقر على حفظ أدناه لإعاده هذه النسخه"

Binary file not shown.

View File

@ -0,0 +1,127 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-01-12 11:13+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n>1 && n<5 ? 1 : 2;\n"
#: admin.py:112 templates/reversion/change_list.html:8
#: templates/reversion/recover_list.html:10
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Obnovit smazané %(name)s"
#: admin.py:165
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Vráceno do předchozí verze uložené v %(datetime)s"
#: admin.py:167
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"Objekt %(model)s \"%(name)s\" byl úspěšně obnoven. Můžete ho znovu začít upravovat "
"níže."
#: admin.py:273
#, python-format
msgid "Recover %(name)s"
msgstr "Obnovit %(name)s"
#: admin.py:284
#, python-format
msgid "Revert %(name)s"
msgstr "Navrátit se k předchozí verzi %(name)s"
#: management/commands/createinitialrevisions.py:76
msgid "Initial version."
msgstr "První verze"
#: templates/reversion/change_list.html:11
#, python-format
msgid "Add %(name)s"
msgstr "Přidat %(name)s"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr ""
"Zvolte datum ze seznamu níže pro návrat k předchozí verzi tohoto objektu."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:23
msgid "Date/time"
msgstr "Datum/čas"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Uživatel"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Komentář"
#: templates/reversion/object_history.html:23
#: templates/reversion/recover_list.html:30
msgid "DATETIME_FORMAT"
msgstr "DATETIME_FORMAT"
#: templates/reversion/object_history.html:31
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Tento objekt nemá uloženou žádnou historii změn. Zřejmě nebyl přidát přes toto "
"administrační rozhraní."
#: templates/reversion/recover_form.html:7
#: templates/reversion/recover_list.html:7
#: templates/reversion/revision_form.html:10
msgid "Home"
msgstr "Domů"
#: templates/reversion/recover_form.html:10
#, python-format
msgid "Recover deleted %(verbose_name)s"
msgstr "Obnovit smazané %(verbose_name)s"
#: templates/reversion/recover_form.html:17
msgid "Press the save button below to recover this version of the object."
msgstr "Klikněte na tlačítko uložit pro obnovení této verze objektu."
#: templates/reversion/recover_list.html:17
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr ""
"Zvolte datum ze seznamu níže pro obnovení smazané verze objektu."
#: templates/reversion/recover_list.html:37
msgid "There are no deleted objects to recover."
msgstr "Žádné smazané objekty k obnovení."
#: templates/reversion/revision_form.html:14
msgid "History"
msgstr "Historie"
#: templates/reversion/revision_form.html:15
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Navrátit %(verbose_name)s k předchozí verzi"
#: templates/reversion/revision_form.html:28
msgid "Press the save button below to revert to this version of the object."
msgstr "Klikněte na tlačítko uložit pro návrat k této verzi objektu."

Binary file not shown.

View File

@ -0,0 +1,122 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-07-30 11:17+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin.py:160
msgid "Initial version."
msgstr "Første version."
#: admin.py:194 templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:11
#: templates/reversion/recover_list.html:11
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Gendan slettede %(name)s"
#: admin.py:311
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Gendannet til tidligere version, gemt den %(datetime)s"
#: admin.py:313
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"Gendannelsen af %(model)s \"%(name)s\" var succesfuld. Du kan redigere den igen"
"her under"
#: admin.py:398
#, python-format
msgid "Recover %(name)s"
msgstr "Genskab %(name)s"
#: admin.py:412
#, python-format
msgid "Revert %(name)s"
msgstr "Revertere %(name)s"
#: models.py:55
msgid "date created"
msgstr "oprettelsesdato"
#: models.py:62
msgid "user"
msgstr "bruger"
#: models.py:66
msgid "comment"
msgstr "kommentar"
#: templates/reversion/object_history.html:8
msgid "Choose a date from the list below to revert to a previous version of this object."
msgstr "Vælg en dato fra listen her under for at Revertere til en tidligere version af det her objekt."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:24
msgid "Date/time"
msgstr "Dato/tid"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Bruger"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Kommentar"
#: templates/reversion/object_history.html:38
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Det her objekt har ingen ændringshistorik. Det er sandsynligvis ikke tilføjet via"
"dette admin-side."
#: templates/reversion/recover_form.html:8
#: templates/reversion/recover_list.html:8
#: templates/reversion/revision_form.html:8
msgid "Home"
msgstr "Hjem"
#: templates/reversion/recover_form.html:18
msgid "Press the save button below to recover this version of the object."
msgstr "Tryk på gem knappen nedenunder for at genskab denne version af objektet."
#: templates/reversion/recover_list.html:18
msgid "Choose a date from the list below to recover a deleted version of an object."
msgstr "Vælg en dato fra listen her under for at gendanne til en tidligere version af objektet."
#: templates/reversion/recover_list.html:38
msgid "There are no deleted objects to recover."
msgstr "Der findes inden slettede objekter at gendanne."
#: templates/reversion/revision_form.html:12
msgid "History"
msgstr "Historik"
#: templates/reversion/revision_form.html:13
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Revertere %(verbose_name)s"
#: templates/reversion/revision_form.html:26
msgid "Press the save button below to revert to this version of the object."
msgstr "Tryk på gem her nedenunder for at revertere til denne version af objektet."

Binary file not shown.

View File

@ -0,0 +1,133 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: reversion\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-02-03 08:31+0100\n"
"PO-Revision-Date: 2009-02-03 08:41+0100\n"
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: admin.py:122 templates/reversion/change_list.html:8
#: templates/reversion/recover_list.html:9
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Gelöschte %(name)s wiederherstellen"
#: admin.py:155
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Zu vorheriger Version zurückgesetzt, %(datetime)s gespeichert"
#: admin.py:157
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"%(model)s \"%(name)s\" wurde erfolgreich zurückgesetzt. Sie können mit der "
"Bearbeitung forfahren."
#: admin.py:227
#, python-format
msgid "Recover %s"
msgstr "%s wiederherstellen"
#: admin.py:243
#, python-format
msgid "Revert %(name)s"
msgstr "%(name)s zurücksetzen"
#: management/commands/createinitialrevisions.py:76
msgid "Initial version."
msgstr "Ursprüngliche Version."
#: templates/reversion/change_list.html:11
#, python-format
msgid "Add %(name)s"
msgstr "%(name)s hinzufügen"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr ""
"Wählen Sie einen Zeitpunkt aus der untenstehenden Liste aus, um zu einer "
"vorherigen Version dieses Objektes zurückzukehren."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:21
msgid "Date/time"
msgstr "Datum/Zeit"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Benutzer"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Kommentar"
#: templates/reversion/object_history.html:23
#: templates/reversion/recover_list.html:28
msgid "DATETIME_FORMAT"
msgstr "j. N Y, H:i"
#: templates/reversion/object_history.html:31
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Dieses Objekt hat keine Änderungsgeschichte. Es wurde möglicherweise nicht "
"über diese Verwaltungsseiten angelegt."
#: templates/reversion/recover_form.html:14
#: templates/reversion/recover_list.html:6
#: templates/reversion/revision_form.html:14
msgid "Home"
msgstr "Start"
#: templates/reversion/recover_form.html:17
#, python-format
msgid "Recover deleted %(verbose_name)s"
msgstr "Gelöschte %(verbose_name)s wiederherstellen"
#: templates/reversion/recover_form.html:18
#, python-format
msgid "Recover %(name)s"
msgstr "%(name)s wiederherstellen"
#: templates/reversion/recover_form.html:24
msgid "Press the save button below to recover this version of the object."
msgstr "Sichern Sie, um diese Version des Objektes wiederherzustellen."
#: templates/reversion/recover_list.html:15
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr ""
"Wählen Sie einen Zeitpunk aus der untenstehenden Liste, um eine gelöschte "
"Version des Objektes wiederherzustellen."
#: templates/reversion/recover_list.html:35
msgid "There are no deleted objects to recover."
msgstr "Es sind keine gelöschten Objekte zur Wiederherstellung vorhanden."
#: templates/reversion/revision_form.html:18
msgid "History"
msgstr "Geschichte"
#: templates/reversion/revision_form.html:19
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "%(verbose_name)s zurücksetzen"
#: templates/reversion/revision_form.html:32
msgid "Press the save button below to revert to this version of the object."
msgstr "Sichern Sie, um das Objekt zu dieser Version zurückzusetzen."

Binary file not shown.

View File

@ -0,0 +1,126 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <alexander.ayasca.esquives@gmail.com>, 2013.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-08-31 15:49-0500\n"
"PO-Revision-Date: 2013-08-31 16:22-0500\n"
"Last-Translator: Alexander Ayasca Esquives <alexander.ayasca.esquives@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: admin.py:144
msgid "Initial version."
msgstr "Versión inicial"
#: admin.py:166
#, python-format
msgid "Deleted %(verbose_name)s."
msgstr "%(verbose_name)s eliminados"
#: admin.py:189 templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:11
#: templates/reversion/recover_list.html:11
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Recuperar %(name)s eliminados"
#: admin.py:304
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Revertido a una versión anterior, grabada el %(datetime)s"
#: admin.py:306
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr "El %(model)s \"%(name)s\" fue revertido satisfactoriamente. Puede editarlo nuevamente "
#: admin.py:392
#, python-format
msgid "Recover %(name)s"
msgstr "Recuperar %(name)s"
#: admin.py:406
#, python-format
msgid "Revert %(name)s"
msgstr "Revertir %(name)s"
#: models.py:59
msgid "date created"
msgstr "fecha de creación"
#: models.py:65
msgid "user"
msgstr "usuario"
#: models.py:69
msgid "comment"
msgstr "comentario"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr "Escoja una fecha de la lista siguiente para revertir a una versión anterior de este objeto"
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:24
msgid "Date/time"
msgstr "Fecha/Hora"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Usuario"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Comentario"
#: templates/reversion/object_history.html:36
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr "Este objeto no tiene un historial de cambios. Probablemente no fue añadido por medio del sitio de administración"
#: templates/reversion/recover_form.html:8
#: templates/reversion/recover_list.html:8
#: templates/reversion/revision_form.html:8
msgid "Home"
msgstr "Inicio"
#: templates/reversion/recover_form.html:18
msgid "Press the save button below to recover this version of the object."
msgstr "Presione el botón guardar para recuperar esta versión del objeto"
#: templates/reversion/recover_list.html:18
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr "Escoja una fecha de la lista siguiente para recuperar una versión eliminada del objeto"
#: templates/reversion/recover_list.html:38
msgid "There are no deleted objects to recover."
msgstr "No hay objetos eliminados a recuperar"
#: templates/reversion/revision_form.html:12
msgid "History"
msgstr "Historial"
#: templates/reversion/revision_form.html:13
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Revertir %(verbose_name)s"
#: templates/reversion/revision_form.html:26
msgid "Press the save button below to revert to this version of the object."
msgstr "Presione el botón guardar para revertir a esta versión del objeto"

Binary file not shown.

View File

@ -0,0 +1,134 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Gonzalo Bustos, 2015.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-10-11 19:10-0300\n"
"PO-Revision-Date: 2015-10-11 19:12-0300\n"
"Last-Translator: Gonzalo Bustos\n"
"Language-Team: Spanish (Argentina)\n"
"Language: es_AR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.10\n"
#: admin.py:144
msgid "Initial version."
msgstr "Versión inicial."
#: admin.py:166
#, python-format
msgid "Deleted %(verbose_name)s."
msgstr "%(verbose_name)s eliminados"
#: admin.py:189 templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:11
#: templates/reversion/recover_list.html:11
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Restaurar %(name)s eliminados"
#: admin.py:304
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Revertido a una versión anterior, guardada el %(datetime)s"
#: admin.py:306
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"El %(model)s \"%(name)s\" fue revertido con éxito. Puede editarlo "
"nuevamente a continuación."
#: admin.py:392
#, python-format
msgid "Recover %(name)s"
msgstr "Restaurar %(name)s"
#: admin.py:406
#, python-format
msgid "Revert %(name)s"
msgstr "Revertir %(name)s"
#: models.py:59
msgid "date created"
msgstr "fecha de creación"
#: models.py:65
msgid "user"
msgstr "usuario"
#: models.py:69
msgid "comment"
msgstr "comentario"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr ""
"Elija una fecha del listado a continuación para revertir a una versión "
"anterior de este objeto"
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:24
msgid "Date/time"
msgstr "Fecha/hora"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Usuario"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Comentario"
#: templates/reversion/object_history.html:36
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Este objeto no tiene un historial de cambios. Es probable que no haya sido "
"agregado a través del sitio de administración."
#: templates/reversion/recover_form.html:8
#: templates/reversion/recover_list.html:8
#: templates/reversion/revision_form.html:8
msgid "Home"
msgstr "Inicio"
#: templates/reversion/recover_form.html:18
msgid "Press the save button below to recover this version of the object."
msgstr "Presione el botón guardar para restaurar esta versión del objeto."
#: templates/reversion/recover_list.html:18
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr ""
"Elija una fecha del listado a continuación para restaurar una versión "
"eliminada del objeto."
#: templates/reversion/recover_list.html:38
msgid "There are no deleted objects to recover."
msgstr "No hay objetos eliminados para restaurar."
#: templates/reversion/revision_form.html:12
msgid "History"
msgstr "Historial"
#: templates/reversion/revision_form.html:13
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Revertir %(verbose_name)s"
#: templates/reversion/revision_form.html:26
msgid "Press the save button below to revert to this version of the object."
msgstr "Presione el botón guardar para revertir a esta versión del objeto."

Binary file not shown.

View File

@ -0,0 +1,127 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Simon Charette <charette.s@gmail.com>, 2010.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-10-01 20:56-0400\n"
"PO-Revision-Date: 2011-09-21 16:31-0400\n"
"Last-Translator: Etienne Desautels <etienne.desautels@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n>1;\n"
#: admin.py:143 templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:10
#: templates/reversion/recover_list.html:10
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Récupérer %(name)s supprimés"
#: admin.py:123
#, python-format
msgid "Deleted %(verbose_name)s."
msgstr "Supprimé %(verbose_name)s."
#: admin.py:252
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Restauré depuis une version précédente, sauvée le %(datetime)s"
#: admin.py:254
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"Lélément %(model)s \"%(name)s\" a été restauré avec succès. Vous pouvez "
"léditer à nouveau."
#: admin.py:337
#, python-format
msgid "Recover %(name)s"
msgstr "Récupérer %(name)s"
#: admin.py:349
#, python-format
msgid "Revert %(name)s"
msgstr "Restaurer %(name)s"
#: admin.py:111
msgid "Initial version."
msgstr "Version initiale."
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr "Choisissez une date dans la liste ci-dessous afin de restaurer "
"une version précédente de cet élément."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:23
msgid "Date/time"
msgstr "Date/heure"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Utilisateur"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Commentaire"
#: admin.py:252 templates/reversion/object_history.html:23
#: templates/reversion/recover_list.html:30
msgid "DATETIME_FORMAT"
msgstr "j F Y H:i:s"
#: templates/reversion/object_history.html:36
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Cet élément ne possède pas dhistorique de modifications. Il na "
"probablement pas été ajouté à partir de ce site dadministration."
#: templates/reversion/recover_form.html:7
#: templates/reversion/recover_list.html:7
#: templates/reversion/revision_form.html:7
msgid "Home"
msgstr "Accueil"
#: templates/reversion/recover_form.html:17
msgid "Press the save button below to recover this version of the object."
msgstr "Cliquez sur le bouton <strong>Enregistrer</strong> ci-dessous afin de "
"récupérer cet élément."
#: templates/reversion/recover_list.html:17
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr "Choisissez une date dans la liste ci-dessous afin de récupérer un "
"élément supprimé."
#: templates/reversion/recover_list.html:37
msgid "There are no deleted objects to recover."
msgstr "Il ny a pas déléments supprimés à récupérer."
#: templates/reversion/revision_form.html:11
msgid "History"
msgstr "Historique"
#: templates/reversion/revision_form.html:12
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Restaurer %(verbose_name)s"
#: templates/reversion/revision_form.html:25
msgid "Press the save button below to revert to this version of the object."
msgstr "Cliquez sur le bouton <strong>Enregistrer</strong> ci-dessous pour "
"restaurer cette version de lélément."

Binary file not shown.

View File

@ -0,0 +1,123 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-10 10:27+0200\n"
"PO-Revision-Date: 2009-12-10 10:45+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: admin.py:112 templates/reversion/change_list.html:7
#: templates/reversion/recover_list.html:10
#, python-format
msgid "Recover deleted %(name)s"
msgstr "שחזור %(name)s שנמחקו"
#: admin.py:158
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "שוחזר לגרסה קודמת, נשמרה ב-%(datetime)s"
#: admin.py:160
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"שחזור %(model)s \"%(name)s\" לגרסה קודמת הצליח. ניתן לערוך שוב "
"מתחת."
#: admin.py:259
#, python-format
msgid "Recover %(name)s"
msgstr "אחזור %(name)s"
#: admin.py:269
#, python-format
msgid "Revert %(name)s"
msgstr "שחזור %(name)s"
#: templates/reversion/change_list.html:9
#, python-format
msgid "Add %(name)s"
msgstr "הוספת %(name)s"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr ""
"נא לבחור תאריך מהרשימה להלן כדי לשחזר לגרסה קודמת של "
"אובייקט זה."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:23
msgid "Date/time"
msgstr "תאריך/שעה"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "משתמש"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "הערה"
#: templates/reversion/object_history.html:23
#: templates/reversion/recover_list.html:30
msgid "DATETIME_FORMAT"
msgstr "d.m.Y H:i:s"
#: templates/reversion/object_history.html:31
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"לאובייקט זה אין היסטוריית שינוי. כנראה לא התווסף דרך "
"ממשק הניהול."
#: templates/reversion/recover_form.html:14
#: templates/reversion/recover_list.html:7
#: templates/reversion/revision_form.html:14
msgid "Home"
msgstr "ראשי"
#: templates/reversion/recover_form.html:17
#, python-format
msgid "Recover deleted %(verbose_name)s"
msgstr "אחזור %(verbose_name)s שנמחק"
#: templates/reversion/recover_form.html:24
msgid "Press the save button below to recover this version of the object."
msgstr "נא ללחוץ על לחצן השמירה מתחת כדי לאחזר לגרסה זו של האובייקט"
#: templates/reversion/recover_list.html:17
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr ""
"נא לבחור תאריך מתחת כדי לאחזר גרסה מחוקה של אובייקט"
#: templates/reversion/recover_list.html:37
msgid "There are no deleted objects to recover."
msgstr "אין אובייקטים מחוקים לאחזור"
#: templates/reversion/revision_form.html:18
msgid "History"
msgstr "היסטוריה"
#: templates/reversion/revision_form.html:19
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "שחזור %(verbose_name)s"
#: templates/reversion/revision_form.html:32
msgid "Press the save button below to revert to this version of the object."
msgstr "נא ללחוץ על לחצן השמירה מתחת כדי לשחזר לגרסה זו של האובייקט"

Binary file not shown.

View File

@ -0,0 +1,113 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-29 13:04+0200\n"
"PO-Revision-Date: 2009-08-29 13:44+0100\n"
"Last-Translator: Marco Beri <marcoberi@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: .\admin.py:128
#: .\templates\reversion\change_list.html.py:7
#: .\templates\reversion\recover_list.html.py:10
msgid "Recover deleted %(name)s"
msgstr "Recupera %(name)s cancellati"
#: .\admin.py:173
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Ritorna alla precedente versione, salvata il %(datetime)s"
#: .\admin.py:175
#, python-format
msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below."
msgstr "%(model)s \"%(name)s\" è alla versione precedente. Puoi effettuare nuove modifiche se lo desideri."
#: .\admin.py:271
#, python-format
msgid "Recover %(name)s"
msgstr "Recupera %(name)s"
#: .\admin.py:281
#, python-format
msgid "Revert %(name)s"
msgstr "Ritorna %(name)s"
#: .\templates\reversion\change_list.html.py:9
#, python-format
msgid "Add %(name)s"
msgstr "Aggiungi %(name)s"
#: .\templates\reversion\object_history.html.py:8
msgid "Choose a date from the list below to revert to a previous version of this object."
msgstr "Scegli una data dall'elenco qui sotto per ritornare a una precedente versione di questo oggetto."
#: .\templates\reversion\object_history.html.py:15
#: .\templates\reversion\recover_list.html.py:23
msgid "Date/time"
msgstr "Data/ora"
#: .\templates\reversion\object_history.html.py:16
msgid "User"
msgstr "Utente"
#: .\templates\reversion\object_history.html.py:17
msgid "Comment"
msgstr "Commento"
#: .\templates\reversion\object_history.html.py:23
#: .\templates\reversion\recover_list.html.py:30
msgid "DATETIME_FORMAT"
msgstr "d/m/Y, G:i"
#: .\templates\reversion\object_history.html.py:31
msgid "This object doesn't have a change history. It probably wasn't added via this admin site."
msgstr "Questo oggetto non ha una storia di modifiche. Probabilmente non è stato aggiunto attraverso questo sito di Admin."
#: .\templates\reversion\recover_form.html.py:14
#: .\templates\reversion\recover_list.html.py:7
#: .\templates\reversion\revision_form.html.py:14
msgid "Home"
msgstr "Home"
#: .\templates\reversion\recover_form.html.py:17
#, python-format
msgid "Recover deleted %(verbose_name)s"
msgstr "Recupera %(verbose_name)s cancellato"
#: .\templates\reversion\recover_form.html.py:24
msgid "Press the save button below to recover this version of the object."
msgstr "Premi il pulsante Salva in basso per recuperare questa versione dell'oggetto."
#: .\templates\reversion\recover_list.html.py:17
msgid "Choose a date from the list below to recover a deleted version of an object."
msgstr "Scegli una data dall'elenco qui sotto per recuperare una versione cancellata di questo oggetto."
#: .\templates\reversion\recover_list.html.py:37
msgid "There are no deleted objects to recover."
msgstr "Non ci sono oggetti cancellati da recuperare."
#: .\templates\reversion\revision_form.html.py:18
msgid "History"
msgstr "Storia"
#: .\templates\reversion\revision_form.html.py:19
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Ritorna %(verbose_name)s"
#: .\templates\reversion\revision_form.html.py:32
msgid "Press the save button below to revert to this version of the object."
msgstr "Premi il pulsante Salva in basso per ritornare a questa versione dell'oggetto"
#~ msgid "Recover %s"
#~ msgstr "Recupera %s"

Binary file not shown.

View File

@ -0,0 +1,116 @@
# Norwegian translation for django-reversion
# This file is distributed under the same license as the django-reversion package.
# Sindre Sorhus <sindresorhus@gmail.com>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: django-reversion\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-10-17 09:34+0200\n"
"PO-Revision-Date: 2011-10-17 10:17+0100\n"
"Last-Translator: Sindre Sorhus <sindresorhus@gmail.com>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"X-Poedit-Language: Norwegian Bokmal\n"
"X-Poedit-Country: NORWAY\n"
"X-Poedit-SourceCharset: utf-8\n"
#: admin.py:111
msgid "Initial version."
msgstr "Initial versjon"
#: admin.py:125
#, python-format
msgid "Deleted %(verbose_name)s."
msgstr "Slettet %(verbose_name)s."
#: admin.py:143
#: templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:10
#: templates/reversion/recover_list.html:10
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Gjenopprett slettede %(name)s"
#: admin.py:252
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Gjenopprettet til forrige versjon, lagret den %(datetime)s"
#: admin.py:252
#: templates/reversion/object_history.html:23
#: templates/reversion/recover_list.html:30
msgid "DATETIME_FORMAT"
msgstr "j. F Y H:i"
#: admin.py:254
#, python-format
msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below."
msgstr "%(model)s \"%(name)s\" ble gjenopprettet. Du kan redigere den igjen nedenfor."
#: admin.py:337
#, python-format
msgid "Recover %(name)s"
msgstr "Gjenopprett %(name)s"
#: admin.py:349
#, python-format
msgid "Revert %(name)s"
msgstr "Tilbakestill %(name)s"
#: templates/reversion/object_history.html:8
msgid "Choose a date from the list below to revert to a previous version of this object."
msgstr "Velg en dato fra listen nedenfor for å gå tilbake til en tidligere versjon av dette objektet."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:23
msgid "Date/time"
msgstr "Dato/tid"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Bruker"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Kommentar"
#: templates/reversion/object_history.html:36
msgid "This object doesn't have a change history. It probably wasn't added via this admin site."
msgstr "Dette objektet har ingen endringshistorie. Objektet er sannsynligvis ikke blitt lagt inn via dette admin nettstedet."
#: templates/reversion/recover_form.html:7
#: templates/reversion/recover_list.html:7
#: templates/reversion/revision_form.html:7
msgid "Home"
msgstr "Hjem"
#: templates/reversion/recover_form.html:17
msgid "Press the save button below to recover this version of the object."
msgstr "Trykk på lagre-knappen nedenfor for å gjenopprette denne versjonen av objektet."
#: templates/reversion/recover_list.html:17
msgid "Choose a date from the list below to recover a deleted version of an object."
msgstr "Velg en dato fra listen nedenfor for å gjenopprette en slettet versjon av et objekt."
#: templates/reversion/recover_list.html:37
msgid "There are no deleted objects to recover."
msgstr "Finner ingen slettede objekter å gjenopprette."
#: templates/reversion/revision_form.html:11
msgid "History"
msgstr "Historie"
#: templates/reversion/revision_form.html:12
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Tilbakestill %(verbose_name)s"
#: templates/reversion/revision_form.html:25
msgid "Press the save button below to revert to this version of the object."
msgstr "Trykk på lagre-knappen under for å gå tilbake til denne versjonen av objektet."

Binary file not shown.

View File

@ -0,0 +1,135 @@
# Dutch translations for django-reversion extension
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Alexander Schoemaker <alexander.schoemaker@gmail.com>, 2012.
# Bouke Haarsma <bouke@webatoom.nl>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-12-12 10:41+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Alexander Schoemaker <alexander.schoemaker@gmail.com>\n"
"Language-Team: Dutch\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: admin.py:141
msgid "Initial version."
msgstr "Eerste versie."
#: admin.py:163
#, python-format
msgid "Deleted %(verbose_name)s."
msgstr "%(verbose_name)s is verwijderd."
#: admin.py:186 templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:11
#: templates/reversion/recover_list.html:11
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Herstel verwijderde %(name)s"
#: admin.py:297
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Vorige versie van %(datetime)s is teruggeplaatst"
#: admin.py:299
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"%(model)s \"%(name)s\" is succesvol teruggeplaatst. Je kunt het nu opnieuw "
"wijzigen."
#: admin.py:385
#, python-format
msgid "Recover %(name)s"
msgstr "Herstel %(name)s"
#: admin.py:399
#, python-format
msgid "Revert %(name)s"
msgstr "%(name)s terugplaatsen"
#: models.py:68
msgid "date created"
msgstr "datum aangemaakt"
#: models.py:74
msgid "user"
msgstr "gebruiker"
#: models.py:78
msgid "comment"
msgstr "toelichting"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr ""
"Selecteer een datum uit de lijst om een vorige versie terug te plaatsen."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:24
msgid "Date/time"
msgstr "Datum/tijdstip"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Gebruiker"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Toelichting"
#: templates/reversion/object_history.html:36
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Dit object bevat geen wijzigingshistorie. Vermoedelijk is het niet "
"vanuit sitebeheer toegevoegd."
#: templates/reversion/recover_form.html:8
#: templates/reversion/recover_list.html:8
#: templates/reversion/revision_form.html:8
msgid "Home"
msgstr ""
#: templates/reversion/recover_form.html:18
msgid "Press the save button below to recover this version of the object."
msgstr ""
"Klik op onderstaande knop om deze versie van het object te herstellen."
#: templates/reversion/recover_list.html:18
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr ""
"Selecteer een datum uit de lijst om een verwijderde versie van het object "
"te herstellen."
#: templates/reversion/recover_list.html:38
msgid "There are no deleted objects to recover."
msgstr "Er zijn geen verwijderde objecten die hersteld kunnen worden."
#: templates/reversion/revision_form.html:12
msgid "History"
msgstr "Geschiedenis"
#: templates/reversion/revision_form.html:13
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "%(verbose_name)s terugplaatsen"
#: templates/reversion/revision_form.html:26
msgid "Press the save button below to revert to this version of the object."
msgstr ""
"Klik op onderstaande knop om deze versie van het object terug te plaatsen."

Binary file not shown.

View File

@ -0,0 +1,124 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: reversion\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-03-21 20:05+0100\n"
"PO-Revision-Date: 2011-03-21 20:12+0100\n"
"Last-Translator: Zbigniew Siciarz <zbigniew@siciarz.net>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: admin.py:100
msgid "Initial version."
msgstr "Pierwsza wersja."
#: admin.py:115
#, python-format
msgid "Deleted %(verbose_name)s."
msgstr "Usunięto %(verbose_name)s"
#: admin.py:127
#: templates/reversion/change_list.html:8
#: templates/reversion/recover_list.html:10
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Odzyskaj usunięte %(name)s"
#: admin.py:218
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Przywrócono poprzednią wersję, zapisaną %(datetime)s"
#: admin.py:220
#, python-format
msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below."
msgstr "%(model)s \"%(name)s\" został pomyślnie przywrócony. Możesz go ponownie edytować poniżej."
#: admin.py:321
#, python-format
msgid "Recover %(name)s"
msgstr "Przywróć %(name)s"
#: admin.py:332
#, python-format
msgid "Revert %(name)s"
msgstr "Przywróć %(name)s"
#: templates/reversion/change_list.html:11
#, python-format
msgid "Add %(name)s"
msgstr "Dodaj %(name)s"
#: templates/reversion/object_history.html:8
msgid "Choose a date from the list below to revert to a previous version of this object."
msgstr "Wybierz datę z poniższej listy, by przywrócić ten obiekt do poprzedniej wersji."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:23
msgid "Date/time"
msgstr "Data/czas"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Użytkownik"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Komentarz"
#: templates/reversion/object_history.html:23
#: templates/reversion/recover_list.html:30
msgid "DATETIME_FORMAT"
msgstr "j. N Y, H:i"
#: templates/reversion/object_history.html:31
msgid "This object doesn't have a change history. It probably wasn't added via this admin site."
msgstr "Ten obiekt nie posiada historii zmian. Prawdopodobnie nie został dodany za pomocą tego panelu administracyjnego."
#: templates/reversion/recover_form.html:7
#: templates/reversion/recover_list.html:7
#: templates/reversion/revision_form.html:10
msgid "Home"
msgstr "Strona główna"
#: templates/reversion/recover_form.html:10
#, python-format
msgid "Recover deleted %(verbose_name)s"
msgstr "Przywróć usunięte %(verbose_name)s"
#: templates/reversion/recover_form.html:17
msgid "Press the save button below to recover this version of the object."
msgstr "Naciśnij przycisk Zapisz poniżej, by przywrócić tę wersję obiektu."
#: templates/reversion/recover_list.html:17
msgid "Choose a date from the list below to recover a deleted version of an object."
msgstr "Wybierz datę z poniższej listy, by przywrócić usuniętą wersję obiektu. "
#: templates/reversion/recover_list.html:37
msgid "There are no deleted objects to recover."
msgstr "Nie ma żadnych usuniętych obiektów do przywrócenia."
#: templates/reversion/revision_form.html:14
msgid "History"
msgstr "Historia"
#: templates/reversion/revision_form.html:15
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Przywróć %(verbose_name)s"
#: templates/reversion/revision_form.html:28
msgid "Press the save button below to revert to this version of the object."
msgstr "Naciśnij przycisk Zapisz poniżej, by przywrócić tę wersję obiektu."
#~ msgid "Recover %s"
#~ msgstr "Przywróć %s"

Binary file not shown.

View File

@ -0,0 +1,113 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-29 13:04+0200\n"
"PO-Revision-Date: 2009-08-29 13:44+0100\n"
"Last-Translator: Partec <beto@tangerinalab.com>\n"
"Language-Team: Tangerina Lab <beto@tangerinalab.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: .\admin.py:128
#: .\templates\reversion\change_list.html.py:7
#: .\templates\reversion\recover_list.html.py:10
msgid "Recover deleted %(name)s"
msgstr "Recuperar %(name)s excluído"
#: .\admin.py:173
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Revertido para versão anterior, salva em %(datetime)s"
#: .\admin.py:175
#, python-format
msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below."
msgstr "%(model)s \"%(name)s\" foi revertido com sucesso. Você pode editar novamente abaixo."
#: .\admin.py:271
#, python-format
msgid "Recover %(name)s"
msgstr "Recuperar %(name)s"
#: .\admin.py:281
#, python-format
msgid "Revert %(name)s"
msgstr "Reverter %(name)s"
#: .\templates\reversion\change_list.html.py:9
#, python-format
msgid "Add %(name)s"
msgstr "Adicionar %(name)s"
#: .\templates\reversion\object_history.html.py:8
msgid "Choose a date from the list below to revert to a previous version of this object."
msgstr "Escolha uma data da lista abaixo para reverter para uma versão anterior deste objeto."
#: .\templates\reversion\object_history.html.py:15
#: .\templates\reversion\recover_list.html.py:23
msgid "Date/time"
msgstr "Data/hora"
#: .\templates\reversion\object_history.html.py:16
msgid "User"
msgstr "Usuário"
#: .\templates\reversion\object_history.html.py:17
msgid "Comment"
msgstr "Comentário"
#: .\templates\reversion\object_history.html.py:23
#: .\templates\reversion\recover_list.html.py:30
msgid "DATETIME_FORMAT"
msgstr "d/m/Y, G:i"
#: .\templates\reversion\object_history.html.py:31
msgid "This object doesn't have a change history. It probably wasn't added via this admin site."
msgstr "Este objeto não possui um histórico de mudanças. Ele provavelmente não foi adicionado por este site de admin."
#: .\templates\reversion\recover_form.html.py:14
#: .\templates\reversion\recover_list.html.py:7
#: .\templates\reversion\revision_form.html.py:14
msgid "Home"
msgstr "Home"
#: .\templates\reversion\recover_form.html.py:17
#, python-format
msgid "Recover deleted %(verbose_name)s"
msgstr "Recuperar %(verbose_name)s excluído"
#: .\templates\reversion\recover_form.html.py:24
msgid "Press the save button below to recover this version of the object."
msgstr "Pressione o botão salvar, abaixo, para recuperar essa versão do objeto."
#: .\templates\reversion\recover_list.html.py:17
msgid "Choose a date from the list below to recover a deleted version of an object."
msgstr "Escolha uma data da lista abaixo para recuperar uma versão excluída de um objeto."
#: .\templates\reversion\recover_list.html.py:37
msgid "There are no deleted objects to recover."
msgstr "Não há objetos excluídos para recuperar."
#: .\templates\reversion\revision_form.html.py:18
msgid "History"
msgstr "Histórico"
#: .\templates\reversion\revision_form.html.py:19
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Reverter %(verbose_name)s"
#: .\templates\reversion\revision_form.html.py:32
msgid "Press the save button below to revert to this version of the object."
msgstr "Pressione o botão salvar, abaixo, para reverter para essa versão do objeto."
#~ msgid "Recover %s"
#~ msgstr "Recuperar %s"

Binary file not shown.

View File

@ -0,0 +1,118 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: reversion\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-02-03 08:31+0100\n"
"PO-Revision-Date: 2009-10-14 22:21+0300\n"
"Last-Translator: Alexander Yakovlev <ayakovlev@rambler.ru>\n"
"Language-Team: Russian <ru@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Russian\n"
"X-Poedit-Country: RUSSIAN FEDERATION\n"
#: admin.py:122
#: templates/reversion/change_list.html:8
#: templates/reversion/recover_list.html:9
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Восстановить удаленный %(name)s"
#: admin.py:155
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Возвращено к предыдущей версии, сохраненной %(datetime)s"
#: admin.py:157
#, python-format
msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below."
msgstr "%(model)s \"%(name)s\" возвращен. Можете продолжить его редактирование."
#: admin.py:227
#, python-format
msgid "Recover %s"
msgstr "Восстановить %s"
#: admin.py:243
#, python-format
msgid "Revert %(name)s"
msgstr "Вернуть %(name)s"
#: templates/reversion/change_list.html:11
#, python-format
msgid "Add %(name)s"
msgstr "Добавить %(name)s"
#: templates/reversion/object_history.html:8
msgid "Choose a date from the list below to revert to a previous version of this object."
msgstr "Выберите дату из списка, чтобы вернуть предыдущую версию этого объекта."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:21
msgid "Date/time"
msgstr "Дата/время"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Пользователь"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Комментарий"
#: templates/reversion/object_history.html:23
#: templates/reversion/recover_list.html:28
msgid "DATETIME_FORMAT"
msgstr "d.m.Y H:i"
#: templates/reversion/object_history.html:31
msgid "This object doesn't have a change history. It probably wasn't added via this admin site."
msgstr "У этого объекта нет истории изменений. Возможно, он был добавлен не через администраторский сайт."
#: templates/reversion/recover_form.html:14
#: templates/reversion/recover_list.html:6
#: templates/reversion/revision_form.html:14
msgid "Home"
msgstr "Начало"
#: templates/reversion/recover_form.html:17
#, python-format
msgid "Recover deleted %(verbose_name)s"
msgstr "Восстановить удаленный %(verbose_name)s"
#: templates/reversion/recover_form.html:18
#, python-format
msgid "Recover %(name)s"
msgstr "Восстановить %(name)s"
#: templates/reversion/recover_form.html:24
msgid "Press the save button below to recover this version of the object."
msgstr "Нажмите кнопку \"Сохранить\" далее, чтобы восстановить эту версию объекта."
#: templates/reversion/recover_list.html:15
msgid "Choose a date from the list below to recover a deleted version of an object."
msgstr "Выберите дату из списка, чтобы восстановить удаленную версию объекта."
#: templates/reversion/recover_list.html:35
msgid "There are no deleted objects to recover."
msgstr "Не найдено удаленных объектов для восстановления."
#: templates/reversion/revision_form.html:18
msgid "History"
msgstr "История"
#: templates/reversion/revision_form.html:19
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Вернуть %(verbose_name)s"
#: templates/reversion/revision_form.html:32
msgid "Press the save button below to revert to this version of the object."
msgstr "Нажмите кнопку \"Сохранить\" далее, чтобы вернуть эту версию объекта."

Binary file not shown.

View File

@ -0,0 +1,128 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-14 19:05+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Juraj Bubniak <translations@jbub.eu>\n"
"Language-Team: Slovak <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: admin.py:153
msgid "Initial version."
msgstr "Počiatočná verzia."
#: admin.py:187 templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:11
#: templates/reversion/recover_list.html:11
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Obnoviť vymazaný %(name)s"
#: admin.py:304
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Obnovená predošlá verzia, uložená %(datetime)s"
#: admin.py:306
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"Objekt %(model)s \"%(name)s\" bol úspešne obnovený. Môžete ho znovu upraviť "
"nižšie."
#: admin.py:391
#, python-format
msgid "Recover %(name)s"
msgstr "Obnoviť %(name)s"
#: admin.py:405
#, python-format
msgid "Revert %(name)s"
msgstr "Vrátiť sa k predošlej verzii %(name)s"
#: models.py:55
msgid "date created"
msgstr "dátum vytvorenia"
#: models.py:61
msgid "user"
msgstr "používateľ"
#: models.py:65
msgid "comment"
msgstr "komentár"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr ""
"Vyberte dátum z nižšie uvedeného zoznamu pre návrat k predošlej verzii tohto "
"objektu."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:24
msgid "Date/time"
msgstr "Dátum/čas"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Používateľ"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Komentár"
#: templates/reversion/object_history.html:36
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Tento objekt nemá históriu zmien. Pravdepodobne nebol pridaný cez túto "
"admin stránku."
#: templates/reversion/recover_form.html:8
#: templates/reversion/recover_list.html:8
#: templates/reversion/revision_form.html:8
msgid "Home"
msgstr "Domov"
#: templates/reversion/recover_form.html:18
msgid "Press the save button below to recover this version of the object."
msgstr "Pre obnovenie tejto verzie objektu kliknite na tlačidlo uložiť nižšie."
#: templates/reversion/recover_list.html:18
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr ""
"Pre obnovenie vymazanej verzie objektu vyberte dátum z nižšie uvedeného zoznamu."
#: templates/reversion/recover_list.html:38
msgid "There are no deleted objects to recover."
msgstr "Niesú dostupné žiadne vymazané objekty pre obnovenie."
#: templates/reversion/revision_form.html:12
msgid "History"
msgstr "História"
#: templates/reversion/revision_form.html:13
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Vrátiť sa k predošlej verzii %(verbose_name)s"
#: templates/reversion/revision_form.html:26
msgid "Press the save button below to revert to this version of the object."
msgstr "Pre návrat na túto verziu objektu kliknite na tlačidlo uložiť nižšie."

Binary file not shown.

View File

@ -0,0 +1,138 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-06-13 09:56+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: admin.py:139
msgid "Initial version."
msgstr "Första versionen."
#: admin.py:161
#, python-format
msgid "Deleted %(verbose_name)s."
msgstr "Tog bort %(verbose_name)s"
#: admin.py:181 templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:10
#: templates/reversion/recover_list.html:10
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Återskapa bortagna %(name)s"
#: admin.py:292
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Återgick till föregående version, sparad %(datetime)s"
#: admin.py:292 admin.py:456 templates/reversion/object_history.html:23
#: templates/reversion/recover_list.html:30
msgid "DATETIME_FORMAT"
msgstr "Y-m-d H:i"
#: admin.py:294
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr ""
"Återställandet av %(model)s \"%(name)s\" lyckades. Du kan redigera den igen "
"här nedan."
#: admin.py:377
#, python-format
msgid "Recover %(name)s"
msgstr "Återskapa %(name)s"
#: admin.py:388
#, python-format
msgid "Revert %(name)s"
msgstr "Återställ %(name)s"
#: models.py:68
msgid "date created"
msgstr "datum skapad"
#: models.py:74
msgid "user"
msgstr "användare"
#: models.py:78
msgid "comment"
msgstr "kommentar"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr ""
"Välj ett datum från listan här under för att återställa till en tidigare "
"version av det här objektet."
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:23
msgid "Date/time"
msgstr "Datum/tid"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "Användare"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "Kommentar"
#: templates/reversion/object_history.html:36
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Det här objektet saknar ändringshistorik. Det skapades förmodligen inte via "
"den här admin-sajten."
#: templates/reversion/recover_form.html:7
#: templates/reversion/recover_list.html:7
#: templates/reversion/revision_form.html:7
msgid "Home"
msgstr "Hem"
#: templates/reversion/recover_form.html:17
msgid "Press the save button below to recover this version of the object."
msgstr "Tryck på spara här nedan fär att återskapa den här versionen."
#: templates/reversion/recover_list.html:17
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr ""
"Välj ett datum i listan här nedan för att återskapa en borttagen version."
#: templates/reversion/recover_list.html:37
msgid "There are no deleted objects to recover."
msgstr "Det finns inga borttagna objekt att återskapa."
#: templates/reversion/revision_form.html:11
msgid "History"
msgstr "Historik"
#: templates/reversion/revision_form.html:12
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Återställ %(verbose_name)s"
#: templates/reversion/revision_form.html:25
msgid "Press the save button below to revert to this version of the object."
msgstr "Tryck på spara här nedan för att återställa den här versionen."

Binary file not shown.

View File

@ -0,0 +1,134 @@
# Translation of django-reversion into Ukrainian.
# This file is distributed under the same license as the django-reversion package.
# Illia Volochii <illia.volochii@gmail.com>, 2017.
msgid ""
msgstr ""
"Project-Id-Version: django-reversion\n"
"Report-Msgid-Bugs-To: https://github.com/etianen/django-reversion/issues\n"
"POT-Creation-Date: 2017-11-03 12:02+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Illia Volochii <illia.volochii@gmail.com>\n"
"Language-Team: Ukrainian\n"
"Language: uk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: reversion/admin.py:83
msgid "Initial version."
msgstr "Початкова версія."
#: reversion/admin.py:197
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "Повернуто до попередньої версії, яка збережена %(datetime)s"
#: reversion/admin.py:221
#, python-format
msgid "Recover %(name)s"
msgstr "Відновити %(name)s"
#: reversion/admin.py:237
#, python-format
msgid "Revert %(name)s"
msgstr "Повернути %(name)s"
#: reversion/admin.py:272 reversion/templates/reversion/change_list.html:7
#: reversion/templates/reversion/recover_form.html:10
#: reversion/templates/reversion/recover_list.html:10
#, python-format
msgid "Recover deleted %(name)s"
msgstr "Відновити видалені %(name)s"
#: reversion/models.py:31
#, python-format
msgid "Could not save %(object_repr)s version - missing dependency."
msgstr "Неможливо зберегти версію \"%(object_repr)s\" - відсутня залежність."
#: reversion/models.py:45
msgid "date created"
msgstr "дата створення"
#: reversion/models.py:54
msgid "user"
msgstr "користувач"
#: reversion/models.py:60
msgid "comment"
msgstr "коментар"
#: reversion/models.py:242
#, python-format
msgid "Could not load %(object_repr)s version - incompatible version data."
msgstr ""
"Неможливо завантажити версію \"%(object_repr)s\" - несумісні дані версій."
#: reversion/models.py:246
#, python-format
msgid "Could not load %(object_repr)s version - unknown serializer %(format)s."
msgstr ""
"Неможливо завантажити версію \"%(object_repr)s\" - невідомий серіалізатор "
"%(format)s."
#: reversion/templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr ""
"Виберіть дату із списку нижче, щоб повернутися до попередньої версії цього "
"об'єкта."
#: reversion/templates/reversion/object_history.html:15
#: reversion/templates/reversion/recover_list.html:23
msgid "Date/time"
msgstr "Дата/час"
#: reversion/templates/reversion/object_history.html:16
msgid "User"
msgstr "Користувач"
#: reversion/templates/reversion/object_history.html:17
msgid "Action"
msgstr "Дія"
#: reversion/templates/reversion/object_history.html:38
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Цей об'єкт не має історії змін. Напевно, він був доданий не через цей сайт "
"адміністрування."
#: reversion/templates/reversion/recover_form.html:7
#: reversion/templates/reversion/recover_list.html:7
#: reversion/templates/reversion/revision_form.html:7
msgid "Home"
msgstr "Домівка"
#: reversion/templates/reversion/recover_form.html:20
msgid "Press the save button below to recover this version of the object."
msgstr "Натисніть кнопку \"Зберегти\" нижче, щоб відновити цю версію об'єкта."
#: reversion/templates/reversion/recover_list.html:17
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr "Виберіть дату із списку нижче, щоб відновити видалену версію об'єкта."
#: reversion/templates/reversion/recover_list.html:37
msgid "There are no deleted objects to recover."
msgstr "Не знайдено видалених об'єктів для відновлення."
#: reversion/templates/reversion/revision_form.html:11
msgid "History"
msgstr "Історія"
#: reversion/templates/reversion/revision_form.html:12
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "Повернути %(verbose_name)s"
#: reversion/templates/reversion/revision_form.html:21
msgid "Press the save button below to revert to this version of the object."
msgstr ""
"Натисніть кнопку \"Зберегти\" нижче, щоб повернутися до цієї версії об'єкта."

Binary file not shown.

View File

@ -0,0 +1,121 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-06-12 14:21+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: admin.py:160
msgid "Initial version."
msgstr "初始版本"
#: admin.py:194 templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:11
#: templates/reversion/recover_list.html:11
#, python-format
msgid "Recover deleted %(name)s"
msgstr "恢复已删除的 %(name)s"
#: admin.py:311
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "恢复到 %(datetime)s 的版本"
#: admin.py:313
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr "%(model)s \"%(name)s\" 已成功恢复,你可以在下面在此编辑它。"
#: admin.py:398
#, python-format
msgid "Recover %(name)s"
msgstr "恢复 %(name)s"
#: admin.py:412
#, python-format
msgid "Revert %(name)s"
msgstr "恢复 %(name)s"
#: models.py:55
msgid "date created"
msgstr "创建日期"
#: models.py:62
msgid "user"
msgstr "用户"
#: models.py:66
msgid "comment"
msgstr "评论"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr "单击下方的日期以恢复当前对象到之前的版本。"
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:24
msgid "Date/time"
msgstr "时间"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "用户"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "评论"
#: templates/reversion/object_history.html:38
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr "此对象不存在任何变更历史,它可能不是通过管理站点添加的。"
#: templates/reversion/recover_form.html:8
#: templates/reversion/recover_list.html:8
#: templates/reversion/revision_form.html:8
msgid "Home"
msgstr "首页"
#: templates/reversion/recover_form.html:18
msgid "Press the save button below to recover this version of the object."
msgstr "单击保存按钮以恢复为此版本。"
#: templates/reversion/recover_list.html:18
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr "单击下方的日期以恢复一个已删除的对象。"
#: templates/reversion/recover_list.html:38
msgid "There are no deleted objects to recover."
msgstr "没有可供恢复的已删除对象。"
#: templates/reversion/revision_form.html:12
msgid "History"
msgstr "历史"
#: templates/reversion/revision_form.html:13
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "恢复 %(verbose_name)s"
#: templates/reversion/revision_form.html:26
msgid "Press the save button below to revert to this version of the object."
msgstr "单击保存按钮将此对象恢复到此版本。"

Binary file not shown.

View File

@ -0,0 +1,121 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-06-12 14:21+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: admin.py:160
msgid "Initial version."
msgstr "初始版本"
#: admin.py:194 templates/reversion/change_list.html:7
#: templates/reversion/recover_form.html:11
#: templates/reversion/recover_list.html:11
#, python-format
msgid "Recover deleted %(name)s"
msgstr "恢复已删除的 %(name)s"
#: admin.py:311
#, python-format
msgid "Reverted to previous version, saved on %(datetime)s"
msgstr "恢复到 %(datetime)s 的版本"
#: admin.py:313
#, python-format
msgid ""
"The %(model)s \"%(name)s\" was reverted successfully. You may edit it again "
"below."
msgstr "%(model)s \"%(name)s\" 已成功恢复,你可以在下面在此编辑它。"
#: admin.py:398
#, python-format
msgid "Recover %(name)s"
msgstr "恢复 %(name)s"
#: admin.py:412
#, python-format
msgid "Revert %(name)s"
msgstr "恢复 %(name)s"
#: models.py:55
msgid "date created"
msgstr "创建日期"
#: models.py:62
msgid "user"
msgstr "用户"
#: models.py:66
msgid "comment"
msgstr "评论"
#: templates/reversion/object_history.html:8
msgid ""
"Choose a date from the list below to revert to a previous version of this "
"object."
msgstr "单击下方的日期以恢复当前对象到之前的版本。"
#: templates/reversion/object_history.html:15
#: templates/reversion/recover_list.html:24
msgid "Date/time"
msgstr "时间"
#: templates/reversion/object_history.html:16
msgid "User"
msgstr "用户"
#: templates/reversion/object_history.html:17
msgid "Comment"
msgstr "评论"
#: templates/reversion/object_history.html:38
msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr "此对象不存在任何变更历史,它可能不是通过管理站点添加的。"
#: templates/reversion/recover_form.html:8
#: templates/reversion/recover_list.html:8
#: templates/reversion/revision_form.html:8
msgid "Home"
msgstr "首页"
#: templates/reversion/recover_form.html:18
msgid "Press the save button below to recover this version of the object."
msgstr "单击保存按钮以恢复为此版本。"
#: templates/reversion/recover_list.html:18
msgid ""
"Choose a date from the list below to recover a deleted version of an object."
msgstr "单击下方的日期以恢复一个已删除的对象。"
#: templates/reversion/recover_list.html:38
msgid "There are no deleted objects to recover."
msgstr "没有可供恢复的已删除对象。"
#: templates/reversion/revision_form.html:12
msgid "History"
msgstr "历史"
#: templates/reversion/revision_form.html:13
#, python-format
msgid "Revert %(verbose_name)s"
msgstr "恢复 %(verbose_name)s"
#: templates/reversion/revision_form.html:26
msgid "Press the save button below to revert to this version of the object."
msgstr "单击保存按钮将此对象恢复到此版本。"

View File

View File

@ -0,0 +1,57 @@
from __future__ import unicode_literals
from django.apps import apps
from django.contrib import admin
from django.core.management.base import BaseCommand, CommandError
from reversion.revisions import is_registered
class BaseRevisionCommand(BaseCommand):
def add_arguments(self, parser):
super(BaseRevisionCommand, self).add_arguments(parser)
parser.add_argument(
"app_label",
metavar="app_label",
nargs="*",
help="Optional app_label or app_label.model_name list.",
)
parser.add_argument(
"--using",
default=None,
help="The database to query for revision data.",
)
parser.add_argument(
"--model-db",
default=None,
help="The database to query for model data.",
)
def get_models(self, options):
# Load admin classes.
admin.autodiscover()
# Get options.
app_labels = options["app_label"]
# Parse model classes.
if len(app_labels) == 0:
selected_models = apps.get_models()
else:
selected_models = set()
for label in app_labels:
if "." in label:
# This is an app.Model specifier.
try:
model = apps.get_model(label)
except LookupError:
raise CommandError("Unknown model: {}".format(label))
selected_models.add(model)
else:
# This is just an app - no model qualifier.
app_label = label
try:
app = apps.get_app_config(app_label)
except LookupError:
raise CommandError("Unknown app: {}".format(app_label))
selected_models.update(app.get_models())
for model in selected_models:
if is_registered(model):
yield model

View File

@ -0,0 +1,74 @@
from __future__ import unicode_literals
from django.db import reset_queries, transaction, router
from reversion.models import Revision, Version, _safe_subquery
from reversion.management.commands import BaseRevisionCommand
from reversion.revisions import create_revision, set_comment, add_to_revision
class Command(BaseRevisionCommand):
help = "Creates initial revisions for a given app [and model]."
def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument(
"--comment",
action="store",
default="Initial version.",
help="Specify the comment to add to the revisions. Defaults to 'Initial version'.")
parser.add_argument(
"--batch-size",
action="store",
type=int,
default=500,
help="For large sets of data, revisions will be populated in batches. Defaults to 500.",
)
def handle(self, *app_labels, **options):
verbosity = options["verbosity"]
using = options["using"]
model_db = options["model_db"]
comment = options["comment"]
batch_size = options["batch_size"]
# Create revisions.
using = using or router.db_for_write(Revision)
with transaction.atomic(using=using):
for model in self.get_models(options):
# Check all models for empty revisions.
if verbosity >= 1:
self.stdout.write("Creating revisions for {name}".format(
name=model._meta.verbose_name,
))
created_count = 0
live_objs = _safe_subquery(
"exclude",
model._default_manager.using(model_db),
model._meta.pk.name,
Version.objects.using(using).get_for_model(
model,
model_db=model_db,
),
"object_id",
)
# Save all the versions.
ids = list(live_objs.values_list("pk", flat=True).order_by())
total = len(ids)
for i in range(0, total, batch_size):
chunked_ids = ids[i:i+batch_size]
objects = live_objs.in_bulk(chunked_ids)
for obj in objects.values():
with create_revision(using=using):
set_comment(comment)
add_to_revision(obj, model_db=model_db)
created_count += 1
reset_queries()
if verbosity >= 2:
self.stdout.write("- Created {created_count} / {total}".format(
created_count=created_count,
total=total,
))
# Print out a message, if feeling verbose.
if verbosity >= 1:
self.stdout.write("- Created {total} / {total}".format(
total=total,
))

View File

@ -0,0 +1,95 @@
from __future__ import unicode_literals
from datetime import timedelta
from django.db import transaction, models, router
from django.utils import timezone
from reversion.models import Revision, Version
from reversion.management.commands import BaseRevisionCommand
class Command(BaseRevisionCommand):
help = "Deletes revisions for a given app [and model]."
def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument(
"--days",
default=0,
type=int,
help="Delete only revisions older than the specified number of days.",
)
parser.add_argument(
"--keep",
default=0,
type=int,
help="Keep the specified number of revisions (most recent) for each object.",
)
def handle(self, *app_labels, **options):
verbosity = options["verbosity"]
using = options["using"]
model_db = options["model_db"]
days = options["days"]
keep = options["keep"]
# Delete revisions.
using = using or router.db_for_write(Revision)
with transaction.atomic(using=using):
revision_query = models.Q()
keep_revision_ids = set()
# By default, delete nothing.
can_delete = False
# Get all revisions for the given revision manager and model.
for model in self.get_models(options):
if verbosity >= 1:
self.stdout.write("Finding stale revisions for {name}".format(
name=model._meta.verbose_name,
))
# Find all matching revision IDs.
model_query = Version.objects.using(using).get_for_model(
model,
model_db=model_db,
)
if keep:
overflow_object_ids = list(Version.objects.using(using).get_for_model(
model,
model_db=model_db,
).order_by().values_list("object_id").annotate(
count=models.Count("object_id"),
).filter(
count__gt=keep,
).values_list("object_id", flat=True).iterator())
# Only delete overflow revisions.
model_query = model_query.filter(object_id__in=overflow_object_ids)
for object_id in overflow_object_ids:
if verbosity >= 2:
self.stdout.write("- Finding stale revisions for {name} #{object_id}".format(
name=model._meta.verbose_name,
object_id=object_id,
))
# But keep the underflow revisions.
keep_revision_ids.update(Version.objects.using(using).get_for_object_reference(
model,
object_id,
model_db=model_db,
).values_list("revision_id", flat=True)[:keep].iterator())
# Add to revision query.
revision_query |= models.Q(
pk__in=model_query.order_by().values_list("revision_id", flat=True)
)
# If we have at least one model, then we can delete.
can_delete = True
if can_delete:
revisions_to_delete = Revision.objects.using(using).filter(
revision_query,
date_created__lt=timezone.now() - timedelta(days=days),
).exclude(
pk__in=keep_revision_ids
).order_by()
else:
revisions_to_delete = Revision.objects.using(using).none()
# Print out a message, if feeling verbose.
if verbosity >= 1:
self.stdout.write("Deleting {total} revisions...".format(
total=revisions_to_delete.count(),
))
revisions_to_delete.delete()

51
reversion/middleware.py Normal file
View File

@ -0,0 +1,51 @@
import sys
from reversion.revisions import create_revision as create_revision_base
from reversion.views import _request_creates_revision, _set_user_from_request, create_revision
class RevisionMiddleware(object):
"""Wraps the entire request in a revision."""
manage_manually = False
using = None
atomic = True
def __init__(self, get_response=None):
super(RevisionMiddleware, self).__init__()
# Support Django 1.10 middleware.
if get_response is not None:
self.get_response = create_revision(
manage_manually=self.manage_manually,
using=self.using,
atomic=self.atomic
)(get_response)
def process_request(self, request):
if _request_creates_revision(request):
context = create_revision_base(
manage_manually=self.manage_manually,
using=self.using,
atomic=self.atomic
)
context.__enter__()
if not hasattr(request, "_revision_middleware"):
setattr(request, "_revision_middleware", {})
request._revision_middleware[self] = context
def _close_revision(self, request, is_exception):
if self in getattr(request, "_revision_middleware", {}):
_set_user_from_request(request)
request._revision_middleware.pop(self).__exit__(*sys.exc_info() if is_exception else (None, None, None))
def process_response(self, request, response):
self._close_revision(request, False)
return response
def process_exception(self, request, exception):
self._close_revision(request, True)
def __call__(self, request):
return self.get_response(request)

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.db.models.deletion
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Revision',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('manager_slug', models.CharField(default='default', max_length=200, db_index=True)),
('date_created', models.DateTimeField(auto_now_add=True, help_text='The date and time this revision was created.', verbose_name='date created', db_index=True)),
('comment', models.TextField(help_text='A text comment on this revision.', verbose_name='comment', blank=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, help_text='The user who created this revision.', null=True, verbose_name='user')),
],
options={
"ordering": ("-pk",)
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Version',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('object_id', models.TextField(help_text='Primary key of the model under version control.')),
('object_id_int', models.IntegerField(help_text="An indexed, integer version of the stored model's primary key, used for faster lookups.", null=True, db_index=True, blank=True)),
('format', models.CharField(help_text='The serialization format used by this model.', max_length=255)),
('serialized_data', models.TextField(help_text='The serialized form of this version of the model.')),
('object_repr', models.TextField(help_text='A string representation of the object.')),
('content_type', models.ForeignKey(help_text='Content type of the model under version control.', on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('revision', models.ForeignKey(help_text='The revision that contains this version.', on_delete=django.db.models.deletion.CASCADE, to='reversion.Revision')),
],
options={
"ordering": ("-pk",)
},
bases=(models.Model,),
),
]

View File

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-06-06 13:22
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
replaces = [('reversion', '0001_initial'), ('reversion', '0002_auto_20141216_1509'), ('reversion', '0003_auto_20160601_1600'), ('reversion', '0004_auto_20160611_1202')]
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Revision',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_created', models.DateTimeField(db_index=True, help_text='The date and time this revision was created.', verbose_name='date created')),
('comment', models.TextField(blank=True, help_text='A text comment on this revision.', verbose_name='comment')),
('user', models.ForeignKey(blank=True, help_text='The user who created this revision.', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='user')),
],
options={
"ordering": ("-pk",)
},
),
migrations.CreateModel(
name='Version',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.CharField(help_text='Primary key of the model under version control.', max_length=191)),
('format', models.CharField(help_text='The serialization format used by this model.', max_length=255)),
('serialized_data', models.TextField(help_text='The serialized form of this version of the model.')),
('object_repr', models.TextField(help_text='A string representation of the object.')),
('content_type', models.ForeignKey(help_text='Content type of the model under version control.', on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('revision', models.ForeignKey(help_text='The revision that contains this version.', on_delete=django.db.models.deletion.CASCADE, to='reversion.Revision')),
('db', models.CharField(help_text='The database the model under version control is stored in.', max_length=191)),
],
options={
"ordering": ("-pk",)
},
),
migrations.AlterUniqueTogether(
name='version',
unique_together=set([('db', 'content_type', 'object_id', 'revision')]),
),
]

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('reversion', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='revision',
name='manager_slug',
field=models.CharField(default='default', max_length=191, db_index=True),
),
]

View File

@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.6 on 2016-06-01 16:00
from __future__ import unicode_literals
from collections import defaultdict
from django.db import DEFAULT_DB_ALIAS, migrations, models, router
from django.apps import apps as live_apps
def de_dupe_version_table(apps, schema_editor):
"""
Removes some duplicate Version models that may have crept into the database and will prevent the
unique index being added by migration 0004.
"""
db_alias = schema_editor.connection.alias
Version = apps.get_model("reversion", "Version")
keep_version_ids = Version.objects.using(db_alias).order_by().values_list(
# Group by the unique constraint we intend to enforce.
"revision_id",
"content_type_id",
"object_id",
).annotate(
# Add in the most recent id for each duplicate row.
max_pk=models.Max("pk"),
).values_list("max_pk", flat=True)
# Do not do anything if we're keeping all ids anyway.
if keep_version_ids.count() == Version.objects.using(db_alias).all().count():
return
# Delete all duplicate versions. Can't do this as a delete with subquery because MySQL doesn't like running a
# subquery on the table being updated/deleted.
delete_version_ids = list(Version.objects.using(db_alias).exclude(
pk__in=keep_version_ids,
).values_list("pk", flat=True))
Version.objects.using(db_alias).filter(
pk__in=delete_version_ids,
).delete()
def set_version_db(apps, schema_editor):
"""
Updates the db field in all Version models to point to the correct write
db for the model.
"""
db_alias = schema_editor.connection.alias
Version = apps.get_model("reversion", "Version")
content_types = Version.objects.using(db_alias).order_by().values_list(
"content_type_id",
"content_type__app_label",
"content_type__model"
).distinct()
model_dbs = defaultdict(list)
for content_type_id, app_label, model_name in content_types:
# We need to be able to access all models in the project, and we can't
# specify them up-front in the migration dependencies. So we have to
# just get the live model. This should be fine, since we don't actually
# manipulate the live model in any way.
try:
model = live_apps.get_model(app_label, model_name)
except LookupError:
# If the model appears not to exist, play it safe and use the default db.
db = "default"
else:
db = router.db_for_write(model)
model_dbs[db].append(content_type_id)
# Update db field.
# speedup for case when there is only default db
if DEFAULT_DB_ALIAS in model_dbs and len(model_dbs) == 1:
Version.objects.using(db_alias).update(db=DEFAULT_DB_ALIAS)
else:
for db, content_type_ids in model_dbs.items():
Version.objects.using(db_alias).filter(
content_type__in=content_type_ids
).update(db=db)
class Migration(migrations.Migration):
dependencies = [
('reversion', '0002_auto_20141216_1509'),
]
operations = [
migrations.RemoveField(
model_name='revision',
name='manager_slug',
),
migrations.RemoveField(
model_name='version',
name='object_id_int',
),
migrations.AlterField(
model_name='version',
name='object_id',
field=models.CharField(help_text='Primary key of the model under version control.', max_length=191),
),
migrations.AlterField(
model_name='revision',
name='date_created',
field=models.DateTimeField(db_index=True, help_text='The date and time this revision was created.', verbose_name='date created'),
),
migrations.AddField(
model_name='version',
name='db',
field=models.CharField(null=True, help_text='The database the model under version control is stored in.', max_length=191),
),
migrations.RunPython(de_dupe_version_table),
migrations.RunPython(set_version_db),
]

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-06-11 12:02
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reversion', '0003_auto_20160601_1600'),
]
operations = [
migrations.AlterField(
model_name='version',
name='db',
field=models.CharField(help_text='The database the model under version control is stored in.', max_length=191),
),
migrations.AlterUniqueTogether(
name='version',
unique_together=set([('db', 'content_type', 'object_id', 'revision')]),
),
]

View File

356
reversion/models.py Normal file
View File

@ -0,0 +1,356 @@
from __future__ import unicode_literals
from collections import defaultdict
from itertools import chain
from django.contrib.contenttypes.models import ContentType
try:
from django.contrib.contenttypes.fields import GenericForeignKey
except ImportError: # Django < 1.9 pragma: no cover
from django.contrib.contenttypes.generic import GenericForeignKey
from django.conf import settings
from django.core import serializers
from django.core.serializers.base import DeserializationError
from django.core.exceptions import ObjectDoesNotExist
from django.db import models, IntegrityError, transaction, router, connections
from django.db.models.deletion import Collector
from django.db.models.expressions import RawSQL
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _, ugettext
from django.utils.encoding import force_text, python_2_unicode_compatible
from reversion.errors import RevertError
from reversion.revisions import _get_options, _get_content_type, _follow_relations_recursive
def _safe_revert(versions):
unreverted_versions = []
for version in versions:
try:
with transaction.atomic(using=version.db):
version.revert()
except (IntegrityError, ObjectDoesNotExist):
unreverted_versions.append(version)
if len(unreverted_versions) == len(versions):
raise RevertError(ugettext("Could not save %(object_repr)s version - missing dependency.") % {
"object_repr": unreverted_versions[0],
})
if unreverted_versions:
_safe_revert(unreverted_versions)
@python_2_unicode_compatible
class Revision(models.Model):
"""A group of related serialized versions."""
date_created = models.DateTimeField(
db_index=True,
verbose_name=_("date created"),
help_text="The date and time this revision was created.",
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True,
null=True,
on_delete=models.SET_NULL,
verbose_name=_("user"),
help_text="The user who created this revision.",
)
comment = models.TextField(
blank=True,
verbose_name=_("comment"),
help_text="A text comment on this revision.",
)
def revert(self, delete=False):
# Group the models by the database of the serialized model.
versions_by_db = defaultdict(list)
for version in self.version_set.iterator():
versions_by_db[version.db].append(version)
# For each db, perform a separate atomic revert.
for version_db, versions in versions_by_db.items():
with transaction.atomic(using=version_db):
# Optionally delete objects no longer in the current revision.
if delete:
# Get a set of all objects in this revision.
old_revision = set()
for version in versions:
model = version._model
try:
# Load the model instance from the same DB as it was saved under.
old_revision.add(model._default_manager.using(version.db).get(pk=version.object_id))
except model.DoesNotExist:
pass
# Calculate the set of all objects that are in the revision now.
current_revision = chain.from_iterable(
_follow_relations_recursive(obj)
for obj in old_revision
)
# Delete objects that are no longer in the current revision.
collector = Collector(using=version_db)
collector.collect([item for item in current_revision if item not in old_revision])
collector.delete()
# Attempt to revert all revisions.
_safe_revert(versions)
def __str__(self):
return ", ".join(force_text(version) for version in self.version_set.all())
class Meta:
app_label = "reversion"
ordering = ("-pk",)
class SubquerySQL(RawSQL):
def as_sql(self, compiler, connection):
return self.sql, self.params
class VersionQuerySet(models.QuerySet):
def get_for_model(self, model, model_db=None):
model_db = model_db or router.db_for_write(model)
content_type = _get_content_type(model, self.db)
return self.filter(
content_type=content_type,
db=model_db,
)
def get_for_object_reference(self, model, object_id, model_db=None):
return self.get_for_model(model, model_db=model_db).filter(
object_id=object_id,
)
def get_for_object(self, obj, model_db=None):
return self.get_for_object_reference(obj.__class__, obj.pk, model_db=model_db)
def get_deleted(self, model, model_db=None):
# Try to do a faster JOIN.
model_db = model_db or router.db_for_write(model)
connection = connections[self.db]
if self.db == model_db and connection.vendor in ("sqlite", "postgresql"):
content_type = _get_content_type(model, self.db)
subquery = SubquerySQL(
"""
SELECT MAX(V.{id})
FROM {version} AS V
LEFT JOIN {model} ON V.{object_id} = CAST({model}.{model_id} as {str})
WHERE
V.{db} = %s AND
V.{content_type_id} = %s AND
{model}.{model_id} IS NULL
GROUP BY V.{object_id}
""".format(
id=connection.ops.quote_name("id"),
version=connection.ops.quote_name(Version._meta.db_table),
model=connection.ops.quote_name(model._meta.db_table),
model_id=connection.ops.quote_name(model._meta.pk.db_column or model._meta.pk.attname),
object_id=connection.ops.quote_name("object_id"),
str=Version._meta.get_field("object_id").db_type(connection),
db=connection.ops.quote_name("db"),
content_type_id=connection.ops.quote_name("content_type_id"),
),
(model_db, content_type.id),
output_field=Version._meta.pk,
)
else:
# We have to use a slow subquery.
subquery = self.get_for_model(model, model_db=model_db).exclude(
object_id__in=list(
model._default_manager.using(model_db).values_list("pk", flat=True).order_by().iterator()
),
).values_list("object_id").annotate(
latest_pk=models.Max("pk")
).order_by().values_list("latest_pk", flat=True)
# Perform the subquery.
return self.filter(
pk__in=subquery,
)
def get_unique(self):
last_key = None
for version in self.iterator():
key = (version.object_id, version.content_type_id, version.db, version._local_field_dict)
if last_key != key:
yield version
last_key = key
@python_2_unicode_compatible
class Version(models.Model):
"""A saved version of a database model."""
objects = VersionQuerySet.as_manager()
revision = models.ForeignKey(
Revision,
on_delete=models.CASCADE,
help_text="The revision that contains this version.",
)
object_id = models.CharField(
max_length=191,
help_text="Primary key of the model under version control.",
)
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
help_text="Content type of the model under version control.",
)
@property
def _content_type(self):
return ContentType.objects.db_manager(self._state.db).get_for_id(self.content_type_id)
@property
def _model(self):
return self._content_type.model_class()
# A link to the current instance, not the version stored in this Version!
object = GenericForeignKey(
ct_field="content_type",
fk_field="object_id",
)
db = models.CharField(
max_length=191,
help_text="The database the model under version control is stored in.",
)
format = models.CharField(
max_length=255,
help_text="The serialization format used by this model.",
)
serialized_data = models.TextField(
help_text="The serialized form of this version of the model.",
)
object_repr = models.TextField(
help_text="A string representation of the object.",
)
@cached_property
def _object_version(self):
data = self.serialized_data
data = force_text(data.encode("utf8"))
try:
return list(serializers.deserialize(self.format, data, ignorenonexistent=True))[0]
except DeserializationError:
raise RevertError(ugettext("Could not load %(object_repr)s version - incompatible version data.") % {
"object_repr": self.object_repr,
})
except serializers.SerializerDoesNotExist:
raise RevertError(ugettext("Could not load %(object_repr)s version - unknown serializer %(format)s.") % {
"object_repr": self.object_repr,
"format": self.format,
})
@cached_property
def _local_field_dict(self):
"""
A dictionary mapping field names to field values in this version
of the model.
Parent links of inherited multi-table models will not be followed.
"""
version_options = _get_options(self._model)
object_version = self._object_version
obj = object_version.object
model = self._model
field_dict = {}
for field_name in version_options.fields:
field = model._meta.get_field(field_name)
if isinstance(field, models.ManyToManyField):
# M2M fields with a custom through are not stored in m2m_data, but as a separate model.
if field.attname in object_version.m2m_data:
field_dict[field.attname] = object_version.m2m_data[field.attname]
else:
field_dict[field.attname] = getattr(obj, field.attname)
return field_dict
@cached_property
def field_dict(self):
"""
A dictionary mapping field names to field values in this version
of the model.
This method will follow parent links, if present.
"""
field_dict = self._local_field_dict
# Add parent data.
for parent_model, field in self._model._meta.concrete_model._meta.parents.items():
content_type = _get_content_type(parent_model, self._state.db)
parent_id = field_dict[field.attname]
parent_version = self.revision.version_set.get(
content_type=content_type,
object_id=parent_id,
db=self.db,
)
field_dict.update(parent_version.field_dict)
return field_dict
def revert(self):
self._object_version.save(using=self.db)
def __str__(self):
return self.object_repr
class Meta:
app_label = 'reversion'
unique_together = (
("db", "content_type", "object_id", "revision"),
)
ordering = ("-pk",)
class _Str(models.Func):
"""Casts a value to the database's text type."""
function = "CAST"
template = "%(function)s(%(expressions)s as %(db_type)s)"
def __init__(self, expression):
super(_Str, self).__init__(expression, output_field=models.TextField())
def as_sql(self, compiler, connection):
self.extra["db_type"] = self.output_field.db_type(connection)
return super(_Str, self).as_sql(compiler, connection)
def _safe_subquery(method, left_query, left_field_name, right_subquery, right_field_name):
right_subquery = right_subquery.order_by().values_list(right_field_name, flat=True)
left_field = left_query.model._meta.get_field(left_field_name)
right_field = right_subquery.model._meta.get_field(right_field_name)
# If the databases don't match, we have to do it in-memory.
# If it's not a supported database, we also have to do it in-memory.
if (
left_query.db != right_subquery.db or not
(
left_field.get_internal_type() != right_field.get_internal_type() and
connections[left_query.db].vendor in ("sqlite", "postgresql")
)
):
right_subquery = list(right_subquery.iterator())
else:
# If the left hand side is not a text field, we need to cast it.
if not isinstance(left_field, (models.CharField, models.TextField)):
left_field_name_str = "{}_str".format(left_field_name)
left_query = left_query.annotate(**{
left_field_name_str: _Str(left_field_name),
})
left_field_name = left_field_name_str
# If the right hand side is not a text field, we need to cast it.
if not isinstance(right_field, (models.CharField, models.TextField)):
right_field_name_str = "{}_str".format(right_field_name)
right_subquery = right_subquery.annotate(**{
right_field_name_str: _Str(right_field_name),
}).values_list(right_field_name_str, flat=True)
# All done!
return getattr(left_query, method)(**{
"{}__in".format(left_field_name): right_subquery,
})

433
reversion/revisions.py Normal file
View File

@ -0,0 +1,433 @@
from __future__ import unicode_literals
from collections import namedtuple, defaultdict
from contextlib import contextmanager
from functools import wraps
from threading import local
from django.apps import apps
from django.core import serializers
from django.core.exceptions import ObjectDoesNotExist
from django.db import models, transaction, router
from django.db.models.query import QuerySet
from django.db.models.signals import post_save, m2m_changed
from django.utils.encoding import force_text
from django.utils import timezone, six
from reversion.compat import remote_field
from reversion.errors import RevisionManagementError, RegistrationError
from reversion.signals import pre_revision_commit, post_revision_commit
_VersionOptions = namedtuple("VersionOptions", (
"fields",
"follow",
"format",
"for_concrete_model",
"ignore_duplicates",
))
_StackFrame = namedtuple("StackFrame", (
"manage_manually",
"user",
"comment",
"date_created",
"db_versions",
"meta",
))
class _Local(local):
def __init__(self):
self.stack = ()
_local = _Local()
def is_active():
return bool(_local.stack)
def _current_frame():
if not is_active():
raise RevisionManagementError("There is no active revision for this thread")
return _local.stack[-1]
def _copy_db_versions(db_versions):
return {
db: versions.copy()
for db, versions
in db_versions.items()
}
def _push_frame(manage_manually, using):
if is_active():
current_frame = _current_frame()
db_versions = _copy_db_versions(current_frame.db_versions)
db_versions.setdefault(using, {})
stack_frame = current_frame._replace(
manage_manually=manage_manually,
db_versions=db_versions,
)
else:
stack_frame = _StackFrame(
manage_manually=manage_manually,
user=None,
comment="",
date_created=timezone.now(),
db_versions={using: {}},
meta=(),
)
_local.stack += (stack_frame,)
def _update_frame(**kwargs):
_local.stack = _local.stack[:-1] + (_current_frame()._replace(**kwargs),)
def _pop_frame():
prev_frame = _current_frame()
_local.stack = _local.stack[:-1]
if is_active():
current_frame = _current_frame()
db_versions = {
db: prev_frame.db_versions[db]
for db
in current_frame.db_versions.keys()
}
_update_frame(
user=prev_frame.user,
comment=prev_frame.comment,
date_created=prev_frame.date_created,
db_versions=db_versions,
meta=prev_frame.meta,
)
def is_manage_manually():
return _current_frame().manage_manually
def set_user(user):
_update_frame(user=user)
def get_user():
return _current_frame().user
def set_comment(comment):
_update_frame(comment=comment)
def get_comment():
return _current_frame().comment
def set_date_created(date_created):
_update_frame(date_created=date_created)
def get_date_created():
return _current_frame().date_created
def add_meta(model, **values):
_update_frame(meta=_current_frame().meta + ((model, values),))
def _follow_relations(obj):
version_options = _get_options(obj.__class__)
for follow_name in version_options.follow:
try:
follow_obj = getattr(obj, follow_name)
except ObjectDoesNotExist:
continue
if isinstance(follow_obj, models.Model):
yield follow_obj
elif isinstance(follow_obj, (models.Manager, QuerySet)):
for follow_obj_instance in follow_obj.all():
yield follow_obj_instance
elif follow_obj is not None:
raise RegistrationError("{name}.{follow_name} should be a Model or QuerySet".format(
name=obj.__class__.__name__,
follow_name=follow_name,
))
def _follow_relations_recursive(obj):
def do_follow(obj):
if obj not in relations:
relations.add(obj)
for related in _follow_relations(obj):
do_follow(related)
relations = set()
do_follow(obj)
return relations
def _add_to_revision(obj, using, model_db, explicit):
from reversion.models import Version
# Exit early if the object is not fully-formed.
if obj.pk is None:
return
version_options = _get_options(obj.__class__)
content_type = _get_content_type(obj.__class__, using)
object_id = force_text(obj.pk)
version_key = (content_type, object_id)
# If the obj is already in the revision, stop now.
db_versions = _current_frame().db_versions
versions = db_versions[using]
if version_key in versions and not explicit:
return
# Get the version data.
version = Version(
content_type=content_type,
object_id=object_id,
db=model_db,
format=version_options.format,
serialized_data=serializers.serialize(
version_options.format,
(obj,),
fields=version_options.fields,
),
object_repr=force_text(obj),
)
# If the version is a duplicate, stop now.
if version_options.ignore_duplicates and explicit:
previous_version = Version.objects.using(using).get_for_object(obj, model_db=model_db).first()
if previous_version and previous_version._local_field_dict == version._local_field_dict:
return
# Store the version.
db_versions = _copy_db_versions(db_versions)
db_versions[using][version_key] = version
_update_frame(db_versions=db_versions)
# Follow relations.
for follow_obj in _follow_relations(obj):
_add_to_revision(follow_obj, using, model_db, False)
def add_to_revision(obj, model_db=None):
model_db = model_db or router.db_for_write(obj.__class__, instance=obj)
for db in _current_frame().db_versions.keys():
_add_to_revision(obj, db, model_db, True)
def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None):
from reversion.models import Revision
# Only save versions that exist in the database.
model_db_pks = defaultdict(lambda: defaultdict(set))
for version in versions:
model_db_pks[version._model][version.db].add(version.object_id)
model_db_existing_pks = {
model: {
db: frozenset(map(
force_text,
model._default_manager.using(db).filter(pk__in=pks).values_list("pk", flat=True),
))
for db, pks in db_pks.items()
}
for model, db_pks in model_db_pks.items()
}
versions = [
version for version in versions
if version.object_id in model_db_existing_pks[version._model][version.db]
]
# Bail early if there are no objects to save.
if not versions:
return
# Save a new revision.
revision = Revision(
date_created=date_created,
user=user,
comment=comment,
)
# Send the pre_revision_commit signal.
pre_revision_commit.send(
sender=create_revision,
revision=revision,
versions=versions,
)
# Save the revision.
revision.save(using=using)
# Save version models.
for version in versions:
version.revision = revision
version.save(using=using)
# Save the meta information.
for meta_model, meta_fields in meta:
meta_model._default_manager.db_manager(using=using).create(
revision=revision,
**meta_fields
)
# Send the post_revision_commit signal.
post_revision_commit.send(
sender=create_revision,
revision=revision,
versions=versions,
)
@contextmanager
def _dummy_context():
yield
@contextmanager
def _create_revision_context(manage_manually, using, atomic):
_push_frame(manage_manually, using)
try:
context = transaction.atomic(using=using) if atomic else _dummy_context()
with context:
yield
# Only save for a db if that's the last stack frame for that db.
if not any(using in frame.db_versions for frame in _local.stack[:-1]):
current_frame = _current_frame()
_save_revision(
versions=current_frame.db_versions[using].values(),
user=current_frame.user,
comment=current_frame.comment,
meta=current_frame.meta,
date_created=current_frame.date_created,
using=using,
)
finally:
_pop_frame()
def create_revision(manage_manually=False, using=None, atomic=True):
from reversion.models import Revision
using = using or router.db_for_write(Revision)
return _ContextWrapper(_create_revision_context, (manage_manually, using, atomic))
class _ContextWrapper(object):
def __init__(self, func, args):
self._func = func
self._args = args
self._context = func(*args)
def __enter__(self):
return self._context.__enter__()
def __exit__(self, exc_type, exc_value, traceback):
return self._context.__exit__(exc_type, exc_value, traceback)
def __call__(self, func):
@wraps(func)
def do_revision_context(*args, **kwargs):
with self._func(*self._args):
return func(*args, **kwargs)
return do_revision_context
def _post_save_receiver(sender, instance, using, **kwargs):
if is_registered(sender) and is_active() and not is_manage_manually():
add_to_revision(instance, model_db=using)
def _m2m_changed_receiver(instance, using, action, model, reverse, **kwargs):
if action.startswith("post_") and not reverse:
if is_registered(instance) and is_active() and not is_manage_manually():
add_to_revision(instance, model_db=using)
def _get_registration_key(model):
return (model._meta.app_label, model._meta.model_name)
_registered_models = {}
def is_registered(model):
return _get_registration_key(model) in _registered_models
def get_registered_models():
return (apps.get_model(*key) for key in _registered_models.keys())
def _get_senders_and_signals(model):
yield model, post_save, _post_save_receiver
opts = model._meta.concrete_model._meta
for field in opts.local_many_to_many:
m2m_model = remote_field(field).through
if isinstance(m2m_model, six.string_types):
if "." not in m2m_model:
m2m_model = "{app_label}.{m2m_model}".format(
app_label=opts.app_label,
m2m_model=m2m_model
)
yield m2m_model, m2m_changed, _m2m_changed_receiver
def register(model=None, fields=None, exclude=(), follow=(), format="json",
for_concrete_model=True, ignore_duplicates=False):
def register(model):
# Prevent multiple registration.
if is_registered(model):
raise RegistrationError("{model} has already been registered with django-reversion".format(
model=model,
))
# Parse fields.
opts = model._meta.concrete_model._meta
version_options = _VersionOptions(
fields=tuple(
field_name
for field_name
in ([
field.name
for field
in opts.local_fields + opts.local_many_to_many
] if fields is None else fields)
if field_name not in exclude
),
follow=tuple(follow),
format=format,
for_concrete_model=for_concrete_model,
ignore_duplicates=ignore_duplicates,
)
# Register the model.
_registered_models[_get_registration_key(model)] = version_options
# Connect signals.
for sender, signal, signal_receiver in _get_senders_and_signals(model):
signal.connect(signal_receiver, sender=sender)
# All done!
return model
# Return a class decorator if model is not given
if model is None:
return register
# Register the model.
return register(model)
def _assert_registered(model):
if not is_registered(model):
raise RegistrationError("{model} has not been registered with django-reversion".format(
model=model,
))
def _get_options(model):
_assert_registered(model)
return _registered_models[_get_registration_key(model)]
def unregister(model):
_assert_registered(model)
del _registered_models[_get_registration_key(model)]
# Disconnect signals.
for sender, signal, signal_receiver in _get_senders_and_signals(model):
signal.disconnect(signal_receiver, sender=sender)
def _get_content_type(model, using):
from django.contrib.contenttypes.models import ContentType
version_options = _get_options(model)
return ContentType.objects.db_manager(using).get_for_model(
model,
for_concrete_model=version_options.for_concrete_model,
)

10
reversion/signals.py Normal file
View File

@ -0,0 +1,10 @@
from django.dispatch.dispatcher import Signal
_signal_args = [
"revision",
"versions",
]
pre_revision_commit = Signal(providing_args=_signal_args)
post_revision_commit = Signal(providing_args=_signal_args)

View File

@ -0,0 +1,10 @@
{% extends "admin/change_list.html" %}
{% load i18n admin_urls %}
{% block object-tools-items %}
{% if not is_popup and has_add_permission and has_change_permission %}
<li><a href="{% url opts|admin_urlname:'recoverlist' %}" class="recoverlink">{% blocktrans with cl.opts.verbose_name_plural|escape as name %}Recover deleted {{name}}{% endblocktrans %}</a></li>
{% endif %}
{{block.super}}
{% endblock %}

View File

@ -0,0 +1,42 @@
{% extends "admin/object_history.html" %}
{% load i18n %}
{% block content %}
<div id="content-main">
<p>{% blocktrans %}Choose a date from the list below to revert to a previous version of this object.{% endblocktrans %}</p>
<div class="module">
{% if action_list %}
<table id="change-history" class="table table-striped table-bordered">
<thead>
<tr>
<th scope="col">{% trans 'Date/time' %}</th>
<th scope="col">{% trans 'User' %}</th>
<th scope="col">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
{% for action in action_list %}
<tr>
<th scope="row"><a href="{{action.url}}">{{action.revision.date_created|date:"DATETIME_FORMAT"}}</a></th>
<td>
{% if action.revision.user %}
{{action.revision.user.get_username}}
{% if action.revision.user.get_full_name %} ({{action.revision.user.get_full_name}}){% endif %}
{% else %}
&mdash;
{% endif %}
</td>
<td>{{action.revision.comment|linebreaksbr|default:""}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>{% trans "This object doesn't have a change history. It probably wasn't added via this admin site." %}</p>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "reversion/revision_form.html" %}
{% load i18n admin_urls %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans "Home" %}</a> &rsaquo;
<a href="{% url 'admin:app_list' opts.app_label %}">{{opts.app_config.verbose_name}}</a> &rsaquo;
<a href="{% url opts|admin_urlname:'changelist' %}">{{opts.verbose_name_plural|capfirst}}</a> &rsaquo;
<a href="{% url opts|admin_urlname:'recoverlist' %}">{% blocktrans with opts.verbose_name_plural as name %}Recover deleted {{name}}{% endblocktrans %}</a> &rsaquo;
{{title}}
</div>
{% endblock %}
{% block object-tools %}{% endblock %}
{% block form_top %}
<p>{% blocktrans %}Press the save button below to recover this version of the object.{% endblocktrans %}</p>
{% endblock %}
{% block submit_buttons_top %}{% with is_popup=1 %}{{block.super}}{% endwith %}{% endblock %}
{% block submit_buttons_bottom %}{% with is_popup=1 %}{{block.super}}{% endwith %}{% endblock %}

View File

@ -0,0 +1,41 @@
{% extends "admin/base_site.html" %}
{% load i18n l10n admin_urls %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> &rsaquo;
<a href="{% url 'admin:app_list' opts.app_label %}">{{opts.app_config.verbose_name}}</a> &rsaquo;
<a href="{% url opts|admin_urlname:'changelist' %}">{{opts.verbose_name_plural|capfirst}}</a> &rsaquo;
{% blocktrans with opts.verbose_name_plural|escape as name %}Recover deleted {{name}}{% endblocktrans %}
</div>
{% endblock %}
{% block content %}
<div id="content-main">
<p>{% blocktrans %}Choose a date from the list below to recover a deleted version of an object.{% endblocktrans %}</p>
<div class="module">
{% if deleted %}
<table id="change-history" class="table table-striped table-bordered">
<thead>
<tr>
<th scope="col">{% trans 'Date/time' %}</th>
<th scope="col">{{opts.verbose_name|capfirst}}</th>
</tr>
</thead>
<tbody>
{% for deletion in deleted %}
<tr>
<th scope="row"><a href="{% url opts|admin_urlname:'recover' deletion.pk|unlocalize %}">{{deletion.revision.date_created}}</a></th>
<td>{{deletion.object_repr}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>{% trans "There are no deleted objects to recover." %}</p>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends "admin/change_form.html" %}
{% load i18n admin_urls %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans "Home" %}</a> &rsaquo;
<a href="{% url 'admin:app_list' opts.app_label %}">{{opts.app_config.verbose_name}}</a> &rsaquo;
<a href="{% url opts|admin_urlname:'changelist' %}">{{opts.verbose_name_plural|capfirst}}</a> &rsaquo;
<a href="{% url opts|admin_urlname:'change' object_id %}">{{original|truncatewords:"18"}}</a> &rsaquo;
<a href="{% url opts|admin_urlname:'history' object_id %}">{% trans "History" %}</a> &rsaquo;
{% blocktrans with opts.verbose_name as verbose_name %}Revert {{verbose_name}}{% endblocktrans %}
</div>
{% endblock %}
{% block object-tools %}{% endblock %}
{% block form_top %}
<p>{% blocktrans %}Press the save button below to revert to this version of the object.{% endblocktrans %}</p>
{% endblock %}
{% block submit_buttons_top %}{% with is_popup=1 %}{{block.super}}{% endwith %}{% endblock %}
{% block submit_buttons_bottom %}{% with is_popup=1 %}{{block.super}}{% endwith %}{% endblock %}

68
reversion/views.py Normal file
View File

@ -0,0 +1,68 @@
from functools import wraps
from reversion.compat import is_authenticated
from reversion.revisions import create_revision as create_revision_base, set_user, get_user
class _RollBackRevisionView(Exception):
def __init__(self, response):
self.response = response
def _request_creates_revision(request):
return request.method not in ("OPTIONS", "GET", "HEAD")
def _set_user_from_request(request):
if getattr(request, "user", None) and is_authenticated(request.user) and get_user() is None:
set_user(request.user)
def create_revision(manage_manually=False, using=None, atomic=True):
"""
View decorator that wraps the request in a revision.
The revision will have it's user set from the request automatically.
"""
def decorator(func):
@wraps(func)
def do_revision_view(request, *args, **kwargs):
if _request_creates_revision(request):
try:
with create_revision_base(manage_manually=manage_manually, using=using, atomic=atomic):
response = func(request, *args, **kwargs)
# Check for an error response.
if response.status_code >= 400:
raise _RollBackRevisionView(response)
# Otherwise, we're good.
_set_user_from_request(request)
return response
except _RollBackRevisionView as ex:
return ex.response
return func(request, *args, **kwargs)
return do_revision_view
return decorator
class RevisionMixin(object):
"""
A class-based view mixin that wraps the request in a revision.
The revision will have it's user set from the request automatically.
"""
revision_manage_manually = False
revision_using = None
revision_atomic = True
def __init__(self, *args, **kwargs):
super(RevisionMixin, self).__init__(*args, **kwargs)
self.dispatch = create_revision(
manage_manually=self.revision_manage_manually,
using=self.revision_using,
atomic=self.revision_atomic
)(self.dispatch)

2
setup.cfg Normal file
View File

@ -0,0 +1,2 @@
[bdist_wheel]
universal = 1

45
setup.py Normal file
View File

@ -0,0 +1,45 @@
from setuptools import setup, find_packages
from reversion import __version__
# Load in babel support, if available.
try:
from babel.messages import frontend as babel
cmdclass = {
"compile_catalog": babel.compile_catalog,
"extract_messages": babel.extract_messages,
"init_catalog": babel.init_catalog,
"update_catalog": babel.update_catalog,
}
except ImportError:
cmdclass = {}
setup(
name="django-reversion",
version='.'.join(str(x) for x in __version__),
license="BSD",
description="An extension to the Django web framework that provides version control for model instances.",
author="Dave Hall",
author_email="dave@etianen.com",
url="http://github.com/etianen/django-reversion",
zip_safe=False,
packages=find_packages(),
package_data={
"reversion": ["locale/*/LC_MESSAGES/django.*", "templates/reversion/*.html"]},
cmdclass=cmdclass,
install_requires=[
"django>=1.8",
],
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
"Framework :: Django",
]
)

22
tests/manage.py Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings")
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django # noqa
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise
execute_from_command_line(sys.argv)

View File

14
tests/test_app/admin.py Normal file
View File

@ -0,0 +1,14 @@
from django.contrib import admin
from reversion.admin import VersionAdmin
from test_app.models import TestModel, TestModelRelated
class TestModelAdmin(VersionAdmin):
filter_horizontal = ("related",)
admin.site.register(TestModel, TestModelAdmin)
admin.site.register(TestModelRelated, admin.ModelAdmin)

View File

@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-06-14 10:27
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('reversion', '0001_squashed_0004_auto_20160611_1202'),
]
operations = [
migrations.CreateModel(
name='TestMeta',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=191)),
('revision', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='reversion.Revision')),
],
),
migrations.CreateModel(
name='TestModel',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='v1', max_length=191)),
],
),
migrations.CreateModel(
name='TestModelGenericInline',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('object_id', models.IntegerField()),
('inline_name', models.CharField(default='v1', max_length=191)),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
],
),
migrations.CreateModel(
name='TestModelInline',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('inline_name', models.CharField(default='v1', max_length=191)),
],
),
migrations.CreateModel(
name='TestModelRelated',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='v1', max_length=191)),
],
),
migrations.CreateModel(
name='TestModelThrough',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='v1', max_length=191)),
],
),
migrations.CreateModel(
name='TestModelParent',
fields=[
('testmodel_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='test_app.TestModel')),
('parent_name', models.CharField(default='parent v1', max_length=191)),
],
bases=('test_app.testmodel',),
),
migrations.CreateModel(
name='TestModelEscapePK',
fields=[
('name', models.CharField(max_length=191, primary_key=True, serialize=False)),
],
),
migrations.AddField(
model_name='testmodelthrough',
name='test_model',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='test_app.TestModel'),
),
migrations.AddField(
model_name='testmodelthrough',
name='test_model_related',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='test_app.TestModelRelated'),
),
migrations.AddField(
model_name='testmodelinline',
name='test_model',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='test_app.TestModel'),
),
migrations.AddField(
model_name='testmodel',
name='related',
field=models.ManyToManyField(blank=True, related_name='_testmodel_related_+', to='test_app.TestModelRelated'),
),
migrations.AddField(
model_name='testmodel',
name='related_through',
field=models.ManyToManyField(blank=True, related_name='_testmodel_related_through_+', through='test_app.TestModelThrough', to='test_app.TestModelRelated'),
),
]

View File

111
tests/test_app/models.py Normal file
View File

@ -0,0 +1,111 @@
from django.db import models
from django.contrib.contenttypes.models import ContentType
try:
from django.contrib.contenttypes.fields import GenericRelation
except ImportError: # Django < 1.9 pragma: no cover
from django.contrib.contenttypes.generic import GenericRelation
from reversion.models import Revision
class TestModelGenericInline(models.Model):
object_id = models.IntegerField()
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
)
inline_name = models.CharField(
max_length=191,
default="v1",
)
class TestModel(models.Model):
name = models.CharField(
max_length=191,
default="v1",
)
related = models.ManyToManyField(
"TestModelRelated",
blank=True,
related_name="+",
)
related_through = models.ManyToManyField(
"TestModelRelated",
blank=True,
through="TestModelThrough",
related_name="+",
)
generic_inlines = GenericRelation(TestModelGenericInline)
class TestModelEscapePK(models.Model):
name = models.CharField(max_length=191, primary_key=True)
class TestModelThrough(models.Model):
test_model = models.ForeignKey(
"TestModel",
related_name="+",
on_delete=models.CASCADE,
)
test_model_related = models.ForeignKey(
"TestModelRelated",
related_name="+",
on_delete=models.CASCADE,
)
name = models.CharField(
max_length=191,
default="v1",
)
class TestModelRelated(models.Model):
name = models.CharField(
max_length=191,
default="v1",
)
class TestModelParent(TestModel):
parent_name = models.CharField(
max_length=191,
default="parent v1",
)
class TestModelInline(models.Model):
test_model = models.ForeignKey(
TestModel,
on_delete=models.CASCADE,
)
inline_name = models.CharField(
max_length=191,
default="v1",
)
class TestMeta(models.Model):
revision = models.ForeignKey(
Revision,
on_delete=models.CASCADE,
)
name = models.CharField(
max_length=191,
)

Some files were not shown because too many files have changed in this diff Show More