pax_global_header00006660000000000000000000000064145562527220014524gustar00rootroot0000000000000052 comment=a781656740bcb0d998324cd3a8c11c8754e1e1b2 django-reversion-5.0.12/000077500000000000000000000000001455625272200150655ustar00rootroot00000000000000django-reversion-5.0.12/.coveragerc000066400000000000000000000006321455625272200172070ustar00rootroot00000000000000[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 django-reversion-5.0.12/.github/000077500000000000000000000000001455625272200164255ustar00rootroot00000000000000django-reversion-5.0.12/.github/workflows/000077500000000000000000000000001455625272200204625ustar00rootroot00000000000000django-reversion-5.0.12/.github/workflows/python-package.yml000066400000000000000000000047421455625272200241260ustar00rootroot00000000000000name: Python package on: [push, pull_request] jobs: build: runs-on: ubuntu-latest services: postgres: image: postgres env: POSTGRES_PASSWORD: postgres options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 mysql: image: mysql env: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: root options: >- --health-cmd "mysqladmin -uroot -proot ping" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 3306:3306 env: PYTHONDEVMODE: 1 DJANGO_DATABASE_HOST_POSTGRES: localhost DJANGO_DATABASE_USER_POSTGRES: postgres DJANGO_DATABASE_NAME_POSTGRES: postgres DJANGO_DATABASE_PASSWORD_POSTGRES: postgres DJANGO_DATABASE_HOST_MYSQL: 127.0.0.1 DJANGO_DATABASE_USER_MYSQL: root DJANGO_DATABASE_NAME_MYSQL: root DJANGO_DATABASE_PASSWORD_MYSQL: root strategy: matrix: python-version: [3.7, 3.8, 3.9, '3.10', '3.11'] django-version: - '>=4.2,<5.0' - '>=4.1,<4.2' - '>=4.0,<4.1' - '>=3.2,<4.0' exclude: - python-version: 3.7 django-version: '>=4.2,<5.0' - python-version: 3.7 django-version: '>=4.1,<4.2' - python-version: 3.7 django-version: '>=4.0,<4.1' - python-version: '3.11' django-version: '>=3.2,<4.0' - python-version: '3.11' django-version: '>=4.0,<4.1' steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies (Django ${{ matrix.django-version }}) run: | python -m pip install --upgrade pip python -m pip install --pre django'${{ matrix.django-version }}' python -m pip install flake8 coverage sphinx sphinx_rtd_theme psycopg2 mysqlclient -e . - name: Lint with flake8 run: | flake8 - name: Check no missing migrations run: | tests/manage.py makemigrations --check - name: Test with unittest run: | coverage run tests/manage.py test tests coverage report - name: Build docs run: | (cd docs && sphinx-build -n -W . _build) django-reversion-5.0.12/.github/workflows/python-publish.yml000066400000000000000000000011531455625272200241720ustar00rootroot00000000000000name: Upload Python Package on: release: types: [created] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install dependencies run: | python -m pip install --upgrade pip pip install setuptools wheel twine - name: Build and publish env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | python setup.py sdist bdist_wheel twine upload dist/* django-reversion-5.0.12/.gitignore000066400000000000000000000002111455625272200170470ustar00rootroot00000000000000.DS_Store *.db .project .pydevproject .settings .venv *.pyc *.pyo dist build MANIFEST *.egg-info/ docs/_build .coverage *.sqlite3 .idea/ django-reversion-5.0.12/.readthedocs.yaml000066400000000000000000000002331455625272200203120ustar00rootroot00000000000000version: 2 build: os: ubuntu-20.04 tools: python: "3.9" sphinx: configuration: docs/conf.py python: install: - method: pip path: . django-reversion-5.0.12/CHANGELOG.rst000066400000000000000000000661301455625272200171140ustar00rootroot00000000000000.. _changelog: django-reversion changelog ========================== 5.0.12 - 2024-01-30 ------------------- - Fix missing migration introduced in v5.0.11. 5.0.11 - 2024-01-29 ------------------- - Improved the Chinese translation (@zengqiu). 5.0.10 - 2023-12-30 ------------------- - Fix N+1 queries while rendering the ``recover_list.html`` template (@armonge). 5.0.9 - 2023-12-20 ------------------ - Broken release. 5.0.8 - 2023-11-08 ------------------ - Fix ``get_deleted`` (@siddarta-weis, @etianen). 5.0.7 - 2023-11-07 ------------------ - Speed up ``get_deleted`` (@caullla). 5.0.6 - 2023-09-29 ------------------ - Fix handling case of missing object in admin revert (@julianklotz) 5.0.5 - 2023-09-19 ------------------ - Handling case of missing object in admin revert (@etianen, @PavelPancocha) - CI improvements (@etianen, @browniebroke) 5.0.4 - 2022-11-12 ------------------ - Fix warning log formatting for failed reverts (@tony). 5.0.3 - 2022-10-02 ------------------ - A revision will no longer be created if a transaction is marked as rollback, as this would otherwise cause an additional database error (@proofit404). - A warning log is now emitted if a revert fails due to database integrity errors, making debugging the final ``RevertError`` easier. 5.0.2 - 2022-08-06 ------------------ - Fixed doc builds on readthedocs (@etianen). 5.0.1 - 2022-06-18 ------------------ - Fix admin detail view in multi-database configuration (@atten). 5.0.0 - 2022-02-20 ------------------ - Added support for using django-reversion contexts in ``asyncio`` tasks (@bellini666). - **Breaking:** Dropped support for Python 3.6. 4.0.2 - 2022-01-30 ------------------ - Improved performance of `createinitialrevisions` management command (@philipstarkey). 4.0.1 - 2021-11-04 ------------------ - Django 4.0b support (@smithdc1, @kevinmarsh). - Optimized ``VersionQuerySet.get_deleted``. 4.0.0 - 2021-07-09 ------------------ - **Breaking:** The ``create_revision`` view decorator and ``RevisionMiddleware`` no longer roll back the revision and database transaction on response status code >= 400. It's the responsibility of the view to use `transaction.atomic()` to roll back any invalid data. This can be enabled globally by setting ``ATOMIC_REQUESTS=True``. (@etianen) https://docs.djangoproject.com/en/3.1/ref/settings/#std:setting-DATABASE-ATOMIC_REQUESTS - Fixing gettext plural forms with Django (@martinsvoboda). - Deprecation removals (@lociii, @Peter-van-Tol). - CI testing improvements (@etianen, @michael-k). - Documentation fixes (@erikrw, @jedie, @michael-k). 3.0.9 - 2021-01-22 ------------------ - Significant speedup to ``Version.objects.get_deleted(...)`` database query for PostgreSQL (@GeyseR). - Testing against Django 3.1 (@michael-k). - Django 4.0 compatibility improvements (@GitRon). 3.0.8 - 2020-08-31 ------------------ - Added ``use_natural_foreign_keys`` option to ``reversion.register()`` (@matwey). - Documentation improvments and minor fixes (@ad-m, @martey, @annainfo, @etianen, @m4rk3r, @adityakrgupta25, @ekinertac). - Dropped support for Django 1.11 LTS. 3.0.7 - 2020-02-17 ------------------ - Removing deprecated usages of `ugettext_lazy` (@François GUÉRIN). - Slovenian translation (@Bor Plestenjak). 3.0.6 - 2020-02-17 ------------------ - Packaging improvements (@michael-k). - Removing deprecated usages of `force_text` (@Tenzer). - Documentation fixes (@chicheng). 3.0.5 - 2019-12-02 ------------------ - Improved performance of `get_deleted` for large datasets (@jeremy-engel). - Django 3.0 compatibility (@claudep). - Drops Django < 1.11 compatibility (@claudep). - Drops Python 2.7 compatibility (@claudep). - Fixed errors in management commands when `django.contrib.admin` is not in `INSTALLED_APPS` (@irtimir). 3.0.4 - 2019-05-22 ------------------ - Remove `django.contrib.admin` dependency from django-reversion (Aitor Ruano). - README refactor (@rhenter). - Testing against Django 2.2 (@claudep). 3.0.3 - 2019-01-24 ------------------ - Improved performance of many reversion database queries using `EXISTS` subquery (@MRigal). - Added support for Django 2.1 `view` permission (@ofw). 3.0.2 - 2018-11-05 ------------------ - Removed squashed migrations, as they subtly messed up the Django migrations framework (@etianen). To upgrade to ``>= 3.0.2`` from ``< 3.0.1``: .. code:: pip install django-reversion==3.0.1 python manage.py migrate reversion pip install --upgrade django-reversion python manage.py migrate reversion 3.0.1 - 2018-10-23 ------------------ - Added squashed migrations back in to allow older installations to upgrade (Christopher Thorne). - Fixed TypeError exception when accessing m2m_data attribute from a field that points to Django’s User model (@denisroldan). 3.0.0 - 2018-07-19 ------------------ - **Breaking:** ``Revision.comment`` now contains the raw JSON change message generated by django admin, rather than a string. Accessing ``Revision.comment`` directly is no longer recommended. Instead, use ``Revision.get_comment()``. (@RamezIssac). - **BREAKING:** django-reversion now uses ``_base_manager`` to calculate deleted models, not ``_default_manager``. This change will only affect models that perform default filtering in their ``_default_manager`` (@ivissani). - Added ``request_creates_revision`` hook to ``RevisionMiddleware`` and ``views.create_revision``. (@kklingenberg). - Added ``revision_request_creates_revision`` hook to ``views.RevisionMixinView``. (@kklingenberg). - Added ``--meta`` flag to ``./manage.py createrevisions`` (@massover). - Fixed bug when reverting deleted nested inlines (Primož Kariž). - Added tests for django 2.1 (@claudep). 2.0.13 - 2018-01-23 ------------------- - Improve performance of ``get_deleted()`` for Oracle (@passuf). - Minor tweaks (@etianen, @michael-k). 2.0.12 - 2017-12-05 ------------------- - Fixed MySQL error in ``get_deleted()``. 2.0.11 - 2017-11-27 ------------------- - Dramatically improved performance of ``get_deleted()`` over large datasets (@alexey-v-paramonov, @etianen). - Ukranian translation (@illia-v). - Bugfixes (@achidlow, @claudep, @etianen). 2.0.10 - 2017-08-18 ------------------- - 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 - 2017-06-19 ------------------ - 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 - 2016-11-28 ------------------ - 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 - 2016-10-31 ------------------ - 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 - 2016-07-21 ------------------ - Fixed ``RevisionMiddleware`` always rolling back transactions in gunicorn (@stebunovd, @etianen). - Tweaks and minor bugfixes (@SahilMak). 2.0.5 - 2016-06-29 ------------------ - Fixed LookupError when running migration 0003 with stale content types (@etianen). 2.0.4 - 2016-06-20 ------------------ - 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 - 2016-06-14 ------------------ - Added support for m2m fields with a custom ``through`` model (@etianen). 2.0.2 - 2016-06-13 ------------------ - Fixing migration 0003 in MySQL (@etianen). 2.0.1 - 2016-06-13 ------------------ - Improved performance of migration 0003 (@BertrandBordage). - De-duplicating ``Version`` table before applying migration 0004 (@BertrandBordage, @etianen). 2.0.0 - 2016-06-11 ------------------ 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_duplicates"] = 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 `_. The old implementation is available for reference from the `previous release `_. * **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 `_. The old implementation is available for reference from the `previous release `_. * **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 `_. The old implementation is available for reference from the `previous release `_. 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 accessing 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 accessing 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 accessing 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. * Experimental 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 formatting 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 scalability of Version.object.get_deleted() method. * Improved scalability 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. django-reversion-5.0.12/LICENSE000066400000000000000000000027641455625272200161030ustar00rootroot00000000000000Copyright (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. django-reversion-5.0.12/MANIFEST.in000066400000000000000000000003621455625272200166240ustar00rootroot00000000000000include 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 django-reversion-5.0.12/README.rst000066400000000000000000000051631455625272200165610ustar00rootroot00000000000000================ django-reversion ================ |PyPI latest| |PyPI Version| |PyPI License| |Github actions| |Docs| **django-reversion** is an extension to the Django web framework that provides version control for model instances. Requirements ============ - Python 3.7 or later - Django 3.2 or later Features ======== - Roll back to any point in a model instance's history. - Recover deleted model instances. - Simple admin integration. Documentation ============= Check out the latest ``django-reversion`` documentation at `Getting Started `_ Issue tracking and source code can be found at the `main project website `_. You can keep up to date with the latest announcements by joining the `django-reversion discussion group `_. Upgrading ========= Please check the `Changelog `_ 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 `_, and submit pull requests for any new code. 1. Fork the `repository `_ on GitHub. 2. Make a branch off of master and commit your changes to it. 3. Install requirements. .. code:: bash $ pip install django psycopg2 mysqlclient -e . 4. Run the tests .. code:: bash $ tests/manage.py test tests 5. Create a Pull Request with your contribution Contributors ============ The django-reversion project was developed by `Dave Hall `_ and contributed to by `many other people `_. .. |Docs| image:: https://readthedocs.org/projects/django-reversion/badge/?version=latest :target: http://django-reversion.readthedocs.org/en/latest/?badge=latest .. |PyPI Version| image:: https://img.shields.io/pypi/pyversions/django-reversion.svg?maxAge=60 :target: https://pypi.python.org/pypi/django-reversion .. |PyPI License| image:: https://img.shields.io/pypi/l/django-reversion.svg?maxAge=120 :target: https://github.com/rhenter/django-reversion/blob/master/LICENSE .. |PyPI latest| image:: https://img.shields.io/pypi/v/django-reversion.svg?maxAge=120 :target: https://pypi.python.org/pypi/django-reversion .. |Github actions| image:: https://github.com/etianen/django-reversion/workflows/Python%20package/badge.svg :target: https://github.com/etianen/django-reversion django-reversion-5.0.12/docs/000077500000000000000000000000001455625272200160155ustar00rootroot00000000000000django-reversion-5.0.12/docs/_include/000077500000000000000000000000001455625272200175775ustar00rootroot00000000000000django-reversion-5.0.12/docs/_include/admin.rst000066400000000000000000000004421455625272200214210ustar00rootroot00000000000000Register 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 django-reversion-5.0.12/docs/_include/create-revision-args.rst000066400000000000000000000003251455625272200243620ustar00rootroot00000000000000``manage_manually`` .. include:: /_include/create-revision-manage-manually.rst ``using`` .. include:: /_include/create-revision-using.rst ``atomic`` .. include:: /_include/create-revision-atomic.rst django-reversion-5.0.12/docs/_include/create-revision-atomic.rst000066400000000000000000000001171455625272200247010ustar00rootroot00000000000000If ``True``, the revision block will be wrapped in a ``transaction.atomic()``. django-reversion-5.0.12/docs/_include/create-revision-manage-manually.rst000066400000000000000000000002371455625272200265000ustar00rootroot00000000000000If ``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. django-reversion-5.0.12/docs/_include/create-revision-using.rst000066400000000000000000000002651455625272200245560ustar00rootroot00000000000000The 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. django-reversion-5.0.12/docs/_include/model-db-arg.rst000066400000000000000000000001501455625272200225570ustar00rootroot00000000000000``model_db`` The database where the model is saved. Defaults to the default database for the model. django-reversion-5.0.12/docs/_include/post-register.rst000066400000000000000000000001461455625272200231410ustar00rootroot00000000000000.. Hint:: Whenever you register a model with django-reversion, run :ref:`createinitialrevisions`. django-reversion-5.0.12/docs/_include/signal-args.rst000066400000000000000000000002431455625272200225370ustar00rootroot00000000000000``sender`` The ``reversion.create_revision`` object. ``revision`` The :ref:`Revision` model. ``versions`` The :ref:`Version` models in the revision. django-reversion-5.0.12/docs/_include/throws-registration-error.rst000066400000000000000000000001341455625272200255140ustar00rootroot00000000000000Throws :ref:`RegistrationError` if the model has not been registered with django-reversion. django-reversion-5.0.12/docs/_include/throws-revert-error.rst000066400000000000000000000002331455625272200243110ustar00rootroot00000000000000Throws :ref:`RevertError` if the model could not be deserialized or reverted, e.g. the serialized data is not compatible with the current database schema. django-reversion-5.0.12/docs/_include/throws-revision-error.rst000066400000000000000000000001141455625272200246360ustar00rootroot00000000000000Throws :ref:`RevisionManagementError` if there is no active revision block. django-reversion-5.0.12/docs/admin.rst000066400000000000000000000077471455625272200176560ustar00rootroot00000000000000.. _admin: Admin integration ================= django-reversion can be used to add rollback and recovery to your admin site. .. Important:: Using the admin integration's preview feature will restore your model inside a temporary transaction, then roll back the transaction once the preview is rendered. The ``Model.save()`` method, along with any ``pre_save`` and ``post_save`` signals, will be run as part of the preview transaction. Any non-transactional side-effects of these functions (e.g. filesystem, cache) will **not be rolled back** at the end of the preview. The ``raw=True`` flag will be set in ``pre_save`` and ``post_save`` signals, allowing you to distinguish preview transactions from regular database transactions and avoid non-transactional side-effects. Alternatively, use `transaction.on_commit() `_ to register side-effects to be carried out only on committed transactions. .. 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() `, 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() `. 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().reversion_register(model, **options) ``model`` The model that will be registered with django-reversion. ``options`` Registration options, see :ref:`reversion.register() `. django-reversion-5.0.12/docs/api.rst000066400000000000000000000307161455625272200173270ustar00rootroot00000000000000.. _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 you’re 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! ``use_natural_foreign_keys=False`` If ``True``, the the model will be serialized using natural keys. See `Serialization of natural keys `_ .. 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() `. .. 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.get_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. django-reversion-5.0.12/docs/changelog.rst000066400000000000000000000000361455625272200204750ustar00rootroot00000000000000.. include:: ../CHANGELOG.rst django-reversion-5.0.12/docs/commands.rst000066400000000000000000000030271455625272200203520ustar00rootroot00000000000000.. _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." ./manage.py createinitialrevisions your_app.YourModel --meta="{\"your_app.RevisionMeta\": {\"hello\": \"world\"}}" 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. django-reversion-5.0.12/docs/common-problems.rst000066400000000000000000000016331455625272200216630ustar00rootroot00000000000000.. _common-problems: Common problems =============== Incompatible version data ------------------------- Django-reversion stores the versions of a model as JSON. If a model changes, the migrations are not applied to the stored JSON data. Therefore it can happen that an old version can no longer be restored. In this case the following error occurs: .. code:: python reversion.errors.RevertError: Could not load - incompatible version data. 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. django-reversion-5.0.12/docs/conf.py000066400000000000000000000227351455625272200173250ustar00rootroot00000000000000#!/usr/bin/env python3 # # 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('.')) import os 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 = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx"] intersphinx_mapping = { "python": ("https://docs.python.org/3", None), } # 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 = 'en' # 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 suppress_warnings = ["image.nonlocal_uri"] # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # Use RTD theme locally. if not os.environ.get('READTHEDOCS', None) == 'True': import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # The name for this set of Sphinx documents. # " v 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 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 django-reversion-5.0.12/docs/errors.rst000066400000000000000000000007331455625272200200660ustar00rootroot00000000000000.. _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. django-reversion-5.0.12/docs/index.rst000066400000000000000000000020741455625272200176610ustar00rootroot00000000000000.. _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``. 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 common-problems changelog Usage ^^^^^ .. toctree:: :maxdepth: 2 admin commands api views middleware errors signals django-reversion-5.0.12/docs/middleware.rst000066400000000000000000000030361455625272200206660ustar00rootroot00000000000000.. _middleware: Middleware ========== Shortcuts when using django-reversion in views. reversion.middleware.RevisionMiddleware --------------------------------------- Wrap every request 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`` setting. .. Warning:: This will wrap every request that meets the specified criterion 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 ``RevisionMiddleware.request_creates_revision(request)`` By default, any request that isn't ``GET``, ``HEAD`` or ``OPTIONS`` will be wrapped in a revision block. Override this method if you need to apply a custom rule. For example: .. code:: python from reversion.middleware import RevisionMiddleware class BypassRevisionMiddleware(RevisionMiddleware): def request_creates_revision(self, request): # Bypass the revision according to some header silent = request.META.get("HTTP_X_NOREVISION", "false") return super().request_creates_revision(request) and \ silent != "true" django-reversion-5.0.12/docs/signals.rst000066400000000000000000000007021455625272200202060ustar00rootroot00000000000000.. _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 django-reversion-5.0.12/docs/views.rst000066400000000000000000000031451455625272200177070ustar00rootroot00000000000000.. _views: Views ===== Shortcuts when using django-reversion in views. Decorators ---------- ``reversion.views.create_revision(manage_manually=False, using=None, atomic=True, request_creates_revision=None)`` Decorates a view to wrap every request 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() ` within your view. .. include:: /_include/create-revision-args.rst ``request_creates_revision`` Hook used to decide whether a request should be wrapped in a revision block. If ``None``, it will default to omitting ``GET``, ``HEAD`` and ``OPTIONS`` requests. reversion.views.RevisionMixin ----------------------------- Mixin a class-based view to wrap every request 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() ` 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 ``RevisionMixin.revision_request_creates_revision(request)`` By default, any request that isn't ``GET``, ``HEAD`` or ``OPTIONS`` will be wrapped in a revision block. Override this method if you need to apply a custom rule. django-reversion-5.0.12/reversion/000077500000000000000000000000001455625272200171015ustar00rootroot00000000000000django-reversion-5.0.12/reversion/__init__.py000066400000000000000000000016331455625272200212150ustar00rootroot00000000000000""" An extension to the Django web framework that provides version control for model instances. Developed by Dave Hall. """ 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 = (5, 0, 12) django-reversion-5.0.12/reversion/admin.py000066400000000000000000000307041455625272200205470ustar00rootroot00000000000000from contextlib import contextmanager from django.db import models, transaction, connections from django.contrib import admin, messages from django.contrib.admin import options from django.contrib.admin.utils import unquote, quote from django.contrib.contenttypes.admin import GenericInlineModelAdmin from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import PermissionDenied, ImproperlyConfigured from django.shortcuts import get_object_or_404, render, redirect from django.urls import reverse, re_path from django.utils.text import capfirst from django.utils.timezone import template_localtime from django.utils.translation import gettext as _ from django.utils.encoding import force_str from django.utils.formats import localize 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 class _RollBackRevisionView(Exception): def __init__(self, response): self.response = response 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 ( f"reversion/{opts.app_label}/{opts.object_name.lower()}/{template_name}", f"reversion/{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, message): change_message = message or _("Initial version.") entry = super().log_addition(request, object, change_message) if is_active(): set_comment(entry.get_change_message()) return entry def log_change(self, request, object, message): entry = super().log_change(request, object, message) if is_active(): set_comment(entry.get_change_message()) return entry # 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 self.model._meta.private_fields: if ( isinstance(field, GenericRelation) and field.remote_field.model == 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, field.remote_field.model) ): fk_name = field.name break if fk_name and not inline_model._meta.get_field(fk_name).remote_field.is_hidden(): field = inline_model._meta.get_field(fk_name) accessor = field.remote_field.get_accessor_name() follow_field = accessor return inline_model, follow_field def __init__(self, *args, **kwargs): super().__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().get_urls() admin_site = self.admin_site opts = self.model._meta info = opts.app_label, opts.model_name, reversion_urls = [ re_path(r"^recover/$", admin_site.admin_view(self.recoverlist_view), name='%s_%s_recoverlist' % info), re_path(r"^recover/(\d+)/$", admin_site.admin_view(self.recover_view), name='%s_%s_recover' % info), re_path( r"^([^/]+)/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().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().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 connections[version.db].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)), }) elif response.status_code == 200: 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. else: raise RevertError(_("Could not load %(object_repr)s version - not found") % { "object_repr": version.object_repr, }) except (RevertError, models.ProtectedError) as ex: opts = self.model._meta messages.error(request, force_str(ex)) return redirect(f"{self.admin_site.name}:{opts.app_label}_{opts.model_name}_changelist") 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().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).select_related("revision") ) # 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_str(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 view or change permissions for model if hasattr(self, 'has_view_or_change_permission'): # for Django >= 2.1 if not self.has_view_or_change_permission(request): raise PermissionDenied else: if not self.has_change_permission(request): raise PermissionDenied opts = self.model._meta action_list = [ { "revision": version.revision, "url": reverse( f"{self.admin_site.name}:{opts.app_label}_{opts.model_name}_revision", 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().history_view(request, object_id, context) django-reversion-5.0.12/reversion/apps.py000066400000000000000000000003541455625272200204200ustar00rootroot00000000000000from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ class ReversionConfig(AppConfig): name = 'reversion' verbose_name = _('Reversion') default_auto_field = 'django.db.models.AutoField' django-reversion-5.0.12/reversion/errors.py000066400000000000000000000005451455625272200207730ustar00rootroot00000000000000class 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.""" django-reversion-5.0.12/reversion/locale/000077500000000000000000000000001455625272200203405ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/ar/000077500000000000000000000000001455625272200207425ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/ar/LC_MESSAGES/000077500000000000000000000000001455625272200225275ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/ar/LC_MESSAGES/django.mo000066400000000000000000000050441455625272200243310ustar00rootroot00000000000000|LQ> BDHYr3P(XH pVl4C^m~FB$):dt=Z<+ ph         Choose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDate/timeHistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createduserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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; أختر تاريخ من القائمه أدناه لإسترجاع نسخه سابقه من هذا الكيانأختر تاريخ من القائمه أدناه لأعاده نسخه سابقه من هذا الكيانالتعليقالتاريخ/ الوقتالتاريخالرئيسيهالنسخة الأوليةأنقر على حفظ أدناه لأسترجاع هذه النسخهأنقر على حفظ أدناه لإعاده هذه النسخهإستعيد %(name)sأستعيد المحذوف من %(name)sأعد %(name)sإسترجع %(verbose_name)sاُعيد لنسخه سابقه، حُفظ في %(datetime)sتم أعاده %(model)s "%(name)s" بنجاح ، يمكنك/ي التعديل مجددالا يوجد كيانات محذوفه لإسترجاعهالا يوجد تاريخ تعديل لهذا الكيان. ربما لم يُنشأ من موقع الإدارهالمستخدمالتعليقتاريخ الأنشاءالمستخدمdjango-reversion-5.0.12/reversion/locale/ar/LC_MESSAGES/django.po000066400000000000000000000100331455625272200243260ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: LANGUAGE \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 "أنقر على حفظ أدناه لإعاده هذه النسخه" django-reversion-5.0.12/reversion/locale/cs/000077500000000000000000000000001455625272200207455ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/cs/LC_MESSAGES/000077500000000000000000000000001455625272200225325ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/cs/LC_MESSAGES/django.mo000066400000000000000000000046741455625272200243440ustar00rootroot00000000000000| LQK BD ev 3P (]XlD}N  ,7@ FBSB!)&.P6`& s>     Add %(name)sChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeHistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRecover deleted %(verbose_name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.UserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3; Přidat %(name)sZvolte datum ze seznamu níže pro obnovení smazané verze objektu.Zvolte datum ze seznamu níže pro návrat k předchozí verzi tohoto objektu.KomentářDATETIME_FORMATDatum/časHistorieDomůPrvní verzeKlikněte na tlačítko uložit pro obnovení této verze objektu.Klikněte na tlačítko uložit pro návrat k této verzi objektu.Obnovit %(name)sObnovit smazané %(name)sObnovit smazané %(verbose_name)sNavrátit se k předchozí verzi %(name)sNavrátit %(verbose_name)s k předchozí verziVráceno do předchozí verze uložené v %(datetime)sObjekt %(model)s "%(name)s" byl úspěšně obnoven. Můžete ho znovu začít upravovat níže.Žádné smazané objekty k obnovení.Tento objekt nemá uloženou žádnou historii změn. Zřejmě nebyl přidát přes toto administrační rozhraní.Uživateldjango-reversion-5.0.12/reversion/locale/cs/LC_MESSAGES/django.po000066400000000000000000000075051455625272200243430ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " "<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\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." django-reversion-5.0.12/reversion/locale/da/000077500000000000000000000000001455625272200207245ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/da/LC_MESSAGES/000077500000000000000000000000001455625272200225115ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/da/LC_MESSAGES/django.mo000066400000000000000000000044471455625272200243210ustar00rootroot00000000000000|LQ> BDHYr3P(XH BX_\ IK86V/jd        Choose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDate/timeHistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createduserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); Vælg en dato fra listen her under for at gendanne til en tidligere version af objektet.Vælg en dato fra listen her under for at Revertere til en tidligere version af det her objekt.KommentarDato/tidHistorikHjemFørste version.Tryk på gem knappen nedenunder for at genskab denne version af objektet.Tryk på gem her nedenunder for at revertere til denne version af objektet.Genskab %(name)sGendan slettede %(name)sRevertere %(name)sRevertere %(verbose_name)sGendannet til tidligere version, gemt den %(datetime)sGendannelsen af %(model)s "%(name)s" var succesfuld. Du kan redigere den igenher underDer findes inden slettede objekter at gendanne.Det her objekt har ingen ændringshistorik. Det er sandsynligvis ikke tilføjet viadette admin-side.Brugerkommentaroprettelsesdatobrugerdjango-reversion-5.0.12/reversion/locale/da/LC_MESSAGES/django.po000066400000000000000000000070171455625272200243200ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: LANGUAGE \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." django-reversion-5.0.12/reversion/locale/de/000077500000000000000000000000001455625272200207305ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/de/LC_MESSAGES/000077500000000000000000000000001455625272200225155ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/de/LC_MESSAGES/django.mo000066400000000000000000000050511455625272200243150ustar00rootroot00000000000000 LQc BD8}  3P/(X:BtW} J T ` kv|>=+$?,d>a Bh t      Add %(name)sChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeHistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover %sRecover deleted %(name)sRecover deleted %(verbose_name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.UserProject-Id-Version: reversion Report-Msgid-Bugs-To: PO-Revision-Date: 2009-02-03 08:41+0100 Last-Translator: Jannis Leidel Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); %(name)s hinzufügenWählen Sie einen Zeitpunk aus der untenstehenden Liste, um eine gelöschte Version des Objektes wiederherzustellen.Wählen Sie einen Zeitpunkt aus der untenstehenden Liste aus, um zu einer vorherigen Version dieses Objektes zurückzukehren.Kommentarj. N Y, H:iDatum/ZeitGeschichteStartUrsprüngliche Version.Sichern Sie, um diese Version des Objektes wiederherzustellen.Sichern Sie, um das Objekt zu dieser Version zurückzusetzen.%(name)s wiederherstellen%s wiederherstellenGelöschte %(name)s wiederherstellenGelöschte %(verbose_name)s wiederherstellen%(name)s zurücksetzen%(verbose_name)s zurücksetzenZu vorheriger Version zurückgesetzt, %(datetime)s gespeichert%(model)s "%(name)s" wurde erfolgreich zurückgesetzt. Sie können mit der Bearbeitung forfahren.Es sind keine gelöschten Objekte zur Wiederherstellung vorhanden.Dieses Objekt hat keine Änderungsgeschichte. Es wurde möglicherweise nicht über diese Verwaltungsseiten angelegt.Benutzerdjango-reversion-5.0.12/reversion/locale/de/LC_MESSAGES/django.po000066400000000000000000000077541455625272200243340ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: 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: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." django-reversion-5.0.12/reversion/locale/es/000077500000000000000000000000001455625272200207475ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/es/LC_MESSAGES/000077500000000000000000000000001455625272200225345ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/es/LC_MESSAGES/django.mo000066400000000000000000000046411455625272200243400ustar00rootroot00000000000000L QV BD5z3P(QXz jW][  ' CMTBeC/:IU%r s {        Choose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDate/timeDeleted %(verbose_name)s.HistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createduserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: 2013-08-31 16:22-0500 Last-Translator: Alexander Ayasca Esquives Language-Team: LANGUAGE Language: es MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); Escoja una fecha de la lista siguiente para recuperar una versión eliminada del objetoEscoja una fecha de la lista siguiente para revertir a una versión anterior de este objetoComentarioFecha/Hora%(verbose_name)s eliminadosHistorialInicioVersión inicialPresione el botón guardar para recuperar esta versión del objetoPresione el botón guardar para revertir a esta versión del objetoRecuperar %(name)sRecuperar %(name)s eliminadosRevertir %(name)sRevertir %(verbose_name)sRevertido a una versión anterior, grabada el %(datetime)sEl %(model)s "%(name)s" fue revertido satisfactoriamente. Puede editarlo nuevamente No hay objetos eliminados a recuperarEste objeto no tiene un historial de cambios. Probablemente no fue añadido por medio del sitio de administraciónUsuariocomentariofecha de creaciónusuariodjango-reversion-5.0.12/reversion/locale/es/LC_MESSAGES/django.po000066400000000000000000000072671455625272200243520ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: LANGUAGE \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" django-reversion-5.0.12/reversion/locale/es_AR/000077500000000000000000000000001455625272200213315ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/es_AR/LC_MESSAGES/000077500000000000000000000000001455625272200231165ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/es_AR/LC_MESSAGES/django.mo000066400000000000000000000046421455625272200247230ustar00rootroot00000000000000L QV BD5z3P(QXz G]:`   +5<CND;4]p){t |        Choose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDate/timeDeleted %(verbose_name)s.HistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createduserProject-Id-Version: Report-Msgid-Bugs-To: PO-Revision-Date: 2015-10-11 19:12-0300 Last-Translator: Gonzalo Bustos Language-Team: Spanish (Argentina) Language: es_AR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); X-Generator: Poedit 1.6.10 Elija una fecha del listado a continuación para restaurar una versión eliminada del objeto.Elija una fecha del listado a continuación para revertir a una versión anterior de este objetoComentarioFecha/hora%(verbose_name)s eliminadosHistorialInicioVersión inicial.Presione el botón guardar para restaurar esta versión del objeto.Presione el botón guardar para revertir a esta versión del objeto.Restaurar %(name)sRestaurar %(name)s eliminadosRevertir %(name)sRevertir %(verbose_name)sRevertido a una versión anterior, guardada el %(datetime)sEl %(model)s "%(name)s" fue revertido con éxito. Puede editarlo nuevamente a continuación.No hay objetos eliminados para restaurar.Este objeto no tiene un historial de cambios. Es probable que no haya sido agregado a través del sitio de administración.Usuariocomentariofecha de creaciónusuariodjango-reversion-5.0.12/reversion/locale/es_AR/LC_MESSAGES/django.po000066400000000000000000000072471455625272200247320ustar00rootroot00000000000000# 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." django-reversion-5.0.12/reversion/locale/fr/000077500000000000000000000000001455625272200207475ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/fr/LC_MESSAGES/000077500000000000000000000000001455625272200225345ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/fr/LC_MESSAGES/django.mo000066400000000000000000000047611455625272200243430ustar00rootroot00000000000000lLQ. BDbs3P(9XbYXis    '/`Al$DWBrf8 U     Choose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeDeleted %(verbose_name)s.HistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.UserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: 2011-09-21 16:31-0400 Last-Translator: Etienne Desautels Language-Team: LANGUAGE Language: fr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Choisissez une date dans la liste ci-dessous afin de récupérer un élément supprimé.Choisissez une date dans la liste ci-dessous afin de restaurer une version précédente de cet élément.Commentairej F Y H:i:sDate/heureSupprimé %(verbose_name)s.HistoriqueAccueilVersion initiale.Cliquez sur le bouton Enregistrer ci-dessous afin de récupérer cet élément.Cliquez sur le bouton Enregistrer ci-dessous pour restaurer cette version de l’élément.Récupérer %(name)sRécupérer %(name)s supprimésRestaurer %(name)sRestaurer %(verbose_name)sRestauré depuis une version précédente, sauvée le %(datetime)sL’élément %(model)s "%(name)s" a été restauré avec succès. Vous pouvez l’éditer à nouveau.Il n’y a pas d’éléments supprimés à récupérer.Cet élément ne possède pas d’historique de modifications. Il n’a probablement pas été ajouté à partir de ce site d’administration.Utilisateurdjango-reversion-5.0.12/reversion/locale/fr/LC_MESSAGES/django.po000066400000000000000000000075151455625272200243460ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: LANGUAGE \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 d’historique de modifications. Il n’a " "probablement pas été ajouté à partir de ce site d’administration." #: 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 Enregistrer 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 n’y 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 Enregistrer ci-dessous pour " "restaurer cette version de l’élément." django-reversion-5.0.12/reversion/locale/he/000077500000000000000000000000001455625272200207345ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/he/LC_MESSAGES/000077500000000000000000000000001455625272200225215ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/he/LC_MESSAGES/django.mo000066400000000000000000000051021455625272200243160ustar00rootroot00000000000000l LQ; BDDU n3P(<XeY]mt@IXj{kk\ p&<f% 3 v 7     Add %(name)sChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeHistoryHomePress the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRecover deleted %(verbose_name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.UserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: 2009-12-10 10:45+0200 Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3; הוספת %(name)sנא לבחור תאריך מתחת כדי לאחזר גרסה מחוקה של אובייקטנא לבחור תאריך מהרשימה להלן כדי לשחזר לגרסה קודמת של אובייקט זה.הערהd.m.‏Y H:i:sתאריך/שעההיסטוריהראשינא ללחוץ על לחצן השמירה מתחת כדי לאחזר לגרסה זו של האובייקטנא ללחוץ על לחצן השמירה מתחת כדי לשחזר לגרסה זו של האובייקטאחזור %(name)sשחזור %(name)s שנמחקואחזור %(verbose_name)s שנמחקשחזור %(name)sשחזור %(verbose_name)sשוחזר לגרסה קודמת, נשמרה ב-%(datetime)sשחזור %(model)s "%(name)s" לגרסה קודמת הצליח. ניתן לערוך שוב מתחת.אין אובייקטים מחוקים לאחזורלאובייקט זה אין היסטוריית שינוי. כנראה לא התווסף דרך ממשק הניהול.משתמשdjango-reversion-5.0.12/reversion/locale/he/LC_MESSAGES/django.po000066400000000000000000000076301455625272200243310ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % " "1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\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 "נא ללחוץ על לחצן השמירה מתחת כדי לשחזר לגרסה זו של האובייקט" django-reversion-5.0.12/reversion/locale/it/000077500000000000000000000000001455625272200207545ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/it/LC_MESSAGES/000077500000000000000000000000001455625272200225415ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/it/LC_MESSAGES/django.mo000066400000000000000000000045351455625272200243470ustar00rootroot00000000000000l LQ; BDDU n3P(<Xe>_`t MML$9aR-sV     Add %(name)sChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeHistoryHomePress the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRecover deleted %(verbose_name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.UserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: 2009-08-29 13:44+0100 Last-Translator: Marco Beri Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); Aggiungi %(name)sScegli una data dall'elenco qui sotto per recuperare una versione cancellata di questo oggetto.Scegli una data dall'elenco qui sotto per ritornare a una precedente versione di questo oggetto.Commentod/m/Y, G:iData/oraStoriaHomePremi il pulsante Salva in basso per recuperare questa versione dell'oggetto.Premi il pulsante Salva in basso per ritornare a questa versione dell'oggettoRecupera %(name)sRecupera %(name)s cancellatiRecupera %(verbose_name)s cancellatoRitorna %(name)sRitorna %(verbose_name)sRitorna alla precedente versione, salvata il %(datetime)s%(model)s "%(name)s" è alla versione precedente. Puoi effettuare nuove modifiche se lo desideri.Non ci sono oggetti cancellati da recuperare.Questo oggetto non ha una storia di modifiche. Probabilmente non è stato aggiunto attraverso questo sito di Admin.Utentedjango-reversion-5.0.12/reversion/locale/it/LC_MESSAGES/django.po000066400000000000000000000074331455625272200243520ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: 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: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" django-reversion-5.0.12/reversion/locale/nb/000077500000000000000000000000001455625272200207375ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/nb/LC_MESSAGES/000077500000000000000000000000001455625272200225245ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/nb/LC_MESSAGES/django.mo000066400000000000000000000046411455625272200243300ustar00rootroot00000000000000lLQ. BDbs3P(9XbUW_  "+ENSQcQ:P:nK/t%      Choose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeDeleted %(verbose_name)s.HistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.UserProject-Id-Version: django-reversion Report-Msgid-Bugs-To: PO-Revision-Date: 2011-10-17 10:17+0100 Last-Translator: Sindre Sorhus Language-Team: Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1) X-Poedit-Language: Norwegian Bokmal X-Poedit-Country: NORWAY X-Poedit-SourceCharset: utf-8 Velg en dato fra listen nedenfor for å gjenopprette en slettet versjon av et objekt.Velg en dato fra listen nedenfor for å gå tilbake til en tidligere versjon av dette objektet.Kommentarj. F Y H:iDato/tidSlettet %(verbose_name)s.HistorieHjemInitial versjonTrykk på lagre-knappen nedenfor for å gjenopprette denne versjonen av objektet.Trykk på lagre-knappen under for å gå tilbake til denne versjonen av objektet.Gjenopprett %(name)sGjenopprett slettede %(name)sTilbakestill %(name)sTilbakestill %(verbose_name)sGjenopprettet til forrige versjon, lagret den %(datetime)s%(model)s "%(name)s" ble gjenopprettet. Du kan redigere den igjen nedenfor.Finner ingen slettede objekter å gjenopprette.Dette objektet har ingen endringshistorie. Objektet er sannsynligvis ikke blitt lagt inn via dette admin nettstedet.Brukerdjango-reversion-5.0.12/reversion/locale/nb/LC_MESSAGES/django.po000066400000000000000000000073031455625272200243310ustar00rootroot00000000000000# Norwegian translation for django-reversion # This file is distributed under the same license as the django-reversion package. # Sindre Sorhus , 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 \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." django-reversion-5.0.12/reversion/locale/nl/000077500000000000000000000000001455625272200207515ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/nl/LC_MESSAGES/000077500000000000000000000000001455625272200225365ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/nl/LC_MESSAGES/django.mo000066400000000000000000000045511455625272200243420ustar00rootroot00000000000000|LQ> BD]n3P(4X] PX&H  FJf0RF=` 8 B N _     Choose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDate/timeDeleted %(verbose_name)s.HistoryInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createduserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Alexander Schoemaker Language-Team: Dutch Language: nl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1) Selecteer een datum uit de lijst om een verwijderde versie van het object te herstellen.Selecteer een datum uit de lijst om een vorige versie terug te plaatsen.ToelichtingDatum/tijdstip%(verbose_name)s is verwijderd.GeschiedenisEerste versie.Klik op onderstaande knop om deze versie van het object te herstellen.Klik op onderstaande knop om deze versie van het object terug te plaatsen.Herstel %(name)sHerstel verwijderde %(name)s%(name)s terugplaatsen%(verbose_name)s terugplaatsenVorige versie van %(datetime)s is teruggeplaatst%(model)s "%(name)s" is succesvol teruggeplaatst. Je kunt het nu opnieuw wijzigen.Er zijn geen verwijderde objecten die hersteld kunnen worden.Dit object bevat geen wijzigingshistorie. Vermoedelijk is het niet vanuit sitebeheer toegevoegd.Gebruikertoelichtingdatum aangemaaktgebruikerdjango-reversion-5.0.12/reversion/locale/nl/LC_MESSAGES/django.po000066400000000000000000000073671455625272200243550ustar00rootroot00000000000000# 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 , 2012. # Bouke Haarsma , 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 \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." django-reversion-5.0.12/reversion/locale/pl/000077500000000000000000000000001455625272200207535ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/pl/LC_MESSAGES/000077500000000000000000000000001455625272200225405ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/pl/LC_MESSAGES/django.mo000066400000000000000000000051621455625272200243430ustar00rootroot00000000000000 LQc BDR 3 P>(XNSS   H HU% 8$ ]] 7 r f     Add %(name)sChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeDeleted %(verbose_name)s.HistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRecover deleted %(verbose_name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.UserProject-Id-Version: reversion Report-Msgid-Bugs-To: PO-Revision-Date: 2011-03-21 20:12+0100 Last-Translator: Zbigniew Siciarz Language-Team: LANGUAGE Language: pl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3); Dodaj %(name)sWybierz datę z poniższej listy, by przywrócić usuniętą wersję obiektu. Wybierz datę z poniższej listy, by przywrócić ten obiekt do poprzedniej wersji.Komentarzj. N Y, H:iData/czasUsunięto %(verbose_name)sHistoriaStrona głównaPierwsza wersja.Naciśnij przycisk Zapisz poniżej, by przywrócić tę wersję obiektu.Naciśnij przycisk Zapisz poniżej, by przywrócić tę wersję obiektu.Przywróć %(name)sOdzyskaj usunięte %(name)sPrzywróć usunięte %(verbose_name)sPrzywróć %(name)sPrzywróć %(verbose_name)sPrzywrócono poprzednią wersję, zapisaną %(datetime)s%(model)s "%(name)s" został pomyślnie przywrócony. Możesz go ponownie edytować poniżej.Nie ma żadnych usuniętych obiektów do przywrócenia.Ten obiekt nie posiada historii zmian. Prawdopodobnie nie został dodany za pomocą tego panelu administracyjnego.Użytkownikdjango-reversion-5.0.12/reversion/locale/pl/LC_MESSAGES/django.po000066400000000000000000000100071455625272200243400ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: LANGUAGE \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n" "%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n" "%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\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" django-reversion-5.0.12/reversion/locale/pt_BR/000077500000000000000000000000001455625272200213465ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/pt_BR/LC_MESSAGES/000077500000000000000000000000001455625272200231335ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/pt_BR/LC_MESSAGES/django.mo000066400000000000000000000045121455625272200247340ustar00rootroot00000000000000l LQ; BDDU n3P(<XeLS#Vw    IMI$6SO+qA     Add %(name)sChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeHistoryHomePress the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRecover deleted %(verbose_name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.UserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: 2009-08-29 13:44+0100 Last-Translator: Partec Language-Team: Tangerina Lab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); Adicionar %(name)sEscolha uma data da lista abaixo para recuperar uma versão excluída de um objeto.Escolha uma data da lista abaixo para reverter para uma versão anterior deste objeto.Comentáriod/m/Y, G:iData/horaHistóricoHomePressione o botão salvar, abaixo, para recuperar essa versão do objeto.Pressione o botão salvar, abaixo, para reverter para essa versão do objeto.Recuperar %(name)sRecuperar %(name)s excluídoRecuperar %(verbose_name)s excluídoReverter %(name)sReverter %(verbose_name)sRevertido para versão anterior, salva em %(datetime)s%(model)s "%(name)s" foi revertido com sucesso. Você pode editar novamente abaixo.Não há objetos excluídos para recuperar.Este objeto não possui um histórico de mudanças. Ele provavelmente não foi adicionado por este site de admin.Usuáriodjango-reversion-5.0.12/reversion/locale/pt_BR/LC_MESSAGES/django.po000066400000000000000000000074111455625272200247400ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: Tangerina Lab \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: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" django-reversion-5.0.12/reversion/locale/ru/000077500000000000000000000000001455625272200207665ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/ru/LC_MESSAGES/000077500000000000000000000000001455625272200225535ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/ru/LC_MESSAGES/django.mo000066400000000000000000000060711455625272200243560ustar00rootroot00000000000000| LQK BDT ep 3P(WX  )= LYz!Y { 4 <  ! ^A o \ m      Add %(name)sChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeHistoryHomePress the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover %sRecover deleted %(name)sRecover deleted %(verbose_name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.UserProject-Id-Version: reversion Report-Msgid-Bugs-To: PO-Revision-Date: 2009-10-14 22:21+0300 Last-Translator: Alexander Yakovlev Language-Team: Russian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Poedit-Language: Russian X-Poedit-Country: RUSSIAN FEDERATION Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3); Добавить %(name)sВыберите дату из списка, чтобы восстановить удаленную версию объекта.Выберите дату из списка, чтобы вернуть предыдущую версию этого объекта.Комментарийd.m.Y H:iДата/времяИсторияНачалоНажмите кнопку "Сохранить" далее, чтобы восстановить эту версию объекта.Нажмите кнопку "Сохранить" далее, чтобы вернуть эту версию объекта.Восстановить %(name)sВосстановить %sВосстановить удаленный %(name)sВосстановить удаленный %(verbose_name)sВернуть %(name)sВернуть %(verbose_name)sВозвращено к предыдущей версии, сохраненной %(datetime)s%(model)s "%(name)s" возвращен. Можете продолжить его редактирование.Не найдено удаленных объектов для восстановления.У этого объекта нет истории изменений. Возможно, он был добавлен не через администраторский сайт.Пользовательdjango-reversion-5.0.12/reversion/locale/ru/LC_MESSAGES/django.po000066400000000000000000000106671455625272200243670ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: Russian \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" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" "%100>=11 && n%100<=14)? 2 : 3);\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 "Нажмите кнопку \"Сохранить\" далее, чтобы вернуть эту версию объекта." django-reversion-5.0.12/reversion/locale/sk/000077500000000000000000000000001455625272200207555ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/sk/LC_MESSAGES/000077500000000000000000000000001455625272200225425ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/sk/LC_MESSAGES/django.mo000066400000000000000000000047001455625272200243420ustar00rootroot00000000000000|LQ> BDHYr3P(XH TW[   (.KDL( 043eY9Z-      Choose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDate/timeHistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createduserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Juraj Bubniak Language-Team: Slovak Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3); Pre obnovenie vymazanej verzie objektu vyberte dátum z nižšie uvedeného zoznamu.Vyberte dátum z nižšie uvedeného zoznamu pre návrat k predošlej verzii tohto objektu.KomentárDátum/časHistóriaDomovPočiatočná verzia.Pre obnovenie tejto verzie objektu kliknite na tlačidlo uložiť nižšie.Pre návrat na túto verziu objektu kliknite na tlačidlo uložiť nižšie.Obnoviť %(name)sObnoviť vymazaný %(name)sVrátiť sa k predošlej verzii %(name)sVrátiť sa k predošlej verzii %(verbose_name)sObnovená predošlá verzia, uložená %(datetime)sObjekt %(model)s "%(name)s" bol úspešne obnovený. Môžete ho znovu upraviť nižšie.Niesú dostupné žiadne vymazané objekty pre obnovenie.Tento objekt nemá históriu zmien. Pravdepodobne nebol pridaný cez túto admin stránku.Používateľkomentárdátum vytvoreniapoužívateľdjango-reversion-5.0.12/reversion/locale/sk/LC_MESSAGES/django.po000066400000000000000000000072751455625272200243570ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: Slovak \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n " ">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\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." django-reversion-5.0.12/reversion/locale/sl_SI/000077500000000000000000000000001455625272200213515ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/sl_SI/LC_MESSAGES/000077500000000000000000000000001455625272200231365ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/sl_SI/LC_MESSAGES/django.mo000066400000000000000000000052341455625272200247410ustar00rootroot00000000000000%01L8QCG<c BD Paz3(XX] erwCOLS\RM  ?? P ` $z  9 $ [ r |      ActionChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.Could not load %(object_repr)s version - incompatible version data.Could not load %(object_repr)s version - unknown serializer %(format)s.Could not save %(object_repr)s version - missing dependency.Date/timeHistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThere are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createduserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Bor Plestenjak Language-Team: LANGUAGE Language: sl_SI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3); DejanjeZa obnovitev izbrisanega objekta izberi datum iz spodnjega seznama.Za povrnitev objekta na prejšnjo različico izberi datum iz spodnjega seznama.Različice %(object_repr)s ni bilo možno naložiti zaradi nezdružljivih podatkov.Različice %(object_repr)s ni bilo možno naložiti zaradi neznane serializacije %(format)s.Različice %(object_repr)s ni bilo možno shraniti zaradi manjkajočih odvisnosti.Datum/časZgodovinaDomovZačetna različica.Za obnovitev te različice objekta spodaj pritisni gumb shrani.Za povrnitev te različice objekta spodaj pritisni gumb shrani.Obnovi %(name)sObnovi izbrisane %(name)sPovrni prejšnjo različico %(name)sPovrni %(verbose_name)sPovrnjeno na prejšnjo različico, shranjeno %(datetime)sNi izbrisanih objektov za obnovitev.Ta objekt nima zgodovine sprememb. Verjetno ni bil dodan preko te strani za administracijo.Uporabnikopombadatum nastankauporabnikdjango-reversion-5.0.12/reversion/locale/sl_SI/LC_MESSAGES/django.po000066400000000000000000000076431455625272200247520ustar00rootroot00000000000000# 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 , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-02-07 23:25+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Bor Plestenjak \n" "Language-Team: LANGUAGE \n" "Language: sl_SI\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" "%100==4 ? 2 : 3);\n" #: admin.py:66 msgid "Initial version." msgstr "Začetna različica." #: admin.py:170 #, python-format msgid "Reverted to previous version, saved on %(datetime)s" msgstr "Povrnjeno na prejšnjo različico, shranjeno %(datetime)s" #: admin.py:194 #, python-format msgid "Recover %(name)s" msgstr "Obnovi %(name)s" #: admin.py:210 #, python-format msgid "Revert %(name)s" msgstr "Povrni prejšnjo različico %(name)s" #: admin.py:245 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 "Obnovi izbrisane %(name)s" #: models.py:33 #, python-format msgid "Could not save %(object_repr)s version - missing dependency." msgstr "Različice %(object_repr)s ni bilo možno shraniti zaradi manjkajočih odvisnosti." #: models.py:46 msgid "date created" msgstr "datum nastanka" #: models.py:55 msgid "user" msgstr "uporabnik" #: models.py:61 msgid "comment" msgstr "opomba" #: models.py:233 #, python-format msgid "Could not load %(object_repr)s version - incompatible version data." msgstr "Različice %(object_repr)s ni bilo možno naložiti zaradi nezdružljivih podatkov." #: models.py:237 #, python-format msgid "Could not load %(object_repr)s version - unknown serializer %(format)s." msgstr "Različice %(object_repr)s ni bilo možno naložiti zaradi neznane serializacije %(format)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 "Za povrnitev objekta na prejšnjo različico izberi datum iz spodnjega seznama." #: 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 "Uporabnik" #: templates/reversion/object_history.html:17 msgid "Action" msgstr "Dejanje" #: 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 "Ta objekt nima zgodovine sprememb. Verjetno ni bil dodan preko te strani za administracijo." #: templates/reversion/recover_form.html:7 #: templates/reversion/recover_list.html:7 #: templates/reversion/revision_form.html:7 msgid "Home" msgstr "Domov" #: templates/reversion/recover_form.html:20 msgid "Press the save button below to recover this version of the object." msgstr "Za obnovitev te različice objekta spodaj pritisni gumb shrani." #: templates/reversion/recover_list.html:17 msgid "" "Choose a date from the list below to recover a deleted version of an object." msgstr "Za obnovitev izbrisanega objekta izberi datum iz spodnjega seznama." #: templates/reversion/recover_list.html:37 msgid "There are no deleted objects to recover." msgstr "Ni izbrisanih objektov za obnovitev." #: templates/reversion/revision_form.html:11 msgid "History" msgstr "Zgodovina" #: templates/reversion/revision_form.html:12 #, python-format msgid "Revert %(verbose_name)s" msgstr "Povrni %(verbose_name)s" #: templates/reversion/revision_form.html:21 msgid "Press the save button below to revert to this version of the object." msgstr "Za povrnitev te različice objekta spodaj pritisni gumb shrani." django-reversion-5.0.12/reversion/locale/sv/000077500000000000000000000000001455625272200207705ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/sv/LC_MESSAGES/000077500000000000000000000000001455625272200225555ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/sv/LC_MESSAGES/django.mo000066400000000000000000000046761455625272200243710ustar00rootroot00000000000000%0L1Q~  B*Dm3P8(X  %A*Mll ' 1 ;E_hlBD8M8jW/e+     Choose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.CommentDATETIME_FORMATDate/timeDeleted %(verbose_name)s.HistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThe %(model)s "%(name)s" was reverted successfully. You may edit it again below.There are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createduserProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1) Välj ett datum i listan här nedan för att återskapa en borttagen version.Välj ett datum från listan här under för att återställa till en tidigare version av det här objektet.KommentarY-m-d H:iDatum/tidTog bort %(verbose_name)sHistorikHemFörsta versionen.Tryck på spara här nedan fär att återskapa den här versionen.Tryck på spara här nedan för att återställa den här versionen.Återskapa %(name)sÅterskapa bortagna %(name)sÅterställ %(name)sÅterställ %(verbose_name)sÅtergick till föregående version, sparad %(datetime)sÅterställandet av %(model)s "%(name)s" lyckades. Du kan redigera den igen här nedan.Det finns inga borttagna objekt att återskapa.Det här objektet saknar ändringshistorik. Det skapades förmodligen inte via den här admin-sajten.Användarekommentardatum skapadanvändaredjango-reversion-5.0.12/reversion/locale/sv/LC_MESSAGES/django.po000066400000000000000000000074611455625272200243670ustar00rootroot00000000000000# 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 , 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 \n" "Language-Team: LANGUAGE \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." django-reversion-5.0.12/reversion/locale/uk/000077500000000000000000000000001455625272200207575ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/uk/LC_MESSAGES/000077500000000000000000000000001455625272200225445ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/uk/LC_MESSAGES/django.mo000066400000000000000000000067461455625272200243600ustar00rootroot00000000000000%01L8QCG<c BD Paz3(XX] erZw~Xtf k U e t y   ,  # c. W         ActionChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.Could not load %(object_repr)s version - incompatible version data.Could not load %(object_repr)s version - unknown serializer %(format)s.Could not save %(object_repr)s version - missing dependency.Date/timeHistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThere are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createduserProject-Id-Version: django-reversion Report-Msgid-Bugs-To: https://github.com/etianen/django-reversion/issues PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Illia Volochii Language-Team: Ukrainian Language: uk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3); ДіяВиберіть дату із списку нижче, щоб відновити видалену версію об'єкта.Виберіть дату із списку нижче, щоб повернутися до попередньої версії цього об'єкта.Неможливо завантажити версію "%(object_repr)s" - несумісні дані версій.Неможливо завантажити версію "%(object_repr)s" - невідомий серіалізатор %(format)s.Неможливо зберегти версію "%(object_repr)s" - відсутня залежність.Дата/часІсторіяДомівкаПочаткова версія.Натисніть кнопку "Зберегти" нижче, щоб відновити цю версію об'єкта.Натисніть кнопку "Зберегти" нижче, щоб повернутися до цієї версії об'єкта.Відновити %(name)sВідновити видалені %(name)sПовернути %(name)sПовернути %(verbose_name)sПовернуто до попередньої версії, яка збережена %(datetime)sНе знайдено видалених об'єктів для відновлення.Цей об'єкт не має історії змін. Напевно, він був доданий не через цей сайт адміністрування.Користувачкоментардата створеннякористувачdjango-reversion-5.0.12/reversion/locale/uk/LC_MESSAGES/django.po000066400000000000000000000120431455625272200243460ustar00rootroot00000000000000# Translation of django-reversion into Ukrainian. # This file is distributed under the same license as the django-reversion package. # Illia Volochii , 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 \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=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != " "11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % " "100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " "(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\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 "" "Натисніть кнопку \"Зберегти\" нижче, щоб повернутися до цієї версії об'єкта." django-reversion-5.0.12/reversion/locale/zh_Hans/000077500000000000000000000000001455625272200217325ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/zh_Hans/LC_MESSAGES/000077500000000000000000000000001455625272200235175ustar00rootroot00000000000000django-reversion-5.0.12/reversion/locale/zh_Hans/LC_MESSAGES/django.mo000066400000000000000000000052371455625272200253250ustar00rootroot00000000000000)LQC72{G< 3=EJB[D (3@(tX   %*;3o9v?A/2Fb2 *3) ] m   ' T \ c j w       ActionChoose a date from the list below to recover a deleted version of an object.Choose a date from the list below to revert to a previous version of this object.Could not load %(object_repr)s version - incompatible version data.Could not load %(object_repr)s version - not foundCould not load %(object_repr)s version - unknown serializer %(format)s.Could not save %(object_repr)s version - missing dependency.Date/timeHistoryHomeInitial version.Press the save button below to recover this version of the object.Press the save button below to revert to this version of the object.Recover %(name)sRecover deleted %(name)sReversionsRevert %(name)sRevert %(verbose_name)sReverted to previous version, saved on %(datetime)sThere are no deleted objects to recover.This object doesn't have a change history. It probably wasn't added via this admin site.Usercommentdate createdreversionreversionsuserversionsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0; 操作单击下方的日期以恢复一个已删除的对象。单击下方的日期以恢复当前对象到之前的版本。无法载入 %(object_repr)s 版本 - 不兼容的版本数据。无法载入 %(object_repr)s 版本 - 未找到无法载入 %(object_repr)s 版本 - 未知的序列化 %(format)s。无法保存 %(object_repr)s 版本 - 缺少依赖时间历史首页初始版本单击保存按钮以恢复为此版本。单击保存按钮将此对象恢复到此版本。恢复 %(name)s恢复已删除的 %(name)s版本记录恢复 %(name)s恢复 %(verbose_name)s恢复到 %(datetime)s 的版本没有可供恢复的已删除对象。此对象不存在任何变更历史,它可能不是通过管理站点添加的。用户评论创建日期版本记录版本记录用户版本django-reversion-5.0.12/reversion/locale/zh_Hans/LC_MESSAGES/django.po000066400000000000000000000101571455625272200253250ustar00rootroot00000000000000# 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 , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-01-25 16:26+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \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:70 msgid "Initial version." msgstr "初始版本" #: .\admin.py:178 #, python-format msgid "Reverted to previous version, saved on %(datetime)s" msgstr "恢复到 %(datetime)s 的版本" #: .\admin.py:186 #, python-format msgid "Could not load %(object_repr)s version - not found" msgstr "无法载入 %(object_repr)s 版本 - 未找到" #: .\admin.py:206 #, python-format msgid "Recover %(name)s" msgstr "恢复 %(name)s" #: .\admin.py:222 #, python-format msgid "Revert %(name)s" msgstr "恢复 %(name)s" #: .\admin.py:259 .\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 "恢复已删除的 %(name)s" #: .\apps.py:7 msgid "Reversion" msgstr "版本记录" #: .\models.py:39 #, python-format msgid "Could not save %(object_repr)s version - missing dependency." msgstr "无法保存 %(object_repr)s 版本 - 缺少依赖" #: .\models.py:52 msgid "date created" msgstr "创建日期" #: .\models.py:61 msgid "user" msgstr "用户" #: .\models.py:67 msgid "comment" msgstr "评论" #: .\models.py:116 msgid "revision" msgstr "修改" #: .\models.py:117 msgid "revisions" msgstr "修改" #: .\models.py:275 #, python-format msgid "Could not load %(object_repr)s version - incompatible version data." msgstr "无法载入 %(object_repr)s 版本 - 不兼容的版本数据。" #: .\models.py:279 #, python-format msgid "Could not load %(object_repr)s version - unknown serializer %(format)s." msgstr "无法载入 %(object_repr)s 版本 - 未知的序列化 %(format)s。" #: .\models.py:335 #, fuzzy msgid "version" msgstr "版本" #: .\models.py:336 msgid "versions" 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:23 msgid "Date/time" msgstr "时间" #: .\templates\reversion\object_history.html:16 msgid "User" msgstr "用户" #: .\templates\reversion\object_history.html:17 msgid "Action" 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:7 #: .\templates\reversion\recover_list.html:7 #: .\templates\reversion\revision_form.html:7 msgid "Home" msgstr "首页" #: .\templates\reversion\recover_form.html:20 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:11 msgid "History" msgstr "历史" #: .\templates\reversion\revision_form.html:12 #, python-format msgid "Revert %(verbose_name)s" msgstr "恢复 %(verbose_name)s" #: .\templates\reversion\revision_form.html:21 msgid "Press the save button below to revert to this version of the object." msgstr "单击保存按钮将此对象恢复到此版本。" django-reversion-5.0.12/reversion/management/000077500000000000000000000000001455625272200212155ustar00rootroot00000000000000django-reversion-5.0.12/reversion/management/__init__.py000066400000000000000000000000001455625272200233140ustar00rootroot00000000000000django-reversion-5.0.12/reversion/management/commands/000077500000000000000000000000001455625272200230165ustar00rootroot00000000000000django-reversion-5.0.12/reversion/management/commands/__init__.py000066400000000000000000000037761455625272200251440ustar00rootroot00000000000000from django.apps import apps from django.conf import settings 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().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. if "django.contrib.admin" in settings.INSTALLED_APPS: 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(f"Unknown model: {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(f"Unknown app: {app_label}") selected_models.update(app.get_models()) for model in selected_models: if is_registered(model): yield model django-reversion-5.0.12/reversion/management/commands/createinitialrevisions.py000066400000000000000000000122331455625272200301500ustar00rootroot00000000000000import json from django.apps import apps from django.core.management import CommandError from django.db import connections, 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, add_meta class Command(BaseRevisionCommand): help = "Creates initial revisions for a given app [and model]." def add_arguments(self, parser): super().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.", ) parser.add_argument( "--meta", action="store", default={}, type=json.loads, help=("Specify meta models and corresponding values for each initial revision as JSON" "eg. --meta \"{\"core.RevisionMeta\", {\"hello\": \"world\"}}\""), ) 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"] meta = options["meta"] meta_models = [] for label in meta.keys(): try: model = apps.get_model(label) meta_models.append(model) except LookupError: raise CommandError(f"Unknown model: {label}") meta_values = meta.values() # Determine if we should use queryset.iterator() using = using or router.db_for_write(Revision) server_side_cursors = not connections[using].settings_dict.get('DISABLE_SERVER_SIDE_CURSORS') use_iterator = connections[using].vendor in ("postgresql",) and server_side_cursors # Create revisions. 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", ) live_objs = live_objs.order_by() # Save all the versions. if use_iterator: total = live_objs.count() if total: for obj in live_objs.iterator(batch_size): self.create_revision(obj, using, meta, meta_models, meta_values, comment, model_db) created_count += 1 # Print out a message every batch_size if feeling extra verbose if not created_count % batch_size: self.batch_complete(verbosity, created_count, total) else: # Save all the versions. ids = list(live_objs.values_list("pk", flat=True)) 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(): self.create_revision(obj, using, meta, meta_models, meta_values, comment, model_db) created_count += 1 # Print out a message every batch_size if feeling extra verbose self.batch_complete(verbosity, created_count, total) # Print out a message, if feeling verbose. if verbosity >= 1: self.stdout.write("- Created {total} / {total}".format( total=total, )) def create_revision(self, obj, using, meta, meta_models, meta_values, comment, model_db): with create_revision(using=using): if meta: for model, values in zip(meta_models, meta_values): add_meta(model, **values) set_comment(comment) add_to_revision(obj, model_db=model_db) def batch_complete(self, verbosity, created_count, total): reset_queries() if verbosity >= 2: self.stdout.write("- Created {created_count} / {total}".format( created_count=created_count, total=total, )) django-reversion-5.0.12/reversion/management/commands/deleterevisions.py000066400000000000000000000100201455625272200265650ustar00rootroot00000000000000from 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().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() django-reversion-5.0.12/reversion/middleware.py000066400000000000000000000012471455625272200215740ustar00rootroot00000000000000from reversion.views import _request_creates_revision, create_revision class RevisionMiddleware: """Wraps the entire request in a revision.""" manage_manually = False using = None atomic = True def __init__(self, get_response): self.get_response = create_revision( manage_manually=self.manage_manually, using=self.using, atomic=self.atomic, request_creates_revision=self.request_creates_revision )(get_response) def request_creates_revision(self, request): return _request_creates_revision(request) def __call__(self, request): return self.get_response(request) django-reversion-5.0.12/reversion/migrations/000077500000000000000000000000001455625272200212555ustar00rootroot00000000000000django-reversion-5.0.12/reversion/migrations/0001_squashed_0004_auto_20160611_1202.py000066400000000000000000000053371455625272200272330ustar00rootroot00000000000000# Generated by Django 1.9.7 on 2016-06-06 13:22 from django.conf import settings from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): 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",), 'verbose_name': 'revision', 'verbose_name_plural': 'revisions', }, ), 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",), 'verbose_name': 'version', 'verbose_name_plural': 'versions', }, ), migrations.AlterUniqueTogether( name='version', unique_together={('db', 'content_type', 'object_id', 'revision')}, ), ] django-reversion-5.0.12/reversion/migrations/0002_add_index_on_version_for_content_type_and_db.py000066400000000000000000000006511455625272200334020ustar00rootroot00000000000000# Generated by Django 3.2.6 on 2021-08-16 18:08 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('reversion', '0001_squashed_0004_auto_20160611_1202'), ] operations = [ migrations.AddIndex( model_name='version', index=models.Index(fields=['content_type', 'db'], name='reversion_v_content_f95daf_idx'), ), ] django-reversion-5.0.12/reversion/migrations/__init__.py000066400000000000000000000000001455625272200233540ustar00rootroot00000000000000django-reversion-5.0.12/reversion/models.py000066400000000000000000000371651455625272200207520ustar00rootroot00000000000000from collections import defaultdict from itertools import chain, groupby import logging import django from django.apps import apps from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.core import serializers from django.core.exceptions import ObjectDoesNotExist from django.core.serializers.base import DeserializationError from django.db import IntegrityError, connections, models, router, transaction from django.db.models.deletion import Collector from django.db.models.functions import Cast from django.utils.encoding import force_str from django.utils.functional import cached_property from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ from reversion.errors import RevertError from reversion.revisions import (_follow_relations_recursive, _get_content_type, _get_options) logger = logging.getLogger(__name__) def _safe_revert(versions): unreverted_versions = [] for version in versions: try: with transaction.atomic(using=version.db): version.revert() except (IntegrityError, ObjectDoesNotExist): logger.warning(f'Could not revert to {version}', exc_info=True) unreverted_versions.append(version) if len(unreverted_versions) == len(versions): raise RevertError(gettext("Could not save %(object_repr)s version - missing dependency.") % { "object_repr": unreverted_versions[0], }) if unreverted_versions: _safe_revert(unreverted_versions) 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 get_comment(self): try: LogEntry = apps.get_model('admin.LogEntry') return LogEntry(change_message=self.comment).get_change_message() except LookupError: return self.comment 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) new_objs = [item for item in current_revision if item not in old_revision] for model, group in groupby(new_objs, type): collector.collect(list(group)) collector.delete() # Attempt to revert all revisions. _safe_revert(versions) def __str__(self): return ", ".join(force_str(version) for version in self.version_set.all()) class Meta: verbose_name = _('revision') verbose_name_plural = _('revisions') app_label = "reversion" ordering = ("-pk",) 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): 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", "oracle"): pk_field_name = model._meta.pk.name object_id_cast_target = model._meta.get_field(pk_field_name) if django.VERSION >= (2, 1): # django 2.0 contains a critical bug that doesn't allow the code below to work, # fallback to casting primary keys then # see https://code.djangoproject.com/ticket/29142 if django.VERSION < (2, 2): # properly cast autofields for django before 2.2 as it was fixed in django itself later # see https://github.com/django/django/commit/ac25dd1f8d48accc765c05aebb47c427e51f3255 object_id_cast_target = { "AutoField": models.IntegerField(), "BigAutoField": models.BigIntegerField(), }.get(object_id_cast_target.__class__.__name__, object_id_cast_target) casted_object_id = Cast(models.OuterRef("object_id"), object_id_cast_target) model_qs = ( model._default_manager .using(model_db) .filter(**{pk_field_name: casted_object_id}) ) else: model_qs = ( model._default_manager .using(model_db) .annotate(_pk_to_object_id=Cast("pk", Version._meta.get_field("object_id"))) .filter(_pk_to_object_id=models.OuterRef("object_id")) ) # conditional expressions are being supported since django 3.0 # DISTINCT ON works only for Postgres DB if connection.vendor == "postgresql" and django.VERSION >= (3, 0): subquery = ( self.get_for_model(model, model_db=model_db) .filter(~models.Exists(model_qs)) .order_by("object_id", "-pk") .distinct("object_id") .values("pk") ) else: subquery = ( self.get_for_model(model, model_db=model_db) .annotate(pk_not_exists=~models.Exists(model_qs)) .filter(pk_not_exists=True) .values("object_id") .annotate(latest_pk=models.Max("pk")) .values("latest_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. # Filter by model to reduce query execution time. return self.get_for_model(model, model_db=model_db).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 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): version_options = _get_options(self._model) data = self.serialized_data data = force_str(data.encode("utf8")) try: return list(serializers.deserialize(self.format, data, ignorenonexistent=True, use_natural_foreign_keys=version_options.use_natural_foreign_keys))[0] except DeserializationError: raise RevertError(gettext("Could not load %(object_repr)s version - incompatible version data.") % { "object_repr": self.object_repr, }) except serializers.SerializerDoesNotExist: raise RevertError(gettext("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 object_version.m2m_data and 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: verbose_name = _('version') verbose_name_plural = _('versions') app_label = 'reversion' unique_together = ( ("db", "content_type", "object_id", "revision"), ) indexes = ( models.Index( fields=["content_type", "db"] ), ) 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().__init__(expression, output_field=models.TextField()) def as_sql(self, compiler, connection): self.extra["db_type"] = self.output_field.db_type(connection) return super().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") ) ): return getattr(left_query, method)(**{ f"{left_field_name}__in": 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 = f"{left_field_name}_str" 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 = f"{right_field_name}_str" right_subquery = right_subquery.annotate(**{ right_field_name_str: _Str(right_field_name), }).values_list(right_field_name_str, flat=True) right_field_name = right_field_name_str # Use Exists if running on the same DB, it is much much faster exist_annotation_name = f"{right_subquery.model._meta.db_table}_annotation_str" right_subquery = right_subquery.filter(**{right_field_name: models.OuterRef(left_field_name)}) left_query = left_query.annotate(**{exist_annotation_name: models.Exists(right_subquery)}) return getattr(left_query, method)(**{exist_annotation_name: True}) django-reversion-5.0.12/reversion/revisions.py000066400000000000000000000330271455625272200215010ustar00rootroot00000000000000from contextvars import ContextVar from collections import namedtuple, defaultdict from contextlib import contextmanager from functools import wraps 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_str from django.utils import timezone 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", "use_natural_foreign_keys", )) _StackFrame = namedtuple("StackFrame", ( "manage_manually", "user", "comment", "date_created", "db_versions", "meta", )) _stack = ContextVar("reversion-stack", default=[]) def is_active(): return bool(_stack.get()) def _current_frame(): if not is_active(): raise RevisionManagementError("There is no active revision for this thread") return _stack.get()[-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=(), ) _stack.set(_stack.get() + [stack_frame]) def _update_frame(**kwargs): _stack.get()[-1] = _current_frame()._replace(**kwargs) def _pop_frame(): prev_frame = _current_frame() stack = _stack.get() del 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)): yield from follow_obj.all() 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_str(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, use_natural_foreign_keys=version_options.use_natural_foreign_keys, ), object_repr=force_str(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. # Use _base_manager so we don't have problems when _default_manager is overriden 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_str, model._base_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._base_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): context = transaction.atomic(using=using) if atomic else _dummy_context() with context: _push_frame(manage_manually, using) try: yield if transaction.get_connection(using).in_atomic_block and transaction.get_rollback(using): # Transaction is in invalid state due to catched exception within yield statement. # Do not try to create Revision, otherwise it would lead to the transaction management error. # # Atomic block could be called manually around `create_revision` context manager. # That's why we have to check connection flag instead of `atomic` variable value. return # 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 _stack.get()[:-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: 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 = field.remote_field.through if isinstance(m2m_model, str): 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, use_natural_foreign_keys=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, use_natural_foreign_keys=use_natural_foreign_keys, ) # 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, ) django-reversion-5.0.12/reversion/signals.py000066400000000000000000000002301455625272200211060ustar00rootroot00000000000000from django.dispatch.dispatcher import Signal # Signal arguments: revision and versions pre_revision_commit = Signal() post_revision_commit = Signal() django-reversion-5.0.12/reversion/templates/000077500000000000000000000000001455625272200210775ustar00rootroot00000000000000django-reversion-5.0.12/reversion/templates/reversion/000077500000000000000000000000001455625272200231135ustar00rootroot00000000000000django-reversion-5.0.12/reversion/templates/reversion/change_list.html000066400000000000000000000006531455625272200262650ustar00rootroot00000000000000{% 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 %}
  • {% blocktrans with cl.opts.verbose_name_plural|escape as name %}Recover deleted {{name}}{% endblocktrans %}
  • {% endif %} {{block.super}} {% endblock %} django-reversion-5.0.12/reversion/templates/reversion/object_history.html000066400000000000000000000035631455625272200270370ustar00rootroot00000000000000{% extends "admin/object_history.html" %} {% load i18n %} {% block content %}

    {% blocktrans %}Choose a date from the list below to revert to a previous version of this object.{% endblocktrans %}

    {% if action_list %} {% for action in action_list %} {% endfor %}
    {% trans 'Date/time' %} {% trans 'User' %} {% trans 'Action' %}
    {{action.revision.date_created|date:"DATETIME_FORMAT"}} {% if action.revision.user %} {{action.revision.user.get_username}} {% if action.revision.user.get_full_name %} ({{action.revision.user.get_full_name}}){% endif %} {% else %} — {% endif %} {{action.revision.get_comment|linebreaksbr|default:""}}
    {% else %}

    {% trans "This object doesn't have a change history. It probably wasn't added via this admin site." %}

    {% endif %}
    {% endblock %} django-reversion-5.0.12/reversion/templates/reversion/recover_form.html000066400000000000000000000017741455625272200265020ustar00rootroot00000000000000{% extends "reversion/revision_form.html" %} {% load i18n admin_urls %} {% block breadcrumbs %} {% endblock %} {% block object-tools %}{% endblock %} {% block form_top %}

    {% blocktrans %}Press the save button below to recover this version of the object.{% endblocktrans %}

    {% 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 %} django-reversion-5.0.12/reversion/templates/reversion/recover_list.html000066400000000000000000000033461455625272200265070ustar00rootroot00000000000000{% extends "admin/base_site.html" %} {% load i18n l10n admin_urls %} {% block breadcrumbs %} {% endblock %} {% block content %}

    {% blocktrans %}Choose a date from the list below to recover a deleted version of an object.{% endblocktrans %}

    {% if deleted %} {% for deletion in deleted %} {% endfor %}
    {% trans 'Date/time' %} {{opts.verbose_name|capfirst}}
    {{deletion.revision.date_created}} {{deletion.object_repr}}
    {% else %}

    {% trans "There are no deleted objects to recover." %}

    {% endif %}
    {% endblock %} django-reversion-5.0.12/reversion/templates/reversion/revision_form.html000066400000000000000000000021711455625272200266630ustar00rootroot00000000000000{% extends "admin/change_form.html" %} {% load i18n admin_urls %} {% block breadcrumbs %} {% endblock %} {% block object-tools %}{% endblock %} {% block form_top %}

    {% blocktrans %}Press the save button below to revert to this version of the object.{% endblocktrans %}

    {% 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 %} django-reversion-5.0.12/reversion/views.py000066400000000000000000000037521455625272200206170ustar00rootroot00000000000000from functools import wraps from reversion.revisions import create_revision as create_revision_base, set_user, get_user 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 request.user.is_authenticated and get_user() is None: set_user(request.user) def create_revision(manage_manually=False, using=None, atomic=True, request_creates_revision=None): """ View decorator that wraps the request in a revision. The revision will have it's user set from the request automatically. """ request_creates_revision = request_creates_revision or _request_creates_revision def decorator(func): @wraps(func) def do_revision_view(request, *args, **kwargs): if request_creates_revision(request): with create_revision_base(manage_manually=manage_manually, using=using, atomic=atomic): response = func(request, *args, **kwargs) # Otherwise, we're good. _set_user_from_request(request) return response return func(request, *args, **kwargs) return do_revision_view return decorator class RevisionMixin: """ 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().__init__(*args, **kwargs) self.dispatch = create_revision( manage_manually=self.revision_manage_manually, using=self.revision_using, atomic=self.revision_atomic, request_creates_revision=self.revision_request_creates_revision )(self.dispatch) def revision_request_creates_revision(self, request): return _request_creates_revision(request) django-reversion-5.0.12/setup.cfg000066400000000000000000000000651455625272200167070ustar00rootroot00000000000000[flake8] max-line-length=120 exclude=venv,migrations django-reversion-5.0.12/setup.py000066400000000000000000000033151455625272200166010ustar00rootroot00000000000000from 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 = {} def read(filepath): with open(filepath, encoding="utf-8") as f: return f.read() 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.", long_description=read('README.rst'), author="Dave Hall", author_email="dave@etianen.com", url="https://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>=3.2", ], python_requires='>=3.7', 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 :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', "Framework :: Django", ] ) django-reversion-5.0.12/tests/000077500000000000000000000000001455625272200162275ustar00rootroot00000000000000django-reversion-5.0.12/tests/manage.py000077500000000000000000000010401455625272200200270ustar00rootroot00000000000000#!/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 as exc: 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?" ) from exc execute_from_command_line(sys.argv) django-reversion-5.0.12/tests/test_app/000077500000000000000000000000001455625272200200465ustar00rootroot00000000000000django-reversion-5.0.12/tests/test_app/__init__.py000066400000000000000000000000001455625272200221450ustar00rootroot00000000000000django-reversion-5.0.12/tests/test_app/admin.py000066400000000000000000000004711455625272200215120ustar00rootroot00000000000000from 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) django-reversion-5.0.12/tests/test_app/migrations/000077500000000000000000000000001455625272200222225ustar00rootroot00000000000000django-reversion-5.0.12/tests/test_app/migrations/0001_initial.py000066400000000000000000000126231455625272200246710ustar00rootroot00000000000000# Generated by Django 3.1 on 2020-08-31 10:05 from django.db import migrations, models import django.db.models.deletion class Migration(migrations.Migration): initial = True dependencies = [ ('reversion', '0001_squashed_0004_auto_20160611_1202'), ('contenttypes', '0002_remove_content_type_name'), ] operations = [ 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='TestModelEscapePK', fields=[ ('name', models.CharField(max_length=191, primary_key=True, serialize=False)), ], ), 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)), ('test_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='test_app.testmodel')), ], ), 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='TestModelWithNaturalKey', 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='TestModelThrough', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(default='v1', max_length=191)), ('test_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='test_app.testmodel')), ('test_model_related', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='test_app.testmodelrelated')), ], ), migrations.CreateModel( name='TestModelNestedInline', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('nested_inline_name', models.CharField(default='v1', max_length=191)), ('test_model_inline', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='test_app.testmodelinline')), ], ), migrations.CreateModel( name='TestModelInlineByNaturalKey', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('test_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='test_app.testmodelwithnaturalkey')), ], ), 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.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'), ), 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='TestModelWithUniqueConstraint', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=191, unique=True)), ], ), ] django-reversion-5.0.12/tests/test_app/migrations/0002_alter_testmodel_related_and_more.py000066400000000000000000000012601455625272200317670ustar00rootroot00000000000000# Generated by Django 5.0.1 on 2024-01-30 19:07 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('test_app', '0001_initial'), ] operations = [ migrations.AlterField( model_name='testmodel', name='related', field=models.ManyToManyField(blank=True, related_name='+', to='test_app.testmodelrelated'), ), migrations.AlterField( model_name='testmodel', name='related_through', field=models.ManyToManyField(blank=True, related_name='+', through='test_app.TestModelThrough', to='test_app.testmodelrelated'), ), ] django-reversion-5.0.12/tests/test_app/migrations/__init__.py000066400000000000000000000000001455625272200243210ustar00rootroot00000000000000django-reversion-5.0.12/tests/test_app/models.py000066400000000000000000000056451455625272200217150ustar00rootroot00000000000000from django.db import models from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.models import ContentType 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 TestModelNestedInline(models.Model): test_model_inline = models.ForeignKey( TestModelInline, on_delete=models.CASCADE, ) nested_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, ) class TestModelWithNaturalKeyManager(models.Manager): def get_by_natural_key(self, name): return self.get(name=name) class TestModelWithNaturalKey(models.Model): name = models.CharField( max_length=191, default="v1", ) objects = TestModelWithNaturalKeyManager() def natural_key(self): return (self.name,) class TestModelInlineByNaturalKey(models.Model): test_model = models.ForeignKey( TestModelWithNaturalKey, on_delete=models.CASCADE, ) class TestModelWithUniqueConstraint(models.Model): name = models.CharField( max_length=191, unique=True, ) django-reversion-5.0.12/tests/test_app/tests/000077500000000000000000000000001455625272200212105ustar00rootroot00000000000000django-reversion-5.0.12/tests/test_app/tests/__init__.py000066400000000000000000000000001455625272200233070ustar00rootroot00000000000000django-reversion-5.0.12/tests/test_app/tests/base.py000066400000000000000000000065201455625272200224770ustar00rootroot00000000000000from datetime import timedelta from importlib import import_module, reload from io import StringIO from django.conf import settings from django.contrib.auth.models import User from django.core.management import call_command from django.urls import clear_url_caches from django.test import TestCase, TransactionTestCase from django.test.utils import override_settings from django.utils import timezone import reversion from reversion.models import Revision, Version from test_app.models import TestModel, TestModelParent # Test helpers. class TestBaseMixin: databases = "__all__" def reloadUrls(self): reload(import_module(settings.ROOT_URLCONF)) clear_url_caches() def setUp(self): super().setUp() for model in list(reversion.get_registered_models()): reversion.unregister(model) def tearDown(self): super().tearDown() for model in list(reversion.get_registered_models()): reversion.unregister(model) def callCommand(self, command, *args, **kwargs): kwargs.setdefault("stdout", StringIO()) kwargs.setdefault("stderr", StringIO()) kwargs.setdefault("verbosity", 2) return call_command(command, *args, **kwargs) def assertSingleRevision(self, objects, user=None, comment="", meta_names=(), date_created=None, using=None, model_db=None): revision = Version.objects.using(using).get_for_object(objects[0], model_db=model_db).get().revision self.assertEqual(revision.user, user) if hasattr(comment, 'pattern'): self.assertRegex(revision.get_comment(), comment) elif comment is not None: # Allow a wildcard comment. self.assertEqual(revision.get_comment(), comment) self.assertAlmostEqual(revision.date_created, date_created or timezone.now(), delta=timedelta(seconds=1)) # Check meta. self.assertEqual(revision.testmeta_set.count(), len(meta_names)) for meta_name in meta_names: self.assertTrue(revision.testmeta_set.filter(name=meta_name).exists()) # Check objects. self.assertEqual(revision.version_set.count(), len(objects)) for obj in objects: self.assertTrue(Version.objects.using(using).get_for_object( obj, model_db=model_db, ).filter( revision=revision, ).exists()) def assertNoRevision(self, using=None): self.assertEqual(Revision.objects.using(using).all().count(), 0) class TestBase(TestBaseMixin, TestCase): pass class TestBaseTransaction(TestBaseMixin, TransactionTestCase): pass class TestModelMixin: def setUp(self): super().setUp() reversion.register(TestModel) class TestModelParentMixin(TestModelMixin): def setUp(self): super().setUp() reversion.register(TestModelParent, follow=("testmodel_ptr",)) @override_settings(PASSWORD_HASHERS=["django.contrib.auth.hashers.MD5PasswordHasher"]) class UserMixin(TestBase): def setUp(self): super().setUp() self.user = User(username="test", is_staff=True, is_superuser=True) self.user.set_password("password") self.user.save() class LoginMixin(UserMixin): def setUp(self): super().setUp() self.client.login(username="test", password="password") django-reversion-5.0.12/tests/test_app/tests/test_admin.py000066400000000000000000000205401455625272200237120ustar00rootroot00000000000000import re from django.contrib import admin from django.contrib.contenttypes.admin import GenericTabularInline from django.shortcuts import resolve_url import reversion from reversion.admin import VersionAdmin from reversion.models import Version from test_app.models import TestModel, TestModelParent, TestModelInline, TestModelGenericInline, TestModelEscapePK from test_app.tests.base import TestBase, LoginMixin class AdminMixin(TestBase): def setUp(self): super().setUp() admin.site.register(TestModelParent, VersionAdmin) self.reloadUrls() def tearDown(self): super().tearDown() admin.site.unregister(TestModelParent) self.reloadUrls() class AdminRegisterTest(AdminMixin, TestBase): def setAutoRegister(self): self.assertTrue(reversion.is_registered(TestModelParent)) def setAutoRegisterFollowsParent(self): self.assertTrue(reversion.is_registered(TestModel)) class AdminAddViewTest(LoginMixin, AdminMixin, TestBase): def testAddView(self): self.client.post(resolve_url("admin:test_app_testmodelparent_add"), { "name": "v1", "parent_name": "parent_v1", }) obj = TestModelParent.objects.get() self.assertSingleRevision( (obj, obj.testmodel_ptr), user=self.user, comment="Added." ) class AdminUpdateViewTest(LoginMixin, AdminMixin, TestBase): def testUpdateView(self): obj = TestModelParent.objects.create() self.client.post(resolve_url("admin:test_app_testmodelparent_change", obj.pk), { "name": "v2", "parent_name": "parent v2", }) self.assertSingleRevision( (obj, obj.testmodel_ptr), user=self.user, # Django 3.0 changed formatting a bit. comment=re.compile(r"Changed [nN]ame and [pP]arent[ _]name\.") ) class AdminChangelistView(LoginMixin, AdminMixin, TestBase): def testChangelistView(self): obj = TestModelParent.objects.create() response = self.client.get(resolve_url("admin:test_app_testmodelparent_changelist")) self.assertContains(response, resolve_url("admin:test_app_testmodelparent_change", obj.pk)) class AdminRevisionViewTest(LoginMixin, AdminMixin, TestBase): def setUp(self): super().setUp() with reversion.create_revision(): self.obj = TestModelParent.objects.create() with reversion.create_revision(): self.obj.name = "v2" self.obj.parent_name = "parent v2" self.obj.save() def testRevisionView(self): response = self.client.get(resolve_url( "admin:test_app_testmodelparent_revision", self.obj.pk, Version.objects.get_for_object(self.obj)[1].pk, )) self.assertContains(response, 'value="v1"') self.assertContains(response, 'value="parent v1"') # Test that the changes were rolled back. self.obj.refresh_from_db() self.assertEqual(self.obj.name, "v2") self.assertEqual(self.obj.parent_name, "parent v2") self.assertIn("revert", response.context) self.assertTrue(response.context["revert"]) def testRevisionViewOldRevision(self): response = self.client.get(resolve_url( "admin:test_app_testmodelparent_revision", self.obj.pk, Version.objects.get_for_object(self.obj)[0].pk, )) self.assertContains(response, 'value="v2"') self.assertContains(response, 'value="parent v2"') def testRevisionViewRevertError(self): Version.objects.get_for_object(self.obj).update(format="boom") response = self.client.get(resolve_url( "admin:test_app_testmodelparent_revision", self.obj.pk, Version.objects.get_for_object(self.obj)[1].pk, )) self.assertEqual( response["Location"].replace("http://testserver", ""), resolve_url("admin:test_app_testmodelparent_changelist"), ) def testRevisionViewRevert(self): self.client.post(resolve_url( "admin:test_app_testmodelparent_revision", self.obj.pk, Version.objects.get_for_object(self.obj)[1].pk, ), { "name": "v1", "parent_name": "parent v1", }) self.obj.refresh_from_db() self.assertEqual(self.obj.name, "v1") self.assertEqual(self.obj.parent_name, "parent v1") class AdminRecoverViewTest(LoginMixin, AdminMixin, TestBase): def setUp(self): super().setUp() with reversion.create_revision(): obj = TestModelParent.objects.create() obj.delete() def testRecoverView(self): response = self.client.get(resolve_url( "admin:test_app_testmodelparent_recover", Version.objects.get_for_model(TestModelParent).get().pk, )) self.assertContains(response, 'value="v1"') self.assertContains(response, 'value="parent v1"') self.assertIn("recover", response.context) self.assertTrue(response.context["recover"]) def testRecoverViewRecover(self): self.client.post(resolve_url( "admin:test_app_testmodelparent_recover", Version.objects.get_for_model(TestModelParent).get().pk, ), { "name": "v1", "parent_name": "parent v1", }) obj = TestModelParent.objects.get() self.assertEqual(obj.name, "v1") self.assertEqual(obj.parent_name, "parent v1") class AdminRecoverlistViewTest(LoginMixin, AdminMixin, TestBase): def testRecoverlistView(self): with reversion.create_revision(): obj = TestModelParent.objects.create() obj.delete() response = self.client.get(resolve_url("admin:test_app_testmodelparent_recoverlist")) self.assertContains(response, resolve_url( "admin:test_app_testmodelparent_recover", Version.objects.get_for_model(TestModelParent).get().pk, )) class AdminHistoryViewTest(LoginMixin, AdminMixin, TestBase): def testHistorylistView(self): with reversion.create_revision(): obj = TestModelParent.objects.create() response = self.client.get(resolve_url("admin:test_app_testmodelparent_history", obj.pk)) self.assertContains(response, resolve_url( "admin:test_app_testmodelparent_revision", obj.pk, Version.objects.get_for_model(TestModelParent).get().pk, )) class AdminQuotingTest(LoginMixin, AdminMixin, TestBase): def setUp(self): super().setUp() admin.site.register(TestModelEscapePK, VersionAdmin) self.reloadUrls() def tearDown(self): super().tearDown() admin.site.unregister(TestModelEscapePK) self.reloadUrls() def testHistoryWithQuotedPrimaryKey(self): pk = 'ABC_123' quoted_pk = admin.utils.quote(pk) # test is invalid if quoting does not change anything assert quoted_pk != pk with reversion.create_revision(): obj = TestModelEscapePK.objects.create(name=pk) revision_url = resolve_url( "admin:test_app_testmodelescapepk_revision", quoted_pk, Version.objects.get_for_object(obj).get().pk, ) history_url = resolve_url( "admin:test_app_testmodelescapepk_history", quoted_pk ) response = self.client.get(history_url) self.assertContains(response, revision_url) response = self.client.get(revision_url) self.assertContains(response, f'value="{pk}"') class TestModelInlineAdmin(admin.TabularInline): model = TestModelInline class TestModelGenericInlineAdmin(GenericTabularInline): model = TestModelGenericInline class TestModelParentAdmin(VersionAdmin): inlines = (TestModelInlineAdmin, TestModelGenericInlineAdmin) class AdminRegisterInlineTest(TestBase): def setUp(self): super().setUp() admin.site.register(TestModelParent, TestModelParentAdmin) self.reloadUrls() def tearDown(self): super().tearDown() admin.site.unregister(TestModelParent) self.reloadUrls() def testAutoRegisterInline(self): self.assertTrue(reversion.is_registered(TestModelInline)) def testAutoRegisterGenericInline(self): self.assertTrue(reversion.is_registered(TestModelGenericInline)) django-reversion-5.0.12/tests/test_app/tests/test_api.py000066400000000000000000000271611455625272200234010ustar00rootroot00000000000000from datetime import timedelta from unittest.mock import MagicMock from django.contrib.auth.models import User from django.db import models from django.db.transaction import get_connection from django.utils import timezone import reversion from test_app.models import TestModel, TestModelRelated, TestModelThrough, TestModelParent, TestMeta from test_app.tests.base import TestBase, TestBaseTransaction, TestModelMixin, UserMixin class SaveTest(TestModelMixin, TestBase): def testModelSave(self): TestModel.objects.create() self.assertNoRevision() class IsRegisteredTest(TestModelMixin, TestBase): def testIsRegistered(self): self.assertTrue(reversion.is_registered(TestModel)) class IsRegisterUnregisteredTest(TestBase): def testIsRegisteredFalse(self): self.assertFalse(reversion.is_registered(TestModel)) class GetRegisteredModelsTest(TestModelMixin, TestBase): def testGetRegisteredModels(self): self.assertEqual(set(reversion.get_registered_models()), {TestModel}) class RegisterTest(TestBase): def testRegister(self): reversion.register(TestModel) self.assertTrue(reversion.is_registered(TestModel)) def testRegisterDecorator(self): @reversion.register() class TestModelDecorater(models.Model): pass self.assertTrue(reversion.is_registered(TestModelDecorater)) def testRegisterAlreadyRegistered(self): reversion.register(TestModel) with self.assertRaises(reversion.RegistrationError): reversion.register(TestModel) def testRegisterM2MSThroughLazy(self): # When register is used as a decorator in models.py, lazy relations haven't had a chance to be resolved, so # will still be a string. @reversion.register() class TestModelLazy(models.Model): related = models.ManyToManyField( TestModelRelated, through="TestModelThroughLazy", ) class TestModelThroughLazy(models.Model): pass class UnregisterTest(TestModelMixin, TestBase): def testUnregister(self): reversion.unregister(TestModel) self.assertFalse(reversion.is_registered(TestModel)) class UnregisterUnregisteredTest(TestBase): def testUnregisterNotRegistered(self): with self.assertRaises(reversion.RegistrationError): reversion.unregister(User) class CreateRevisionTest(TestModelMixin, TestBase): def testCreateRevision(self): with reversion.create_revision(): obj = TestModel.objects.create() self.assertSingleRevision((obj,)) def testCreateRevisionNested(self): with reversion.create_revision(): with reversion.create_revision(): obj = TestModel.objects.create() self.assertSingleRevision((obj,)) def testCreateRevisionEmpty(self): with reversion.create_revision(): pass self.assertNoRevision() def testCreateRevisionException(self): try: with reversion.create_revision(): TestModel.objects.create() raise Exception("Boom!") except Exception: pass self.assertNoRevision() def testCreateRevisionDecorator(self): obj = reversion.create_revision()(TestModel.objects.create)() self.assertSingleRevision((obj,)) def testPreRevisionCommitSignal(self): _callback = MagicMock() reversion.signals.pre_revision_commit.connect(_callback) with reversion.create_revision(): TestModel.objects.create() self.assertEqual(_callback.call_count, 1) def testPostRevisionCommitSignal(self): _callback = MagicMock() reversion.signals.post_revision_commit.connect(_callback) with reversion.create_revision(): TestModel.objects.create() self.assertEqual(_callback.call_count, 1) class CreateRevisionAtomicTest(TestModelMixin, TestBaseTransaction): def testCreateRevisionAtomic(self): self.assertFalse(get_connection().in_atomic_block) with reversion.create_revision(): self.assertTrue(get_connection().in_atomic_block) def testCreateRevisionNonAtomic(self): self.assertFalse(get_connection().in_atomic_block) with reversion.create_revision(atomic=False): self.assertFalse(get_connection().in_atomic_block) def testCreateRevisionInOnCommitHandler(self): from django.db import transaction from reversion.models import Revision self.assertEqual(Revision.objects.all().count(), 0) with reversion.create_revision(atomic=True): model = TestModel.objects.create() def on_commit(): with reversion.create_revision(atomic=True): model.name = 'oncommit' model.save() transaction.on_commit(on_commit) self.assertEqual(Revision.objects.all().count(), 2) class CreateRevisionManageManuallyTest(TestModelMixin, TestBase): def testCreateRevisionManageManually(self): with reversion.create_revision(manage_manually=True): TestModel.objects.create() self.assertNoRevision() def testCreateRevisionManageManuallyNested(self): with reversion.create_revision(): with reversion.create_revision(manage_manually=True): TestModel.objects.create() self.assertNoRevision() class CreateRevisionDbTest(TestModelMixin, TestBase): databases = {"default", "mysql", "postgres"} def testCreateRevisionMultiDb(self): with reversion.create_revision(using="mysql"), reversion.create_revision(using="postgres"): obj = TestModel.objects.create() self.assertNoRevision() self.assertSingleRevision((obj,), using="mysql") self.assertSingleRevision((obj,), using="postgres") class CreateRevisionFollowTest(TestBase): def testCreateRevisionFollow(self): reversion.register(TestModel, follow=("related",)) reversion.register(TestModelRelated) obj_related = TestModelRelated.objects.create() with reversion.create_revision(): obj = TestModel.objects.create() obj.related.add(obj_related) self.assertSingleRevision((obj, obj_related)) def testCreateRevisionFollowThrough(self): reversion.register(TestModel, follow=("related_through",)) reversion.register(TestModelThrough, follow=("test_model", "test_model_related",)) reversion.register(TestModelRelated) obj_related = TestModelRelated.objects.create() with reversion.create_revision(): obj = TestModel.objects.create() obj_through = TestModelThrough.objects.create( test_model=obj, test_model_related=obj_related, ) self.assertSingleRevision((obj, obj_through, obj_related)) def testCreateRevisionFollowInvalid(self): reversion.register(TestModel, follow=("name",)) with reversion.create_revision(): with self.assertRaises(reversion.RegistrationError): TestModel.objects.create() class CreateRevisionIgnoreDuplicatesTest(TestBase): def testCreateRevisionIgnoreDuplicates(self): reversion.register(TestModel, ignore_duplicates=True) with reversion.create_revision(): obj = TestModel.objects.create() with reversion.create_revision(): obj.save() self.assertSingleRevision((obj,)) class CreateRevisionInheritanceTest(TestModelMixin, TestBase): def testCreateRevisionInheritance(self): reversion.register(TestModelParent, follow=("testmodel_ptr",)) with reversion.create_revision(): obj = TestModelParent.objects.create() self.assertSingleRevision((obj, obj.testmodel_ptr)) class SetCommentTest(TestModelMixin, TestBase): def testSetComment(self): with reversion.create_revision(): reversion.set_comment("comment v1") obj = TestModel.objects.create() self.assertSingleRevision((obj,), comment="comment v1") def testSetCommentNoBlock(self): with self.assertRaises(reversion.RevisionManagementError): reversion.set_comment("comment v1") class GetCommentTest(TestBase): def testGetComment(self): with reversion.create_revision(): reversion.set_comment("comment v1") self.assertEqual(reversion.get_comment(), "comment v1") def testGetCommentDefault(self): with reversion.create_revision(): self.assertEqual(reversion.get_comment(), "") def testGetCommentNoBlock(self): with self.assertRaises(reversion.RevisionManagementError): reversion.get_comment() class SetUserTest(UserMixin, TestModelMixin, TestBase): def testSetUser(self): with reversion.create_revision(): reversion.set_user(self.user) obj = TestModel.objects.create() self.assertSingleRevision((obj,), user=self.user) def testSetUserNoBlock(self): with self.assertRaises(reversion.RevisionManagementError): reversion.set_user(self.user) class GetUserTest(UserMixin, TestBase): def testGetUser(self): with reversion.create_revision(): reversion.set_user(self.user) self.assertEqual(reversion.get_user(), self.user) def testGetUserDefault(self): with reversion.create_revision(): self.assertEqual(reversion.get_user(), None) def testGetUserNoBlock(self): with self.assertRaises(reversion.RevisionManagementError): reversion.get_user() class SetDateCreatedTest(TestModelMixin, TestBase): def testSetDateCreated(self): date_created = timezone.now() - timedelta(days=20) with reversion.create_revision(): reversion.set_date_created(date_created) obj = TestModel.objects.create() self.assertSingleRevision((obj,), date_created=date_created) def testDateCreatedNoBlock(self): with self.assertRaises(reversion.RevisionManagementError): reversion.set_date_created(timezone.now()) class GetDateCreatedTest(TestBase): def testGetDateCreated(self): date_created = timezone.now() - timedelta(days=20) with reversion.create_revision(): reversion.set_date_created(date_created) self.assertEqual(reversion.get_date_created(), date_created) def testGetDateCreatedDefault(self): with reversion.create_revision(): self.assertAlmostEqual(reversion.get_date_created(), timezone.now(), delta=timedelta(seconds=1)) def testGetDateCreatedNoBlock(self): with self.assertRaises(reversion.RevisionManagementError): reversion.get_date_created() class AddMetaTest(TestModelMixin, TestBase): databases = {"default", "mysql", "postgres"} def testAddMeta(self): with reversion.create_revision(): reversion.add_meta(TestMeta, name="meta v1") obj = TestModel.objects.create() self.assertSingleRevision((obj,), meta_names=("meta v1",)) def testAddMetaNoBlock(self): with self.assertRaises(reversion.RevisionManagementError): reversion.add_meta(TestMeta, name="meta v1") def testAddMetaMultDb(self): with reversion.create_revision(using="mysql"), reversion.create_revision(using="postgres"): obj = TestModel.objects.create() reversion.add_meta(TestMeta, name="meta v1") self.assertNoRevision() self.assertSingleRevision((obj,), meta_names=("meta v1",), using="mysql") self.assertSingleRevision((obj,), meta_names=("meta v1",), using="postgres") django-reversion-5.0.12/tests/test_app/tests/test_commands.py000066400000000000000000000176041455625272200244320ustar00rootroot00000000000000import json from datetime import timedelta from django.core.management import CommandError from django.utils import timezone import reversion from test_app.models import TestModel from test_app.tests.base import TestBase, TestModelMixin class CreateInitialRevisionsTest(TestModelMixin, TestBase): def testCreateInitialRevisions(self): obj = TestModel.objects.create() self.callCommand("createinitialrevisions") self.assertSingleRevision((obj,), comment="Initial version.") def testCreateInitialRevisionsAlreadyCreated(self): obj = TestModel.objects.create() self.callCommand("createinitialrevisions") self.callCommand("createinitialrevisions") self.assertSingleRevision((obj,), comment="Initial version.") class CreateInitialRevisionsAppLabelTest(TestModelMixin, TestBase): def testCreateInitialRevisionsAppLabel(self): obj = TestModel.objects.create() self.callCommand("createinitialrevisions", "test_app") self.assertSingleRevision((obj,), comment="Initial version.") def testCreateInitialRevisionsAppLabelMissing(self): with self.assertRaises(CommandError): self.callCommand("createinitialrevisions", "boom") def testCreateInitialRevisionsModel(self): obj = TestModel.objects.create() self.callCommand("createinitialrevisions", "test_app.TestModel") self.assertSingleRevision((obj,), comment="Initial version.") def testCreateInitialRevisionsModelMissing(self): with self.assertRaises(CommandError): self.callCommand("createinitialrevisions", "test_app.boom") def testCreateInitialRevisionsModelMissingApp(self): with self.assertRaises(CommandError): self.callCommand("createinitialrevisions", "boom.boom") def testCreateInitialRevisionsModelNotRegistered(self): TestModel.objects.create() self.callCommand("createinitialrevisions", "auth.User") self.assertNoRevision() class CreateInitialRevisionsDbTest(TestModelMixin, TestBase): databases = {"default", "mysql", "postgres"} def testCreateInitialRevisionsDb(self): obj = TestModel.objects.create() self.callCommand("createinitialrevisions", using="postgres") self.assertNoRevision() self.assertSingleRevision((obj,), comment="Initial version.", using="postgres") def testCreateInitialRevisionsDbMySql(self): obj = TestModel.objects.create() self.callCommand("createinitialrevisions", using="mysql") self.assertNoRevision() self.assertSingleRevision((obj,), comment="Initial version.", using="mysql") class CreateInitialRevisionsModelDbTest(TestModelMixin, TestBase): databases = {"default", "postgres"} def testCreateInitialRevisionsModelDb(self): obj = TestModel.objects.db_manager("postgres").create() self.callCommand("createinitialrevisions", model_db="postgres") self.assertSingleRevision((obj,), comment="Initial version.", model_db="postgres") class CreateInitialRevisionsCommentTest(TestModelMixin, TestBase): def testCreateInitialRevisionsComment(self): obj = TestModel.objects.create() self.callCommand("createinitialrevisions", comment="comment v1") self.assertSingleRevision((obj,), comment="comment v1") class CreateInitialRevisionsMetaTest(TestModelMixin, TestBase): def testCreateInitialRevisionsComment(self): obj = TestModel.objects.create() meta_name = "meta name" meta = json.dumps({"test_app.TestMeta": {"name": meta_name}}) self.callCommand("createinitialrevisions", "--meta", meta) self.assertSingleRevision((obj,), meta_names=(meta_name, ), comment="Initial version.") class DeleteRevisionsTest(TestModelMixin, TestBase): def testDeleteRevisions(self): with reversion.create_revision(): TestModel.objects.create() self.callCommand("deleterevisions") self.assertNoRevision() class DeleteRevisionsAppLabelTest(TestModelMixin, TestBase): def testDeleteRevisionsAppLabel(self): with reversion.create_revision(): TestModel.objects.create() self.callCommand("deleterevisions", "test_app") self.assertNoRevision() def testDeleteRevisionsAppLabelMissing(self): with self.assertRaises(CommandError): self.callCommand("deleterevisions", "boom") def testDeleteRevisionsModel(self): with reversion.create_revision(): TestModel.objects.create() self.callCommand("deleterevisions", "test_app.TestModel") self.assertNoRevision() def testDeleteRevisionsModelMissing(self): with self.assertRaises(CommandError): self.callCommand("deleterevisions", "test_app.boom") def testDeleteRevisionsModelMissingApp(self): with self.assertRaises(CommandError): self.callCommand("deleterevisions", "boom.boom") def testDeleteRevisionsModelNotRegistered(self): with reversion.create_revision(): obj = TestModel.objects.create() self.callCommand("deleterevisions", "auth.User") self.assertSingleRevision((obj,)) class DeleteRevisionsDbTest(TestModelMixin, TestBase): databases = {"default", "mysql", "postgres"} def testDeleteRevisionsDb(self): with reversion.create_revision(using="postgres"): TestModel.objects.create() self.callCommand("deleterevisions", using="postgres") self.assertNoRevision(using="postgres") def testDeleteRevisionsDbMySql(self): with reversion.create_revision(using="mysql"): TestModel.objects.create() self.callCommand("deleterevisions", using="mysql") self.assertNoRevision(using="mysql") def testDeleteRevisionsDbNoMatch(self): with reversion.create_revision(): obj = TestModel.objects.create() self.callCommand("deleterevisions", using="postgres") self.assertSingleRevision((obj,)) class DeleteRevisionsModelDbTest(TestModelMixin, TestBase): databases = {"default", "postgres"} def testDeleteRevisionsModelDb(self): with reversion.create_revision(): TestModel.objects.db_manager("postgres").create() self.callCommand("deleterevisions", model_db="postgres") self.assertNoRevision(using="postgres") class DeleteRevisionsDaysTest(TestModelMixin, TestBase): def testDeleteRevisionsDays(self): date_created = timezone.now() - timedelta(days=20) with reversion.create_revision(): TestModel.objects.create() reversion.set_date_created(date_created) self.callCommand("deleterevisions", days=19) self.assertNoRevision() def testDeleteRevisionsDaysNoMatch(self): date_created = timezone.now() - timedelta(days=20) with reversion.create_revision(): obj = TestModel.objects.create() reversion.set_date_created(date_created) self.callCommand("deleterevisions", days=21) self.assertSingleRevision((obj,), date_created=date_created) class DeleteRevisionsKeepTest(TestModelMixin, TestBase): def testDeleteRevisionsKeep(self): with reversion.create_revision(): obj_1 = TestModel.objects.create() reversion.set_comment("obj_1 v1") with reversion.create_revision(): obj_1.save() reversion.set_comment("obj_1 v2") with reversion.create_revision(): obj_2 = TestModel.objects.create() reversion.set_comment("obj_2 v1") with reversion.create_revision(): obj_2.save() reversion.set_comment("obj_2 v2") with reversion.create_revision(): obj_3 = TestModel.objects.create() self.callCommand("deleterevisions", keep=1) self.assertSingleRevision((obj_1,), comment="obj_1 v2") self.assertSingleRevision((obj_2,), comment="obj_2 v2") self.assertSingleRevision((obj_3,)) django-reversion-5.0.12/tests/test_app/tests/test_middleware.py000066400000000000000000000022311455625272200247340ustar00rootroot00000000000000from django.conf import settings from django.test.utils import override_settings from test_app.models import TestModel from test_app.tests.base import TestBase, TestModelMixin, LoginMixin use_middleware = override_settings( MIDDLEWARE=settings.MIDDLEWARE + ["reversion.middleware.RevisionMiddleware"], ) @use_middleware class RevisionMiddlewareTest(TestModelMixin, TestBase): def testCreateRevision(self): response = self.client.post("/test-app/save-obj/") obj = TestModel.objects.get(pk=response.content) self.assertSingleRevision((obj,)) def testCreateRevisionError(self): with self.assertRaises(Exception): self.client.post("/test-app/save-obj-error/") self.assertNoRevision() def testCreateRevisionGet(self): self.client.get("/test-app/create-revision/") self.assertNoRevision() @use_middleware class RevisionMiddlewareUserTest(TestModelMixin, LoginMixin, TestBase): def testCreateRevisionUser(self): response = self.client.post("/test-app/save-obj/") obj = TestModel.objects.get(pk=response.content) self.assertSingleRevision((obj,), user=self.user) django-reversion-5.0.12/tests/test_app/tests/test_models.py000066400000000000000000000425151455625272200241130ustar00rootroot00000000000000import reversion from reversion.models import Version from test_app.models import ( TestModel, TestModelRelated, TestModelParent, TestModelInline, TestModelNestedInline, TestModelInlineByNaturalKey, TestModelWithNaturalKey, TestModelWithUniqueConstraint, ) from test_app.tests.base import TestBase, TestModelMixin, TestModelParentMixin import json class GetForModelTest(TestModelMixin, TestBase): def testGetForModel(self): with reversion.create_revision(): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_model(obj.__class__).count(), 1) class GetForModelDbTest(TestModelMixin, TestBase): databases = {"default", "mysql", "postgres"} def testGetForModelDb(self): with reversion.create_revision(using="postgres"): obj = TestModel.objects.create() self.assertEqual(Version.objects.using("postgres").get_for_model(obj.__class__).count(), 1) def testGetForModelDbMySql(self): with reversion.create_revision(using="mysql"): obj = TestModel.objects.create() self.assertEqual(Version.objects.using("mysql").get_for_model(obj.__class__).count(), 1) class GetForObjectTest(TestModelMixin, TestBase): def testGetForObject(self): with reversion.create_revision(): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object(obj).count(), 1) def testGetForObjectEmpty(self): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object(obj).count(), 0) def testGetForObjectOrdering(self): with reversion.create_revision(): obj = TestModel.objects.create() with reversion.create_revision(): obj.name = "v2" obj.save() self.assertEqual(Version.objects.get_for_object(obj)[0].field_dict["name"], "v2") self.assertEqual(Version.objects.get_for_object(obj)[1].field_dict["name"], "v1") def testGetForObjectFiltering(self): with reversion.create_revision(): obj_1 = TestModel.objects.create() with reversion.create_revision(): obj_2 = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object(obj_1).get().object, obj_1) self.assertEqual(Version.objects.get_for_object(obj_2).get().object, obj_2) class GetForObjectDbTest(TestModelMixin, TestBase): databases = {"default", "mysql", "postgres"} def testGetForObjectDb(self): with reversion.create_revision(using="postgres"): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object(obj).count(), 0) self.assertEqual(Version.objects.using("postgres").get_for_object(obj).count(), 1) def testGetForObjectDbMySql(self): with reversion.create_revision(using="mysql"): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object(obj).count(), 0) self.assertEqual(Version.objects.using("mysql").get_for_object(obj).count(), 1) class GetForObjectModelDbTest(TestModelMixin, TestBase): databases = {"default", "postgres"} def testGetForObjectModelDb(self): with reversion.create_revision(): obj = TestModel.objects.db_manager("postgres").create() self.assertEqual(Version.objects.get_for_object(obj).count(), 0) self.assertEqual(Version.objects.get_for_object(obj, model_db="postgres").count(), 1) class GetForObjectUniqueTest(TestModelMixin, TestBase): def testGetForObjectUnique(self): with reversion.create_revision(): obj = TestModel.objects.create() with reversion.create_revision(): obj.save() self.assertEqual(len(list(Version.objects.get_for_object(obj).get_unique())), 1) def testGetForObjectUniqueMiss(self): with reversion.create_revision(): obj = TestModel.objects.create() with reversion.create_revision(): obj.name = "v2" obj.save() self.assertEqual(len(list(Version.objects.get_for_object(obj).get_unique())), 2) class GetForObjectReferenceTest(TestModelMixin, TestBase): def testGetForObjectReference(self): with reversion.create_revision(): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj.pk).count(), 1) def testGetForObjectReferenceEmpty(self): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj.pk).count(), 0) def testGetForObjectReferenceOrdering(self): with reversion.create_revision(): obj = TestModel.objects.create() with reversion.create_revision(): obj.name = "v2" obj.save() self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj.pk)[0].field_dict["name"], "v2") self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj.pk)[1].field_dict["name"], "v1") def testGetForObjectReferenceFiltering(self): with reversion.create_revision(): obj_1 = TestModel.objects.create() with reversion.create_revision(): obj_2 = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj_1.pk).get().object, obj_1) self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj_2.pk).get().object, obj_2) class GetForObjectReferenceDbTest(TestModelMixin, TestBase): databases = {"default", "postgres"} def testGetForObjectReferenceModelDb(self): with reversion.create_revision(using="postgres"): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj.pk).count(), 0) self.assertEqual(Version.objects.using("postgres").get_for_object_reference(TestModel, obj.pk).count(), 1) class GetForObjectReferenceModelDbTest(TestModelMixin, TestBase): databases = {"default", "mysql", "postgres"} def testGetForObjectReferenceModelDb(self): with reversion.create_revision(): obj = TestModel.objects.db_manager("postgres").create() self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj.pk).count(), 0) self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj.pk, model_db="postgres").count(), 1) def testGetForObjectReferenceModelDbMySql(self): with reversion.create_revision(): obj = TestModel.objects.db_manager("mysql").create() self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj.pk).count(), 0) self.assertEqual(Version.objects.get_for_object_reference(TestModel, obj.pk, model_db="mysql").count(), 1) class GetDeletedTest(TestModelMixin, TestBase): databases = {"default", "mysql", "postgres"} def testGetDeleted(self): with reversion.create_revision(): obj = TestModel.objects.create() with reversion.create_revision(): obj.save() obj.delete() self.assertEqual(Version.objects.get_deleted(TestModel).count(), 1) def testGetDeletedEmpty(self): with reversion.create_revision(): TestModel.objects.create() self.assertEqual(Version.objects.get_deleted(TestModel).count(), 0) def testGetDeletedOrdering(self): with reversion.create_revision(): obj_1 = TestModel.objects.create() with reversion.create_revision(): obj_2 = TestModel.objects.create() pk_1 = obj_1.pk obj_1.delete() pk_2 = obj_2.pk obj_2.delete() self.assertEqual(Version.objects.get_deleted(TestModel)[0].object_id, str(pk_2)) self.assertEqual(Version.objects.get_deleted(TestModel)[1].object_id, str(pk_1)) def testGetDeletedPostgres(self): with reversion.create_revision(using="postgres"): obj = TestModel.objects.using("postgres").create() with reversion.create_revision(using="postgres"): obj.save() obj.delete() self.assertEqual(Version.objects.using("postgres").get_deleted(TestModel, model_db="postgres").count(), 1) def testGetDeletedMySQL(self): with reversion.create_revision(using="mysql"): obj = TestModel.objects.using("mysql").create() with reversion.create_revision(using="mysql"): obj.save() obj.delete() self.assertEqual(Version.objects.using("mysql").get_deleted(TestModel, model_db="mysql").count(), 1) class GetDeletedDbTest(TestModelMixin, TestBase): databases = {"default", "mysql", "postgres"} def testGetDeletedDb(self): with reversion.create_revision(using="postgres"): obj = TestModel.objects.create() obj.delete() self.assertEqual(Version.objects.get_deleted(TestModel).count(), 0) self.assertEqual(Version.objects.using("postgres").get_deleted(TestModel).count(), 1) def testGetDeletedDbMySql(self): with reversion.create_revision(using="mysql"): obj = TestModel.objects.create() obj.delete() self.assertEqual(Version.objects.get_deleted(TestModel).count(), 0) self.assertEqual(Version.objects.using("mysql").get_deleted(TestModel).count(), 1) class GetDeletedModelDbTest(TestModelMixin, TestBase): databases = {"default", "postgres"} def testGetDeletedModelDb(self): with reversion.create_revision(): obj = TestModel.objects.db_manager("postgres").create() obj.delete() self.assertEqual(Version.objects.get_deleted(TestModel).count(), 0) self.assertEqual(Version.objects.get_deleted(TestModel, model_db="postgres").count(), 1) class FieldDictTest(TestModelMixin, TestBase): def testFieldDict(self): with reversion.create_revision(): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object(obj).get().field_dict, { "id": obj.pk, "name": "v1", "related": [], }) def testFieldDictM2M(self): obj_related = TestModelRelated.objects.create() with reversion.create_revision(): obj = TestModel.objects.create() obj.related.add(obj_related) self.assertEqual(Version.objects.get_for_object(obj).get().field_dict, { "id": obj.pk, "name": "v1", "related": [obj_related.pk], }) class FieldDictFieldsTest(TestBase): def testFieldDictFieldFields(self): reversion.register(TestModel, fields=("name",)) with reversion.create_revision(): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object(obj).get().field_dict, { "name": "v1", }) class FieldDictExcludeTest(TestBase): def testFieldDictFieldExclude(self): reversion.register(TestModel, exclude=("name",)) with reversion.create_revision(): obj = TestModel.objects.create() self.assertEqual(Version.objects.get_for_object(obj).get().field_dict, { "id": obj.pk, "related": [], }) class FieldDictInheritanceTest(TestModelParentMixin, TestBase): def testFieldDictInheritance(self): with reversion.create_revision(): obj = TestModelParent.objects.create() self.assertEqual(Version.objects.get_for_object(obj).get().field_dict, { "id": obj.pk, "name": "v1", "related": [], "parent_name": "parent v1", "testmodel_ptr_id": obj.pk, }) def testFieldDictInheritanceUpdate(self): obj = TestModelParent.objects.create() with reversion.create_revision(): obj.name = "v2" obj.parent_name = "parent v2" obj.save() self.assertEqual(Version.objects.get_for_object(obj).get().field_dict, { "id": obj.pk, "name": "v2", "parent_name": "parent v2", "related": [], "testmodel_ptr_id": obj.pk, }) class M2MTest(TestModelMixin, TestBase): def testM2MSave(self): v1 = TestModelRelated.objects.create(name="v1") v2 = TestModelRelated.objects.create(name="v2") with reversion.create_revision(): obj = TestModel.objects.create() obj.related.add(v1) obj.related.add(v2) version = Version.objects.get_for_object(obj).first() self.assertEqual(set(version.field_dict["related"]), {v1.pk, v2.pk}) class RevertTest(TestModelMixin, TestBase): def testRevert(self): with reversion.create_revision(): obj = TestModel.objects.create() with reversion.create_revision(): obj.name = "v2" obj.save() Version.objects.get_for_object(obj)[1].revert() obj.refresh_from_db() self.assertEqual(obj.name, "v1") def testRevertBadSerializedData(self): with reversion.create_revision(): obj = TestModel.objects.create() Version.objects.get_for_object(obj).update(serialized_data="boom") with self.assertRaises(reversion.RevertError): Version.objects.get_for_object(obj).get().revert() def testRevertBadFormat(self): with reversion.create_revision(): obj = TestModel.objects.create() Version.objects.get_for_object(obj).update(format="boom") with self.assertRaises(reversion.RevertError): Version.objects.get_for_object(obj).get().revert() class RevisionRevertTest(TestModelMixin, TestBase): def testRevert(self): with reversion.create_revision(): obj_1 = TestModel.objects.create( name="obj_1 v1" ) obj_2 = TestModel.objects.create( name="obj_2 v1" ) with reversion.create_revision(): obj_1.name = "obj_1 v2" obj_1.save() obj_2.name = "obj_2 v2" obj_2.save() Version.objects.get_for_object(obj_1)[1].revision.revert() obj_1.refresh_from_db() self.assertEqual(obj_1.name, "obj_1 v1") obj_2.refresh_from_db() self.assertEqual(obj_2.name, "obj_2 v1") class RevisionRevertDeleteTest(TestBase): def testRevertDelete(self): reversion.register(TestModel, follow=("related",)) reversion.register(TestModelRelated) with reversion.create_revision(): obj = TestModel.objects.create() obj_related = TestModelRelated.objects.create() with reversion.create_revision(): obj.related.add(obj_related) obj.name = "v2" obj.save() Version.objects.get_for_object(obj)[1].revision.revert(delete=True) obj.refresh_from_db() self.assertEqual(obj.name, "v1") self.assertFalse(TestModelRelated.objects.filter(pk=obj_related.pk).exists()) def testRevertDeleteNestedInline(self): reversion.register(TestModel, follow=("testmodelinline_set",)) reversion.register( TestModelInline, follow=("testmodelnestedinline_set",)) reversion.register(TestModelNestedInline) with reversion.create_revision(): parent = TestModel.objects.create() child_a = TestModelInline.objects.create( test_model=parent) grandchild_a = TestModelNestedInline.objects.create( test_model_inline=child_a) with reversion.create_revision(): child_b = TestModelInline.objects.create( test_model=parent) grandchild_b = TestModelNestedInline.objects.create( test_model_inline=child_b) reversion.add_to_revision(parent) Version.objects.get_for_object(parent)[1].revision.revert(delete=True) parent.refresh_from_db() self.assertRaises( TestModelInline.DoesNotExist, lambda: child_b.refresh_from_db() ) self.assertRaises( TestModelNestedInline.DoesNotExist, lambda: grandchild_b.refresh_from_db() ) self.assertEqual( list(parent.testmodelinline_set.all()), [child_a] ) self.assertEqual( list(child_a.testmodelnestedinline_set.all()), [grandchild_a] ) class NaturalKeyTest(TestBase): def setUp(self): reversion.register(TestModelInlineByNaturalKey, use_natural_foreign_keys=True) reversion.register(TestModelWithNaturalKey) def testNaturalKeyInline(self): with reversion.create_revision(): inline = TestModelWithNaturalKey.objects.create() obj = TestModelInlineByNaturalKey.objects.create(test_model=inline) self.assertEqual(json.loads(Version.objects.get_for_object(obj).get().serialized_data), [{ 'fields': {'test_model': ['v1']}, 'model': 'test_app.testmodelinlinebynaturalkey', 'pk': 1 }]) self.assertEqual(Version.objects.get_for_object(obj).get().field_dict, { 'test_model_id': 1, 'id': 1, }) class TransactionRollbackTest(TestBase): def setUp(self): reversion.register(TestModelWithUniqueConstraint) def testTransactionInRollbackState(self): with reversion.create_revision(): try: TestModelWithUniqueConstraint.objects.create(name='A') TestModelWithUniqueConstraint.objects.create(name='A') except Exception: pass django-reversion-5.0.12/tests/test_app/tests/test_views.py000066400000000000000000000030751455625272200237630ustar00rootroot00000000000000from test_app.models import TestModel from test_app.tests.base import TestBase, TestModelMixin, LoginMixin class CreateRevisionTest(TestModelMixin, TestBase): def testCreateRevision(self): response = self.client.post("/test-app/create-revision/") obj = TestModel.objects.get(pk=response.content) self.assertSingleRevision((obj,)) def testCreateRevisionGet(self): self.client.get("/test-app/create-revision/") self.assertNoRevision() class CreateRevisionUserTest(LoginMixin, TestModelMixin, TestBase): def testCreateRevisionUser(self): response = self.client.post("/test-app/create-revision/") obj = TestModel.objects.get(pk=response.content) self.assertSingleRevision((obj,), user=self.user) class RevisionMixinTest(TestModelMixin, TestBase): def testRevisionMixin(self): response = self.client.post("/test-app/revision-mixin/") obj = TestModel.objects.get(pk=response.content) self.assertSingleRevision((obj,)) def testRevisionMixinGet(self): self.client.get("/test-app/revision-mixin/") self.assertNoRevision() def testRevisionMixinCustomPredicate(self): self.client.post("/test-app/revision-mixin/", HTTP_X_NOREVISION="true") self.assertNoRevision() class RevisionMixinUserTest(LoginMixin, TestModelMixin, TestBase): def testCreateRevisionUser(self): response = self.client.post("/test-app/revision-mixin/") obj = TestModel.objects.get(pk=response.content) self.assertSingleRevision((obj,), user=self.user) django-reversion-5.0.12/tests/test_app/urls.py000066400000000000000000000004521455625272200214060ustar00rootroot00000000000000from django.urls import path from test_app import views urlpatterns = [ path("save-obj/", views.save_obj_view), path("save-obj-error/", views.save_obj_error_view), path("create-revision/", views.create_revision_view), path("revision-mixin/", views.RevisionMixinView.as_view()), ] django-reversion-5.0.12/tests/test_app/views.py000066400000000000000000000015251455625272200215600ustar00rootroot00000000000000from django.db import transaction from django.http import HttpResponse from django.views.generic.base import View from reversion.views import create_revision, RevisionMixin from test_app.models import TestModel def save_obj_view(request): return HttpResponse(TestModel.objects.create().id) def save_obj_error_view(request): with transaction.atomic(): TestModel.objects.create() raise Exception("Boom!") @create_revision() def create_revision_view(request): return save_obj_view(request) class RevisionMixinView(RevisionMixin, View): def revision_request_creates_revision(self, request): silent = request.headers.get('X-Norevision', "false") == "true" return super().revision_request_creates_revision(request) and not silent def dispatch(self, request): return save_obj_view(request) django-reversion-5.0.12/tests/test_project/000077500000000000000000000000001455625272200207345ustar00rootroot00000000000000django-reversion-5.0.12/tests/test_project/__init__.py000066400000000000000000000000001455625272200230330ustar00rootroot00000000000000django-reversion-5.0.12/tests/test_project/settings.py000066400000000000000000000076011455625272200231520ustar00rootroot00000000000000""" Django settings for test_project project. Generated by "django-admin startproject" using Django 1.10a1. For more information on this file, see https://docs.djangoproject.com/en/dev/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/dev/ref/settings/ """ import os import getpass # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = "lzu78x^s$rit0p*vdt)$1e&hh*)4y=xv))=@zsx(am7t=7406a" # SECURITY WARNING: don"t run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "reversion", "test_app", ] MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] ROOT_URLCONF = "test_project.urls" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] WSGI_APPLICATION = "test_project.wsgi.application" # Database # https://docs.djangoproject.com/en/dev/ref/settings/#databases DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": os.path.join(BASE_DIR, "db.sqlite3"), }, "postgres": { "ENGINE": "django.db.backends.postgresql_psycopg2", "HOST": os.environ.get("DJANGO_DATABASE_HOST_POSTGRES", ""), "NAME": os.environ.get("DJANGO_DATABASE_NAME_POSTGRES", "test_project"), "USER": os.environ.get("DJANGO_DATABASE_USER_POSTGRES", getpass.getuser()), "PASSWORD": os.environ.get("DJANGO_DATABASE_PASSWORD_POSTGRES", ""), }, "mysql": { "ENGINE": "django.db.backends.mysql", "HOST": os.environ.get("DJANGO_DATABASE_HOST_MYSQL", ""), "NAME": os.environ.get("DJANGO_DATABASE_NAME_MYSQL", "test_project"), "USER": os.environ.get("DJANGO_DATABASE_USER_MYSQL", getpass.getuser()), "PASSWORD": os.environ.get("DJANGO_DATABASE_PASSWORD_MYSQL", ""), }, } DEFAULT_AUTO_FIELD = "django.db.models.AutoField" # Password validation # https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] # Internationalization # https://docs.djangoproject.com/en/dev/topics/i18n/ LANGUAGE_CODE = "en-us" TIME_ZONE = "UTC" USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/dev/howto/static-files/ STATIC_URL = "/static/" django-reversion-5.0.12/tests/test_project/urls.py000066400000000000000000000003371455625272200222760ustar00rootroot00000000000000from django.urls import include from django.urls import path from django.contrib import admin admin.autodiscover() urlpatterns = [ path("admin/", admin.site.urls), path("test-app/", include("test_app.urls")), ] django-reversion-5.0.12/tests/test_project/wsgi.py000066400000000000000000000006211455625272200222560ustar00rootroot00000000000000""" WSGI config for test_project project. It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/ """ import os from django.core.wsgi import get_wsgi_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") application = get_wsgi_application()