├── .coveragerc ├── .github └── workflows │ ├── python-package.yml │ └── python-publish.yml ├── .gitignore ├── .readthedocs.yaml ├── CHANGELOG.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── docs ├── _include │ ├── admin.rst │ ├── create-revision-args.rst │ ├── create-revision-atomic.rst │ ├── create-revision-manage-manually.rst │ ├── create-revision-using.rst │ ├── model-db-arg.rst │ ├── post-register.rst │ ├── signal-args.rst │ ├── throws-registration-error.rst │ ├── throws-revert-error.rst │ └── throws-revision-error.rst ├── admin.rst ├── api.rst ├── changelog.rst ├── commands.rst ├── common-problems.rst ├── conf.py ├── errors.rst ├── index.rst ├── middleware.rst ├── signals.rst └── views.rst ├── reversion ├── __init__.py ├── admin.py ├── apps.py ├── errors.py ├── locale │ ├── ar │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── cs │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── da │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── es_AR │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── he │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── it │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── nb │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── nl │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── pl │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── pt_BR │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── sk │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── sl_SI │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── sv │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── uk │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ └── zh_Hans │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── createinitialrevisions.py │ │ └── deleterevisions.py ├── middleware.py ├── migrations │ ├── 0001_squashed_0004_auto_20160611_1202.py │ ├── 0002_add_index_on_version_for_content_type_and_db.py │ └── __init__.py ├── models.py ├── revisions.py ├── signals.py ├── templates │ └── reversion │ │ ├── change_list.html │ │ ├── object_history.html │ │ ├── recover_form.html │ │ ├── recover_list.html │ │ └── revision_form.html └── views.py ├── setup.cfg ├── setup.py └── tests ├── manage.py ├── test_app ├── __init__.py ├── admin.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_alter_testmodel_related_and_more.py │ └── __init__.py ├── models.py ├── tests │ ├── __init__.py │ ├── base.py │ ├── test_admin.py │ ├── test_api.py │ ├── test_commands.py │ ├── test_middleware.py │ ├── test_models.py │ └── test_views.py ├── urls.py └── views.py └── test_project ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = 3 | reversion 4 | test_app 5 | test_project 6 | 7 | [report] 8 | exclude_lines = 9 | # Have to re-enable the standard pragma 10 | pragma: no cover 11 | 12 | # Don't complain if tests don't hit defensive assertion code: 13 | raise AssertionError 14 | raise NotImplementedError 15 | assert False 16 | 17 | # Don't complain if tests don't hit model __str__ methods. 18 | def __str__ 19 | 20 | show_missing = True 21 | skip_covered = True 22 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | name: Python package 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | services: 9 | postgres: 10 | image: postgres 11 | env: 12 | POSTGRES_PASSWORD: postgres 13 | options: >- 14 | --health-cmd pg_isready 15 | --health-interval 10s 16 | --health-timeout 5s 17 | --health-retries 5 18 | ports: 19 | - 5432:5432 20 | mysql: 21 | image: mysql 22 | env: 23 | MYSQL_ROOT_PASSWORD: root 24 | MYSQL_DATABASE: root 25 | options: >- 26 | --health-cmd "mysqladmin -uroot -proot ping" 27 | --health-interval 10s 28 | --health-timeout 5s 29 | --health-retries 5 30 | ports: 31 | - 3306:3306 32 | env: 33 | PYTHONDEVMODE: 1 34 | DJANGO_DATABASE_HOST_POSTGRES: localhost 35 | DJANGO_DATABASE_USER_POSTGRES: postgres 36 | DJANGO_DATABASE_NAME_POSTGRES: postgres 37 | DJANGO_DATABASE_PASSWORD_POSTGRES: postgres 38 | DJANGO_DATABASE_HOST_MYSQL: 127.0.0.1 39 | DJANGO_DATABASE_USER_MYSQL: root 40 | DJANGO_DATABASE_NAME_MYSQL: root 41 | DJANGO_DATABASE_PASSWORD_MYSQL: root 42 | strategy: 43 | matrix: 44 | python-version: [3.8, 3.9, '3.10', '3.11', '3.12'] 45 | django-version: 46 | - '>=5.0,<6.0' 47 | - '>=4.2,<5.0' 48 | exclude: 49 | - python-version: 3.9 50 | django-version: '>=5.0,<6.0' 51 | - python-version: 3.8 52 | django-version: '>=5.0,<6.0' 53 | steps: 54 | - uses: actions/checkout@v2 55 | - name: Set up Python ${{ matrix.python-version }} 56 | uses: actions/setup-python@v2 57 | with: 58 | python-version: ${{ matrix.python-version }} 59 | - name: Install dependencies (Django ${{ matrix.django-version }}) 60 | run: | 61 | python -m pip install --upgrade pip 62 | python -m pip install --pre django'${{ matrix.django-version }}' 63 | python -m pip install flake8 coverage sphinx sphinx_rtd_theme psycopg2 mysqlclient -e . 64 | - name: Lint with flake8 65 | run: | 66 | flake8 67 | - name: Check no missing migrations 68 | run: | 69 | tests/manage.py makemigrations --check 70 | - name: Test with unittest 71 | run: | 72 | coverage run tests/manage.py test tests 73 | coverage report 74 | - name: Build docs 75 | run: | 76 | (cd docs && sphinx-build -n -W . _build) 77 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Set up Python 13 | uses: actions/setup-python@v2 14 | with: 15 | python-version: '3.x' 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install setuptools wheel twine 20 | - name: Build and publish 21 | env: 22 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 23 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 24 | run: | 25 | python setup.py sdist bdist_wheel 26 | twine upload dist/* 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.db 3 | .project 4 | .pydevproject 5 | .settings 6 | .venv 7 | *.pyc 8 | *.pyo 9 | dist 10 | build 11 | MANIFEST 12 | *.egg-info/ 13 | docs/_build 14 | .coverage 15 | *.sqlite3 16 | .idea/ 17 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | build: 3 | os: ubuntu-20.04 4 | tools: 5 | python: "3.9" 6 | sphinx: 7 | configuration: docs/conf.py 8 | python: 9 | install: 10 | - method: pip 11 | path: . 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, David Hall. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of David Hall nor the names of its contributors may be 15 | used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include reversion/templates/reversion/*.html 2 | include reversion/locale/*/LC_MESSAGES/django.* 3 | include LICENSE 4 | include README.rst 5 | include CHANGELOG.rst 6 | include MANIFEST.in 7 | recursive-include docs * 8 | recursive-include tests *.py 9 | prune docs/_build 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | django-reversion 3 | ================ 4 | 5 | |PyPI latest| |PyPI Version| |PyPI License| |Github actions| |Docs| 6 | 7 | 8 | **django-reversion** is an extension to the Django web framework that provides 9 | version control for model instances. 10 | 11 | Requirements 12 | ============ 13 | 14 | - Python 3.8 or later 15 | - Django 4.2 or later 16 | 17 | Features 18 | ======== 19 | 20 | - Roll back to any point in a model instance's history. 21 | - Recover deleted model instances. 22 | - Simple admin integration. 23 | 24 | Documentation 25 | ============= 26 | 27 | Check out the latest ``django-reversion`` documentation at `Getting Started `_ 28 | 29 | 30 | Issue tracking and source code can be found at the 31 | `main project website `_. 32 | 33 | You can keep up to date with the latest announcements by joining the 34 | `django-reversion discussion group `_. 35 | 36 | Upgrading 37 | ========= 38 | 39 | Please check the `Changelog `_ before upgrading 40 | your installation of django-reversion. 41 | 42 | Contributing 43 | ============ 44 | 45 | Bug reports, bug fixes, and new features are always welcome. Please raise issues on the 46 | `django-reversion project site `_, and submit 47 | pull requests for any new code. 48 | 49 | 1. Fork the `repository `_ on GitHub. 50 | 2. Make a branch off of master and commit your changes to it. 51 | 3. Install requirements. 52 | 53 | .. code:: bash 54 | 55 | $ pip install django psycopg2 mysqlclient -e . 56 | 57 | 4. Run the tests 58 | 59 | .. code:: bash 60 | 61 | $ tests/manage.py test tests 62 | 63 | 5. Create a Pull Request with your contribution 64 | 65 | Contributors 66 | ============ 67 | 68 | The django-reversion project was developed by `Dave Hall `_ and contributed 69 | to by `many other people `_. 70 | 71 | 72 | .. |Docs| image:: https://readthedocs.org/projects/django-reversion/badge/?version=latest 73 | :target: http://django-reversion.readthedocs.org/en/latest/?badge=latest 74 | .. |PyPI Version| image:: https://img.shields.io/pypi/pyversions/django-reversion.svg?maxAge=60 75 | :target: https://pypi.python.org/pypi/django-reversion 76 | .. |PyPI License| image:: https://img.shields.io/pypi/l/django-reversion.svg?maxAge=120 77 | :target: https://github.com/rhenter/django-reversion/blob/master/LICENSE 78 | .. |PyPI latest| image:: https://img.shields.io/pypi/v/django-reversion.svg?maxAge=120 79 | :target: https://pypi.python.org/pypi/django-reversion 80 | .. |Github actions| image:: https://github.com/etianen/django-reversion/workflows/Python%20package/badge.svg 81 | :target: https://github.com/etianen/django-reversion 82 | -------------------------------------------------------------------------------- /docs/_include/admin.rst: -------------------------------------------------------------------------------- 1 | Register your models with a subclass of :ref:`VersionAdmin`. 2 | 3 | .. code:: python 4 | 5 | from django.contrib import admin 6 | from reversion.admin import VersionAdmin 7 | 8 | @admin.register(YourModel) 9 | class YourModelAdmin(VersionAdmin): 10 | 11 | pass 12 | 13 | .. include:: /_include/post-register.rst 14 | -------------------------------------------------------------------------------- /docs/_include/create-revision-args.rst: -------------------------------------------------------------------------------- 1 | ``manage_manually`` 2 | .. include:: /_include/create-revision-manage-manually.rst 3 | 4 | ``using`` 5 | .. include:: /_include/create-revision-using.rst 6 | 7 | ``atomic`` 8 | .. include:: /_include/create-revision-atomic.rst 9 | -------------------------------------------------------------------------------- /docs/_include/create-revision-atomic.rst: -------------------------------------------------------------------------------- 1 | If ``True``, the revision block will be wrapped in a ``transaction.atomic()``. 2 | -------------------------------------------------------------------------------- /docs/_include/create-revision-manage-manually.rst: -------------------------------------------------------------------------------- 1 | If ``True``, versions will not be saved when a model's ``save()`` method is called. This allows version control to be switched off for a given revision block. 2 | -------------------------------------------------------------------------------- /docs/_include/create-revision-using.rst: -------------------------------------------------------------------------------- 1 | The database to save the revision data. The revision block will be wrapped in a transaction using this database. If ``None``, the default database for :ref:`Revision` will be used. 2 | -------------------------------------------------------------------------------- /docs/_include/model-db-arg.rst: -------------------------------------------------------------------------------- 1 | ``model_db`` 2 | The database where the model is saved. Defaults to the default database for the model. 3 | -------------------------------------------------------------------------------- /docs/_include/post-register.rst: -------------------------------------------------------------------------------- 1 | .. Hint:: 2 | Whenever you register a model with django-reversion, run :ref:`createinitialrevisions`. 3 | -------------------------------------------------------------------------------- /docs/_include/signal-args.rst: -------------------------------------------------------------------------------- 1 | ``sender`` 2 | The ``reversion.create_revision`` object. 3 | 4 | ``revision`` 5 | The :ref:`Revision` model. 6 | 7 | ``versions`` 8 | The :ref:`Version` models in the revision. 9 | -------------------------------------------------------------------------------- /docs/_include/throws-registration-error.rst: -------------------------------------------------------------------------------- 1 | Throws :ref:`RegistrationError` if the model has not been registered with django-reversion. 2 | -------------------------------------------------------------------------------- /docs/_include/throws-revert-error.rst: -------------------------------------------------------------------------------- 1 | Throws :ref:`RevertError` if the model could not be deserialized or reverted, e.g. the serialized data is not compatible with the current database schema. 2 | -------------------------------------------------------------------------------- /docs/_include/throws-revision-error.rst: -------------------------------------------------------------------------------- 1 | Throws :ref:`RevisionManagementError` if there is no active revision block. 2 | -------------------------------------------------------------------------------- /docs/admin.rst: -------------------------------------------------------------------------------- 1 | .. _admin: 2 | 3 | Admin integration 4 | ================= 5 | 6 | django-reversion can be used to add rollback and recovery to your admin site. 7 | 8 | .. Important:: 9 | Using the admin integration's preview feature will restore your model inside a temporary transaction, then roll 10 | back the transaction once the preview is rendered. 11 | 12 | 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. 13 | 14 | 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. 15 | 16 | Alternatively, use `transaction.on_commit() `_ to register side-effects to be carried out only on committed transactions. 17 | 18 | .. Warning:: 19 | 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! 20 | 21 | 22 | Overview 23 | -------- 24 | 25 | Registering models 26 | ^^^^^^^^^^^^^^^^^^ 27 | 28 | .. include:: /_include/admin.rst 29 | 30 | .. Note:: 31 | 32 | 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() `. 33 | 34 | 35 | Integration with 3rd party apps 36 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 37 | 38 | You can use :ref:`VersionAdmin` as a mixin with a 3rd party admin class. 39 | 40 | .. code:: python 41 | 42 | @admin.register(SomeModel) 43 | class YourModelAdmin(VersionAdmin, SomeModelAdmin): 44 | 45 | pass 46 | 47 | If the 3rd party model is already registered with the Django admin, you may have to unregister it first. 48 | 49 | .. code:: python 50 | 51 | admin.site.unregister(SomeModel) 52 | 53 | @admin.register(SomeModel) 54 | class YourModelAdmin(VersionAdmin, SomeModelAdmin): 55 | 56 | pass 57 | 58 | 59 | .. _VersionAdmin: 60 | 61 | reversion.admin.VersionAdmin 62 | ---------------------------- 63 | 64 | A subclass of ``django.contrib.ModelAdmin`` providing rollback and recovery. 65 | 66 | 67 | ``revision_form_template = None`` 68 | 69 | A custom template to render the revision form. 70 | 71 | Alternatively, create specially named templates to override the default templates on a per-model or per-app basis. 72 | 73 | * ``'reversion/app_label/model_name/revision_form.html'`` 74 | * ``'reversion/app_label/revision_form.html'`` 75 | * ``'reversion/revision_form.html'`` 76 | 77 | 78 | ``recover_list_template = None`` 79 | 80 | A custom template to render the recover list. 81 | 82 | Alternatively, create specially named templates to override the default templates on a per-model or per-app basis. 83 | 84 | * ``'reversion/app_label/model_name/recover_list.html'`` 85 | * ``'reversion/app_label/recover_list.html'`` 86 | * ``'reversion/recover_list.html'`` 87 | 88 | 89 | ``recover_form_template = None`` 90 | 91 | A custom template to render the recover form. 92 | 93 | * ``'reversion/app_label/model_name/recover_form.html'`` 94 | * ``'reversion/app_label/recover_form.html'`` 95 | * ``'reversion/recover_form.html'`` 96 | 97 | 98 | ``history_latest_first = False`` 99 | 100 | If ``True``, revisions will be displayed with the most recent revision first. 101 | 102 | 103 | .. _VersionAdmin_register: 104 | 105 | ``reversion_register(model, **options)`` 106 | 107 | Callback used by the auto-registration machinery to register the model with django-reversion. Override this to customize how models are registered. 108 | 109 | .. code:: python 110 | 111 | def reversion_register(self, model, **options): 112 | options["exclude"] = ("some_field",) 113 | super().reversion_register(model, **options) 114 | 115 | ``model`` 116 | The model that will be registered with django-reversion. 117 | 118 | ``options`` 119 | Registration options, see :ref:`reversion.register() `. 120 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CHANGELOG.rst 2 | -------------------------------------------------------------------------------- /docs/commands.rst: -------------------------------------------------------------------------------- 1 | .. _commands: 2 | 3 | Management commands 4 | =================== 5 | 6 | django-reversion includes a number of ``django-admin.py`` management commands. 7 | 8 | 9 | .. _createinitialrevisions: 10 | 11 | createinitialrevisions 12 | ---------------------- 13 | 14 | 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. 15 | 16 | .. code:: bash 17 | 18 | ./manage.py createinitialrevisions 19 | ./manage.py createinitialrevisions your_app.YourModel --comment="Initial revision." 20 | ./manage.py createinitialrevisions your_app.YourModel --meta="{\"your_app.RevisionMeta\": {\"hello\": \"world\"}}" 21 | 22 | Run ``./manage.py createinitialrevisions --help`` for more information. 23 | 24 | .. Warning:: 25 | For large databases, this command can take a long time to run. 26 | 27 | 28 | deleterevisions 29 | --------------- 30 | 31 | Deletes old revisions. It can be run regularly to keep revision history manageable. 32 | 33 | .. code:: bash 34 | 35 | ./manage.py deleterevisions 36 | # keep any changes from last 30 days 37 | ./manage.py deleterevisions your_app.YourModel --days=30 38 | # keep 30 most recent changes for each item. 39 | ./manage.py deleterevisions your_app.YourModel --keep=30 40 | # Keep anything from last 30 days and at least 3 from older changes. 41 | ./manage.py deleterevisions your_app.YourModel --keep=3 --days=30 42 | 43 | Run ``./manage.py deleterevisions --help`` for more information. 44 | 45 | .. Warning:: 46 | With no arguments, this command will delete your entire revision history! Read the command help for ways to limit which revisions should be deleted. 47 | -------------------------------------------------------------------------------- /docs/common-problems.rst: -------------------------------------------------------------------------------- 1 | .. _common-problems: 2 | 3 | Common problems 4 | =============== 5 | 6 | Incompatible version data 7 | ------------------------- 8 | 9 | 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: 10 | 11 | .. code:: python 12 | 13 | reversion.errors.RevertError: Could not load - incompatible version data. 14 | 15 | 16 | RegistrationError: class 'myapp.MyModel' has already been registered with Reversion 17 | ----------------------------------------------------------------------------------- 18 | 19 | This is caused by your ``models.py`` file being imported twice, resulting in ``reversion.register()`` being called twice for the same model. 20 | 21 | This problem is almost certainly due to relative import statements in your codebase. Try converting all your relative imports into absolute imports. 22 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # django-reversion documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Jun 2 08:41:36 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | # import os 20 | # import sys 21 | # sys.path.insert(0, os.path.abspath('.')) 22 | 23 | import os 24 | from reversion import __version__ 25 | 26 | # -- General configuration ------------------------------------------------ 27 | 28 | # If your documentation needs a minimal Sphinx version, state it here. 29 | # 30 | # needs_sphinx = '1.0' 31 | 32 | # Add any Sphinx extension module names here, as strings. They can be 33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 34 | # ones. 35 | extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx"] 36 | 37 | intersphinx_mapping = { 38 | "python": ("https://docs.python.org/3", None), 39 | } 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = [] 43 | 44 | # The suffix(es) of source filenames. 45 | # You can specify multiple suffix as a list of string: 46 | # 47 | # source_suffix = ['.rst', '.md'] 48 | source_suffix = '.rst' 49 | 50 | # The encoding of source files. 51 | # 52 | # source_encoding = 'utf-8-sig' 53 | 54 | # The master toctree document. 55 | master_doc = 'index' 56 | 57 | # General information about the project. 58 | project = 'django-reversion' 59 | copyright = '2016, Dave Hall' 60 | author = 'Dave Hall' 61 | 62 | # The version info for the project you're documenting, acts as replacement for 63 | # |version| and |release|, also used in various other places throughout the 64 | # built documents. 65 | # 66 | # The short X.Y version. 67 | version = '.'.join(str(x) for x in __version__[:2]) 68 | # The full version, including alpha/beta/rc tags. 69 | release = '.'.join(str(x) for x in __version__) 70 | 71 | # The language for content autogenerated by Sphinx. Refer to documentation 72 | # for a list of supported languages. 73 | # 74 | # This is also used if you do content translation via gettext catalogs. 75 | # Usually you set "language" from the command line for these cases. 76 | language = 'en' 77 | 78 | # There are two options for replacing |today|: either, you set today to some 79 | # non-false value, then it is used: 80 | # 81 | # today = '' 82 | # 83 | # Else, today_fmt is used as the format for a strftime call. 84 | # 85 | # today_fmt = '%B %d, %Y' 86 | 87 | # List of patterns, relative to source directory, that match files and 88 | # directories to ignore when looking for source files. 89 | # This patterns also effect to html_static_path and html_extra_path 90 | exclude_patterns = ['_build', '_include', 'Thumbs.db', '.DS_Store'] 91 | 92 | # The reST default role (used for this markup: `text`) to use for all 93 | # documents. 94 | # 95 | # default_role = None 96 | 97 | # If true, '()' will be appended to :func: etc. cross-reference text. 98 | # 99 | # add_function_parentheses = True 100 | 101 | # If true, the current module name will be prepended to all description 102 | # unit titles (such as .. function::). 103 | # 104 | # add_module_names = True 105 | 106 | # If true, sectionauthor and moduleauthor directives will be shown in the 107 | # output. They are ignored by default. 108 | # 109 | # show_authors = False 110 | 111 | # The name of the Pygments (syntax highlighting) style to use. 112 | pygments_style = 'sphinx' 113 | 114 | # A list of ignored prefixes for module index sorting. 115 | # modindex_common_prefix = [] 116 | 117 | # If true, keep warnings as "system message" paragraphs in the built documents. 118 | # keep_warnings = False 119 | 120 | suppress_warnings = ["image.nonlocal_uri"] 121 | 122 | # If true, `todo` and `todoList` produce output, else they produce nothing. 123 | todo_include_todos = False 124 | 125 | 126 | # -- Options for HTML output ---------------------------------------------- 127 | 128 | # Use RTD theme locally. 129 | if not os.environ.get('READTHEDOCS', None) == 'True': 130 | import sphinx_rtd_theme 131 | html_theme = "sphinx_rtd_theme" 132 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 133 | 134 | # The name for this set of Sphinx documents. 135 | # " v documentation" by default. 136 | # 137 | # html_title = 'django-reversion v1.10.3' 138 | 139 | # A shorter title for the navigation bar. Default is the same as html_title. 140 | # 141 | # html_short_title = None 142 | 143 | # The name of an image file (relative to this directory) to place at the top 144 | # of the sidebar. 145 | # 146 | # html_logo = None 147 | 148 | # The name of an image file (relative to this directory) to use as a favicon of 149 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 150 | # pixels large. 151 | # 152 | # html_favicon = None 153 | 154 | # Add any paths that contain custom static files (such as style sheets) here, 155 | # relative to this directory. They are copied after the builtin static files, 156 | # so a file named "default.css" will overwrite the builtin "default.css". 157 | html_static_path = [] 158 | 159 | # Add any extra paths that contain custom files (such as robots.txt or 160 | # .htaccess) here, relative to this directory. These files are copied 161 | # directly to the root of the documentation. 162 | # 163 | # html_extra_path = [] 164 | 165 | # If not None, a 'Last updated on:' timestamp is inserted at every page 166 | # bottom, using the given strftime format. 167 | # The empty string is equivalent to '%b %d, %Y'. 168 | # 169 | # html_last_updated_fmt = None 170 | 171 | # If true, SmartyPants will be used to convert quotes and dashes to 172 | # typographically correct entities. 173 | # 174 | # html_use_smartypants = True 175 | 176 | # Custom sidebar templates, maps document names to template names. 177 | # 178 | # html_sidebars = {} 179 | 180 | # Additional templates that should be rendered to pages, maps page names to 181 | # template names. 182 | # 183 | # html_additional_pages = {} 184 | 185 | # If false, no module index is generated. 186 | # 187 | # html_domain_indices = True 188 | 189 | # If false, no index is generated. 190 | # 191 | # html_use_index = True 192 | 193 | # If true, the index is split into individual pages for each letter. 194 | # 195 | # html_split_index = False 196 | 197 | # If true, links to the reST sources are added to the pages. 198 | # 199 | # html_show_sourcelink = True 200 | 201 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 202 | # 203 | # html_show_sphinx = True 204 | 205 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 206 | # 207 | # html_show_copyright = True 208 | 209 | # If true, an OpenSearch description file will be output, and all pages will 210 | # contain a tag referring to it. The value of this option must be the 211 | # base URL from which the finished HTML is served. 212 | # 213 | # html_use_opensearch = '' 214 | 215 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 216 | # html_file_suffix = None 217 | 218 | # Language to be used for generating the HTML full-text search index. 219 | # Sphinx supports the following languages: 220 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 221 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' 222 | # 223 | # html_search_language = 'en' 224 | 225 | # A dictionary with options for the search language support, empty by default. 226 | # 'ja' uses this config value. 227 | # 'zh' user can custom change `jieba` dictionary path. 228 | # 229 | # html_search_options = {'type': 'default'} 230 | 231 | # The name of a javascript file (relative to the configuration directory) that 232 | # implements a search results scorer. If empty, the default will be used. 233 | # 234 | # html_search_scorer = 'scorer.js' 235 | 236 | # Output file base name for HTML help builder. 237 | htmlhelp_basename = 'django-reversiondoc' 238 | 239 | # -- Options for LaTeX output --------------------------------------------- 240 | 241 | latex_elements = { 242 | # The paper size ('letterpaper' or 'a4paper'). 243 | # 244 | # 'papersize': 'letterpaper', 245 | 246 | # The font size ('10pt', '11pt' or '12pt'). 247 | # 248 | # 'pointsize': '10pt', 249 | 250 | # Additional stuff for the LaTeX preamble. 251 | # 252 | # 'preamble': '', 253 | 254 | # Latex figure (float) alignment 255 | # 256 | # 'figure_align': 'htbp', 257 | } 258 | 259 | # Grouping the document tree into LaTeX files. List of tuples 260 | # (source start file, target name, title, 261 | # author, documentclass [howto, manual, or own class]). 262 | latex_documents = [ 263 | (master_doc, 'django-reversion.tex', 'django-reversion Documentation', 264 | 'Dave Hall', 'manual'), 265 | ] 266 | 267 | # The name of an image file (relative to this directory) to place at the top of 268 | # the title page. 269 | # 270 | # latex_logo = None 271 | 272 | # For "manual" documents, if this is true, then toplevel headings are parts, 273 | # not chapters. 274 | # 275 | # latex_use_parts = False 276 | 277 | # If true, show page references after internal links. 278 | # 279 | # latex_show_pagerefs = False 280 | 281 | # If true, show URL addresses after external links. 282 | # 283 | # latex_show_urls = False 284 | 285 | # Documents to append as an appendix to all manuals. 286 | # 287 | # latex_appendices = [] 288 | 289 | # If false, no module index is generated. 290 | # 291 | # latex_domain_indices = True 292 | 293 | 294 | # -- Options for manual page output --------------------------------------- 295 | 296 | # One entry per manual page. List of tuples 297 | # (source start file, name, description, authors, manual section). 298 | man_pages = [ 299 | (master_doc, 'django-reversion', 'django-reversion Documentation', 300 | [author], 1) 301 | ] 302 | 303 | # If true, show URL addresses after external links. 304 | # 305 | # man_show_urls = False 306 | 307 | 308 | # -- Options for Texinfo output ------------------------------------------- 309 | 310 | # Grouping the document tree into Texinfo files. List of tuples 311 | # (source start file, target name, title, author, 312 | # dir menu entry, description, category) 313 | texinfo_documents = [ 314 | (master_doc, 'django-reversion', 'django-reversion Documentation', 315 | author, 'django-reversion', 'One line description of project.', 316 | 'Miscellaneous'), 317 | ] 318 | 319 | # Documents to append as an appendix to all manuals. 320 | # 321 | # texinfo_appendices = [] 322 | 323 | # If false, no module index is generated. 324 | # 325 | # texinfo_domain_indices = True 326 | 327 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 328 | # 329 | # texinfo_show_urls = 'footnote' 330 | 331 | # If true, do not generate a @detailmenu in the "Top" node's menu. 332 | # 333 | # texinfo_no_detailmenu = False 334 | -------------------------------------------------------------------------------- /docs/errors.rst: -------------------------------------------------------------------------------- 1 | .. _errors: 2 | 3 | Errors 4 | ====== 5 | 6 | django-reversion defines several custom errors. 7 | 8 | 9 | .. _RegistrationError: 10 | 11 | reversion.RegistrationError 12 | --------------------------- 13 | 14 | Something went wrong with the :ref:`registration-api`. 15 | 16 | 17 | .. _RevisionManagementError: 18 | 19 | reversion.RevisionManagementError 20 | --------------------------------- 21 | 22 | Something went wrong using the :ref:`revision-api`. 23 | 24 | 25 | .. _RevertError: 26 | 27 | reversion.RevertError 28 | --------------------- 29 | 30 | Something went wrong reverting a revision. 31 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. _index: 2 | 3 | django-reversion 4 | ================ 5 | 6 | **django-reversion** is an extension to the Django web framework that provides 7 | version control for model instances. 8 | 9 | 10 | Features 11 | -------- 12 | 13 | - Roll back to any point in a model instance's history. 14 | - Recover deleted model instances. 15 | - Simple admin integration. 16 | 17 | 18 | Installation 19 | ------------ 20 | 21 | To install django-reversion: 22 | 23 | 1. Install with pip: ``pip install django-reversion``. 24 | 2. Add ``'reversion'`` to ``INSTALLED_APPS``. 25 | 3. Run ``manage.py migrate``. 26 | 27 | 28 | Admin integration 29 | ----------------- 30 | 31 | django-reversion can be used to add rollback and recovery to your admin site. 32 | 33 | .. include:: /_include/admin.rst 34 | 35 | For more information about admin integration, see :ref:`admin`. 36 | 37 | 38 | Low-level API 39 | ------------- 40 | 41 | You can use the django-reversion API to build version-controlled applications. See :ref:`api`. 42 | 43 | 44 | More information 45 | ---------------- 46 | 47 | Installation 48 | ^^^^^^^^^^^^ 49 | 50 | .. toctree:: 51 | :maxdepth: 1 52 | 53 | common-problems 54 | changelog 55 | 56 | 57 | Usage 58 | ^^^^^ 59 | 60 | .. toctree:: 61 | :maxdepth: 2 62 | 63 | admin 64 | commands 65 | api 66 | views 67 | middleware 68 | errors 69 | signals 70 | -------------------------------------------------------------------------------- /docs/middleware.rst: -------------------------------------------------------------------------------- 1 | .. _middleware: 2 | 3 | Middleware 4 | ========== 5 | 6 | Shortcuts when using django-reversion in views. 7 | 8 | 9 | reversion.middleware.RevisionMiddleware 10 | --------------------------------------- 11 | 12 | Wrap every request in a revision block. 13 | 14 | The request user will also be added to the revision metadata. 15 | 16 | To enable ``RevisionMiddleware``, add ``'reversion.middleware.RevisionMiddleware'`` to your ``MIDDLEWARE`` setting. 17 | 18 | .. Warning:: 19 | This will wrap every request that meets the specified criterion in a database transaction. For best performance, consider marking individual views instead. 20 | 21 | 22 | ``RevisionMiddleware.manage_manually = False`` 23 | 24 | .. include:: /_include/create-revision-manage-manually.rst 25 | 26 | 27 | ``RevisionMiddleware.using = None`` 28 | 29 | .. include:: /_include/create-revision-using.rst 30 | 31 | 32 | ``RevisionMiddleware.atomic = True`` 33 | 34 | .. include:: /_include/create-revision-atomic.rst 35 | 36 | ``RevisionMiddleware.request_creates_revision(request)`` 37 | 38 | 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. 39 | 40 | For example: 41 | 42 | .. code:: python 43 | 44 | from reversion.middleware import RevisionMiddleware 45 | 46 | class BypassRevisionMiddleware(RevisionMiddleware): 47 | 48 | def request_creates_revision(self, request): 49 | # Bypass the revision according to some header 50 | silent = request.META.get("HTTP_X_NOREVISION", "false") 51 | return super().request_creates_revision(request) and \ 52 | silent != "true" 53 | -------------------------------------------------------------------------------- /docs/signals.rst: -------------------------------------------------------------------------------- 1 | .. _signals: 2 | 3 | Signals 4 | ======= 5 | 6 | django-reversion provides two custom signals. 7 | 8 | 9 | reversion.signals.pre_revision_commit 10 | ------------------------------------- 11 | 12 | Sent just before a revision is saved to the database. 13 | 14 | .. include:: /_include/signal-args.rst 15 | 16 | 17 | reversion.signals.post_revision_commit 18 | -------------------------------------- 19 | 20 | Sent just after a revision and its related versions are saved to the database. 21 | 22 | .. include:: /_include/signal-args.rst 23 | -------------------------------------------------------------------------------- /docs/views.rst: -------------------------------------------------------------------------------- 1 | .. _views: 2 | 3 | Views 4 | ===== 5 | 6 | Shortcuts when using django-reversion in views. 7 | 8 | 9 | Decorators 10 | ---------- 11 | 12 | ``reversion.views.create_revision(manage_manually=False, using=None, atomic=True, request_creates_revision=None)`` 13 | 14 | Decorates a view to wrap every request in a revision block. 15 | 16 | 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. 17 | 18 | .. include:: /_include/create-revision-args.rst 19 | 20 | ``request_creates_revision`` 21 | 22 | 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. 23 | 24 | 25 | reversion.views.RevisionMixin 26 | ----------------------------- 27 | 28 | Mixin a class-based view to wrap every request in a revision block. 29 | 30 | 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. 31 | 32 | .. code:: python 33 | 34 | from django.contrib.auth.views import FormView 35 | from reversion.views import RevisionMixin 36 | 37 | class RevisionFormView(RevisionMixin, FormView): 38 | 39 | pass 40 | 41 | 42 | ``RevisionMixin.revision_manage_manually = False`` 43 | 44 | .. include:: /_include/create-revision-manage-manually.rst 45 | 46 | 47 | ``RevisionMixin.revision_using = None`` 48 | 49 | .. include:: /_include/create-revision-using.rst 50 | 51 | ``RevisionMixin.revision_request_creates_revision(request)`` 52 | 53 | 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. 54 | -------------------------------------------------------------------------------- /reversion/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | An extension to the Django web framework that provides version control for model instances. 3 | 4 | Developed by Dave Hall. 5 | 6 | 7 | """ 8 | 9 | try: 10 | import django # noqa 11 | except ImportError: # pragma: no cover 12 | # The top-level API requires Django, which might not be present if setup.py 13 | # is importing reversion to get __version__. 14 | pass 15 | else: 16 | from reversion.errors import ( # noqa 17 | RevertError, 18 | RevisionManagementError, 19 | RegistrationError, 20 | ) 21 | from reversion.revisions import ( # noqa 22 | is_active, 23 | is_manage_manually, 24 | get_user, 25 | set_user, 26 | get_comment, 27 | set_comment, 28 | get_date_created, 29 | set_date_created, 30 | add_meta, 31 | add_to_revision, 32 | create_revision, 33 | register, 34 | is_registered, 35 | unregister, 36 | get_registered_models, 37 | ) 38 | 39 | __version__ = VERSION = (5, 1, 0) 40 | -------------------------------------------------------------------------------- /reversion/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class ReversionConfig(AppConfig): 6 | name = 'reversion' 7 | verbose_name = _('Reversion') 8 | default_auto_field = 'django.db.models.AutoField' 9 | -------------------------------------------------------------------------------- /reversion/errors.py: -------------------------------------------------------------------------------- 1 | class RevertError(Exception): 2 | 3 | """Exception thrown when something goes wrong with reverting a model.""" 4 | 5 | 6 | class RevisionManagementError(Exception): 7 | 8 | """Exception that is thrown when something goes wrong with revision managment.""" 9 | 10 | 11 | class RegistrationError(Exception): 12 | 13 | """Exception thrown when registration with django-reversion goes wrong.""" 14 | -------------------------------------------------------------------------------- /reversion/locale/ar/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/ar/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/ar/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2015-06-15 01:49+0000\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: LANGUAGE \n" 14 | "Language: \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " 19 | "&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" 20 | 21 | #: reversion/admin.py:161 22 | msgid "Initial version." 23 | msgstr "النسخة الأولية" 24 | 25 | #: reversion/admin.py:195 reversion/templates/reversion/change_list.html:7 26 | #: reversion/templates/reversion/recover_form.html:10 27 | #: reversion/templates/reversion/recover_list.html:10 28 | #, python-format 29 | msgid "Recover deleted %(name)s" 30 | msgstr "أستعيد المحذوف من %(name)s" 31 | 32 | #: reversion/admin.py:312 33 | #, python-format 34 | msgid "Reverted to previous version, saved on %(datetime)s" 35 | msgstr "اُعيد لنسخه سابقه، حُفظ في %(datetime)s" 36 | 37 | #: reversion/admin.py:314 38 | #, python-format 39 | msgid "" 40 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 41 | "below." 42 | msgstr "" 43 | "تم أعاده %(model)s \"%(name)s\" بنجاح ، يمكنك/ي التعديل مجددا" 44 | "" 45 | 46 | #: reversion/admin.py:399 47 | #, python-format 48 | msgid "Recover %(name)s" 49 | msgstr "إستعيد %(name)s" 50 | 51 | #: reversion/admin.py:413 52 | #, python-format 53 | msgid "Revert %(name)s" 54 | msgstr "أعد %(name)s" 55 | 56 | #: reversion/models.py:59 57 | msgid "date created" 58 | msgstr "تاريخ الأنشاء" 59 | 60 | #: reversion/models.py:66 61 | msgid "user" 62 | msgstr "المستخدم" 63 | 64 | #: reversion/models.py:70 65 | msgid "comment" 66 | msgstr "التعليق" 67 | 68 | #: reversion/templates/reversion/object_history.html:8 69 | msgid "" 70 | "Choose a date from the list below to revert to a previous version of this " 71 | "object." 72 | msgstr "أختر تاريخ من القائمه أدناه لأعاده نسخه سابقه من هذا الكيان" 73 | 74 | #: reversion/templates/reversion/object_history.html:15 75 | #: reversion/templates/reversion/recover_list.html:23 76 | msgid "Date/time" 77 | msgstr "التاريخ/ الوقت" 78 | 79 | #: reversion/templates/reversion/object_history.html:16 80 | msgid "User" 81 | msgstr "المستخدم" 82 | 83 | #: reversion/templates/reversion/object_history.html:17 84 | msgid "Comment" 85 | msgstr "التعليق" 86 | 87 | #: reversion/templates/reversion/object_history.html:38 88 | msgid "" 89 | "This object doesn't have a change history. It probably wasn't added via this " 90 | "admin site." 91 | msgstr "لا يوجد تاريخ تعديل لهذا الكيان. ربما لم يُنشأ من موقع الإداره" 92 | 93 | #: reversion/templates/reversion/recover_form.html:7 94 | #: reversion/templates/reversion/recover_list.html:7 95 | #: reversion/templates/reversion/revision_form.html:7 96 | msgid "Home" 97 | msgstr "الرئيسيه" 98 | 99 | #: reversion/templates/reversion/recover_form.html:17 100 | msgid "Press the save button below to recover this version of the object." 101 | msgstr "أنقر على حفظ أدناه لأسترجاع هذه النسخه" 102 | 103 | #: reversion/templates/reversion/recover_list.html:17 104 | msgid "" 105 | "Choose a date from the list below to recover a deleted version of an object." 106 | msgstr "أختر تاريخ من القائمه أدناه لإسترجاع نسخه سابقه من هذا الكيان" 107 | 108 | 109 | #: reversion/templates/reversion/recover_list.html:37 110 | msgid "There are no deleted objects to recover." 111 | msgstr "لا يوجد كيانات محذوفه لإسترجاعها" 112 | 113 | #: reversion/templates/reversion/revision_form.html:11 114 | msgid "History" 115 | msgstr "التاريخ" 116 | 117 | #: reversion/templates/reversion/revision_form.html:12 118 | #, python-format 119 | msgid "Revert %(verbose_name)s" 120 | msgstr "إسترجع %(verbose_name)s" 121 | 122 | #: reversion/templates/reversion/revision_form.html:25 123 | msgid "Press the save button below to revert to this version of the object." 124 | msgstr "أنقر على حفظ أدناه لإعاده هذه النسخه" 125 | 126 | -------------------------------------------------------------------------------- /reversion/locale/cs/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/cs/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/cs/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2011-01-12 11:13+0100\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " 18 | "<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" 19 | 20 | #: admin.py:112 templates/reversion/change_list.html:8 21 | #: templates/reversion/recover_list.html:10 22 | #, python-format 23 | msgid "Recover deleted %(name)s" 24 | msgstr "Obnovit smazané %(name)s" 25 | 26 | #: admin.py:165 27 | #, python-format 28 | msgid "Reverted to previous version, saved on %(datetime)s" 29 | msgstr "Vráceno do předchozí verze uložené v %(datetime)s" 30 | 31 | #: admin.py:167 32 | #, python-format 33 | msgid "" 34 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 35 | "below." 36 | msgstr "" 37 | "Objekt %(model)s \"%(name)s\" byl úspěšně obnoven. Můžete ho znovu začít upravovat " 38 | "níže." 39 | 40 | #: admin.py:273 41 | #, python-format 42 | msgid "Recover %(name)s" 43 | msgstr "Obnovit %(name)s" 44 | 45 | #: admin.py:284 46 | #, python-format 47 | msgid "Revert %(name)s" 48 | msgstr "Navrátit se k předchozí verzi %(name)s" 49 | 50 | #: management/commands/createinitialrevisions.py:76 51 | msgid "Initial version." 52 | msgstr "První verze" 53 | 54 | #: templates/reversion/change_list.html:11 55 | #, python-format 56 | msgid "Add %(name)s" 57 | msgstr "Přidat %(name)s" 58 | 59 | #: templates/reversion/object_history.html:8 60 | msgid "" 61 | "Choose a date from the list below to revert to a previous version of this " 62 | "object." 63 | msgstr "" 64 | "Zvolte datum ze seznamu níže pro návrat k předchozí verzi tohoto objektu." 65 | 66 | #: templates/reversion/object_history.html:15 67 | #: templates/reversion/recover_list.html:23 68 | msgid "Date/time" 69 | msgstr "Datum/čas" 70 | 71 | #: templates/reversion/object_history.html:16 72 | msgid "User" 73 | msgstr "Uživatel" 74 | 75 | #: templates/reversion/object_history.html:17 76 | msgid "Comment" 77 | msgstr "Komentář" 78 | 79 | #: templates/reversion/object_history.html:23 80 | #: templates/reversion/recover_list.html:30 81 | msgid "DATETIME_FORMAT" 82 | msgstr "DATETIME_FORMAT" 83 | 84 | #: templates/reversion/object_history.html:31 85 | msgid "" 86 | "This object doesn't have a change history. It probably wasn't added via this " 87 | "admin site." 88 | msgstr "" 89 | "Tento objekt nemá uloženou žádnou historii změn. Zřejmě nebyl přidát přes toto " 90 | "administrační rozhraní." 91 | 92 | #: templates/reversion/recover_form.html:7 93 | #: templates/reversion/recover_list.html:7 94 | #: templates/reversion/revision_form.html:10 95 | msgid "Home" 96 | msgstr "Domů" 97 | 98 | #: templates/reversion/recover_form.html:10 99 | #, python-format 100 | msgid "Recover deleted %(verbose_name)s" 101 | msgstr "Obnovit smazané %(verbose_name)s" 102 | 103 | #: templates/reversion/recover_form.html:17 104 | msgid "Press the save button below to recover this version of the object." 105 | msgstr "Klikněte na tlačítko uložit pro obnovení této verze objektu." 106 | 107 | #: templates/reversion/recover_list.html:17 108 | msgid "" 109 | "Choose a date from the list below to recover a deleted version of an object." 110 | msgstr "" 111 | "Zvolte datum ze seznamu níže pro obnovení smazané verze objektu." 112 | 113 | #: templates/reversion/recover_list.html:37 114 | msgid "There are no deleted objects to recover." 115 | msgstr "Žádné smazané objekty k obnovení." 116 | 117 | #: templates/reversion/revision_form.html:14 118 | msgid "History" 119 | msgstr "Historie" 120 | 121 | #: templates/reversion/revision_form.html:15 122 | #, python-format 123 | msgid "Revert %(verbose_name)s" 124 | msgstr "Navrátit %(verbose_name)s k předchozí verzi" 125 | 126 | #: templates/reversion/revision_form.html:28 127 | msgid "Press the save button below to revert to this version of the object." 128 | msgstr "Klikněte na tlačítko uložit pro návrat k této verzi objektu." 129 | -------------------------------------------------------------------------------- /reversion/locale/da/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/da/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/da/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2014-07-30 11:17+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #: admin.py:160 22 | msgid "Initial version." 23 | msgstr "Første version." 24 | 25 | #: admin.py:194 templates/reversion/change_list.html:7 26 | #: templates/reversion/recover_form.html:11 27 | #: templates/reversion/recover_list.html:11 28 | #, python-format 29 | msgid "Recover deleted %(name)s" 30 | msgstr "Gendan slettede %(name)s" 31 | 32 | #: admin.py:311 33 | #, python-format 34 | msgid "Reverted to previous version, saved on %(datetime)s" 35 | msgstr "Gendannet til tidligere version, gemt den %(datetime)s" 36 | 37 | #: admin.py:313 38 | #, python-format 39 | msgid "" 40 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 41 | "below." 42 | msgstr "" 43 | "Gendannelsen af %(model)s \"%(name)s\" var succesfuld. Du kan redigere den igen" 44 | "her under" 45 | 46 | #: admin.py:398 47 | #, python-format 48 | msgid "Recover %(name)s" 49 | msgstr "Genskab %(name)s" 50 | 51 | #: admin.py:412 52 | #, python-format 53 | msgid "Revert %(name)s" 54 | msgstr "Revertere %(name)s" 55 | 56 | #: models.py:55 57 | msgid "date created" 58 | msgstr "oprettelsesdato" 59 | 60 | #: models.py:62 61 | msgid "user" 62 | msgstr "bruger" 63 | 64 | #: models.py:66 65 | msgid "comment" 66 | msgstr "kommentar" 67 | 68 | #: templates/reversion/object_history.html:8 69 | msgid "Choose a date from the list below to revert to a previous version of this object." 70 | msgstr "Vælg en dato fra listen her under for at Revertere til en tidligere version af det her objekt." 71 | 72 | #: templates/reversion/object_history.html:15 73 | #: templates/reversion/recover_list.html:24 74 | msgid "Date/time" 75 | msgstr "Dato/tid" 76 | 77 | #: templates/reversion/object_history.html:16 78 | msgid "User" 79 | msgstr "Bruger" 80 | 81 | #: templates/reversion/object_history.html:17 82 | msgid "Comment" 83 | msgstr "Kommentar" 84 | 85 | #: templates/reversion/object_history.html:38 86 | msgid "" 87 | "This object doesn't have a change history. It probably wasn't added via this " 88 | "admin site." 89 | msgstr "" 90 | "Det her objekt har ingen ændringshistorik. Det er sandsynligvis ikke tilføjet via" 91 | "dette admin-side." 92 | 93 | #: templates/reversion/recover_form.html:8 94 | #: templates/reversion/recover_list.html:8 95 | #: templates/reversion/revision_form.html:8 96 | msgid "Home" 97 | msgstr "Hjem" 98 | 99 | #: templates/reversion/recover_form.html:18 100 | msgid "Press the save button below to recover this version of the object." 101 | msgstr "Tryk på gem knappen nedenunder for at genskab denne version af objektet." 102 | 103 | #: templates/reversion/recover_list.html:18 104 | msgid "Choose a date from the list below to recover a deleted version of an object." 105 | msgstr "Vælg en dato fra listen her under for at gendanne til en tidligere version af objektet." 106 | 107 | #: templates/reversion/recover_list.html:38 108 | msgid "There are no deleted objects to recover." 109 | msgstr "Der findes inden slettede objekter at gendanne." 110 | 111 | #: templates/reversion/revision_form.html:12 112 | msgid "History" 113 | msgstr "Historik" 114 | 115 | #: templates/reversion/revision_form.html:13 116 | #, python-format 117 | msgid "Revert %(verbose_name)s" 118 | msgstr "Revertere %(verbose_name)s" 119 | 120 | #: templates/reversion/revision_form.html:26 121 | msgid "Press the save button below to revert to this version of the object." 122 | msgstr "Tryk på gem her nedenunder for at revertere til denne version af objektet." 123 | -------------------------------------------------------------------------------- /reversion/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: reversion\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-02-03 08:31+0100\n" 11 | "PO-Revision-Date: 2009-02-03 08:41+0100\n" 12 | "Last-Translator: Jannis Leidel \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | #: admin.py:122 templates/reversion/change_list.html:8 20 | #: templates/reversion/recover_list.html:9 21 | #, python-format 22 | msgid "Recover deleted %(name)s" 23 | msgstr "Gelöschte %(name)s wiederherstellen" 24 | 25 | #: admin.py:155 26 | #, python-format 27 | msgid "Reverted to previous version, saved on %(datetime)s" 28 | msgstr "Zu vorheriger Version zurückgesetzt, %(datetime)s gespeichert" 29 | 30 | #: admin.py:157 31 | #, python-format 32 | msgid "" 33 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 34 | "below." 35 | msgstr "" 36 | "%(model)s \"%(name)s\" wurde erfolgreich zurückgesetzt. Sie können mit der " 37 | "Bearbeitung forfahren." 38 | 39 | #: admin.py:227 40 | #, python-format 41 | msgid "Recover %s" 42 | msgstr "%s wiederherstellen" 43 | 44 | #: admin.py:243 45 | #, python-format 46 | msgid "Revert %(name)s" 47 | msgstr "%(name)s zurücksetzen" 48 | 49 | #: management/commands/createinitialrevisions.py:76 50 | msgid "Initial version." 51 | msgstr "Ursprüngliche Version." 52 | 53 | #: templates/reversion/change_list.html:11 54 | #, python-format 55 | msgid "Add %(name)s" 56 | msgstr "%(name)s hinzufügen" 57 | 58 | #: templates/reversion/object_history.html:8 59 | msgid "" 60 | "Choose a date from the list below to revert to a previous version of this " 61 | "object." 62 | msgstr "" 63 | "Wählen Sie einen Zeitpunkt aus der untenstehenden Liste aus, um zu einer " 64 | "vorherigen Version dieses Objektes zurückzukehren." 65 | 66 | #: templates/reversion/object_history.html:15 67 | #: templates/reversion/recover_list.html:21 68 | msgid "Date/time" 69 | msgstr "Datum/Zeit" 70 | 71 | #: templates/reversion/object_history.html:16 72 | msgid "User" 73 | msgstr "Benutzer" 74 | 75 | #: templates/reversion/object_history.html:17 76 | msgid "Comment" 77 | msgstr "Kommentar" 78 | 79 | #: templates/reversion/object_history.html:23 80 | #: templates/reversion/recover_list.html:28 81 | msgid "DATETIME_FORMAT" 82 | msgstr "j. N Y, H:i" 83 | 84 | #: templates/reversion/object_history.html:31 85 | msgid "" 86 | "This object doesn't have a change history. It probably wasn't added via this " 87 | "admin site." 88 | msgstr "" 89 | "Dieses Objekt hat keine Änderungsgeschichte. Es wurde möglicherweise nicht " 90 | "über diese Verwaltungsseiten angelegt." 91 | 92 | #: templates/reversion/recover_form.html:14 93 | #: templates/reversion/recover_list.html:6 94 | #: templates/reversion/revision_form.html:14 95 | msgid "Home" 96 | msgstr "Start" 97 | 98 | #: templates/reversion/recover_form.html:17 99 | #, python-format 100 | msgid "Recover deleted %(verbose_name)s" 101 | msgstr "Gelöschte %(verbose_name)s wiederherstellen" 102 | 103 | #: templates/reversion/recover_form.html:18 104 | #, python-format 105 | msgid "Recover %(name)s" 106 | msgstr "%(name)s wiederherstellen" 107 | 108 | #: templates/reversion/recover_form.html:24 109 | msgid "Press the save button below to recover this version of the object." 110 | msgstr "Sichern Sie, um diese Version des Objektes wiederherzustellen." 111 | 112 | #: templates/reversion/recover_list.html:15 113 | msgid "" 114 | "Choose a date from the list below to recover a deleted version of an object." 115 | msgstr "" 116 | "Wählen Sie einen Zeitpunk aus der untenstehenden Liste, um eine gelöschte " 117 | "Version des Objektes wiederherzustellen." 118 | 119 | #: templates/reversion/recover_list.html:35 120 | msgid "There are no deleted objects to recover." 121 | msgstr "Es sind keine gelöschten Objekte zur Wiederherstellung vorhanden." 122 | 123 | #: templates/reversion/revision_form.html:18 124 | msgid "History" 125 | msgstr "Geschichte" 126 | 127 | #: templates/reversion/revision_form.html:19 128 | #, python-format 129 | msgid "Revert %(verbose_name)s" 130 | msgstr "%(verbose_name)s zurücksetzen" 131 | 132 | #: templates/reversion/revision_form.html:32 133 | msgid "Press the save button below to revert to this version of the object." 134 | msgstr "Sichern Sie, um das Objekt zu dieser Version zurückzusetzen." 135 | -------------------------------------------------------------------------------- /reversion/locale/es/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/es/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/es/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , 2013. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2013-08-31 15:49-0500\n" 12 | "PO-Revision-Date: 2013-08-31 16:22-0500\n" 13 | "Last-Translator: Alexander Ayasca Esquives \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: es\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 20 | 21 | #: admin.py:144 22 | msgid "Initial version." 23 | msgstr "Versión inicial" 24 | 25 | #: admin.py:166 26 | #, python-format 27 | msgid "Deleted %(verbose_name)s." 28 | msgstr "%(verbose_name)s eliminados" 29 | 30 | #: admin.py:189 templates/reversion/change_list.html:7 31 | #: templates/reversion/recover_form.html:11 32 | #: templates/reversion/recover_list.html:11 33 | #, python-format 34 | msgid "Recover deleted %(name)s" 35 | msgstr "Recuperar %(name)s eliminados" 36 | 37 | #: admin.py:304 38 | #, python-format 39 | msgid "Reverted to previous version, saved on %(datetime)s" 40 | msgstr "Revertido a una versión anterior, grabada el %(datetime)s" 41 | 42 | #: admin.py:306 43 | #, python-format 44 | msgid "" 45 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 46 | "below." 47 | msgstr "El %(model)s \"%(name)s\" fue revertido satisfactoriamente. Puede editarlo nuevamente " 48 | 49 | #: admin.py:392 50 | #, python-format 51 | msgid "Recover %(name)s" 52 | msgstr "Recuperar %(name)s" 53 | 54 | #: admin.py:406 55 | #, python-format 56 | msgid "Revert %(name)s" 57 | msgstr "Revertir %(name)s" 58 | 59 | #: models.py:59 60 | msgid "date created" 61 | msgstr "fecha de creación" 62 | 63 | #: models.py:65 64 | msgid "user" 65 | msgstr "usuario" 66 | 67 | #: models.py:69 68 | msgid "comment" 69 | msgstr "comentario" 70 | 71 | #: templates/reversion/object_history.html:8 72 | msgid "" 73 | "Choose a date from the list below to revert to a previous version of this " 74 | "object." 75 | msgstr "Escoja una fecha de la lista siguiente para revertir a una versión anterior de este objeto" 76 | 77 | #: templates/reversion/object_history.html:15 78 | #: templates/reversion/recover_list.html:24 79 | msgid "Date/time" 80 | msgstr "Fecha/Hora" 81 | 82 | #: templates/reversion/object_history.html:16 83 | msgid "User" 84 | msgstr "Usuario" 85 | 86 | #: templates/reversion/object_history.html:17 87 | msgid "Comment" 88 | msgstr "Comentario" 89 | 90 | #: templates/reversion/object_history.html:36 91 | msgid "" 92 | "This object doesn't have a change history. It probably wasn't added via this " 93 | "admin site." 94 | msgstr "Este objeto no tiene un historial de cambios. Probablemente no fue añadido por medio del sitio de administración" 95 | 96 | #: templates/reversion/recover_form.html:8 97 | #: templates/reversion/recover_list.html:8 98 | #: templates/reversion/revision_form.html:8 99 | msgid "Home" 100 | msgstr "Inicio" 101 | 102 | #: templates/reversion/recover_form.html:18 103 | msgid "Press the save button below to recover this version of the object." 104 | msgstr "Presione el botón guardar para recuperar esta versión del objeto" 105 | 106 | #: templates/reversion/recover_list.html:18 107 | msgid "" 108 | "Choose a date from the list below to recover a deleted version of an object." 109 | msgstr "Escoja una fecha de la lista siguiente para recuperar una versión eliminada del objeto" 110 | 111 | #: templates/reversion/recover_list.html:38 112 | msgid "There are no deleted objects to recover." 113 | msgstr "No hay objetos eliminados a recuperar" 114 | 115 | #: templates/reversion/revision_form.html:12 116 | msgid "History" 117 | msgstr "Historial" 118 | 119 | #: templates/reversion/revision_form.html:13 120 | #, python-format 121 | msgid "Revert %(verbose_name)s" 122 | msgstr "Revertir %(verbose_name)s" 123 | 124 | #: templates/reversion/revision_form.html:26 125 | msgid "Press the save button below to revert to this version of the object." 126 | msgstr "Presione el botón guardar para revertir a esta versión del objeto" 127 | -------------------------------------------------------------------------------- /reversion/locale/es_AR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/es_AR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/es_AR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Gonzalo Bustos, 2015. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: \n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2015-10-11 19:10-0300\n" 11 | "PO-Revision-Date: 2015-10-11 19:12-0300\n" 12 | "Last-Translator: Gonzalo Bustos\n" 13 | "Language-Team: Spanish (Argentina)\n" 14 | "Language: es_AR\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | "X-Generator: Poedit 1.6.10\n" 20 | 21 | #: admin.py:144 22 | msgid "Initial version." 23 | msgstr "Versión inicial." 24 | 25 | #: admin.py:166 26 | #, python-format 27 | msgid "Deleted %(verbose_name)s." 28 | msgstr "%(verbose_name)s eliminados" 29 | 30 | #: admin.py:189 templates/reversion/change_list.html:7 31 | #: templates/reversion/recover_form.html:11 32 | #: templates/reversion/recover_list.html:11 33 | #, python-format 34 | msgid "Recover deleted %(name)s" 35 | msgstr "Restaurar %(name)s eliminados" 36 | 37 | #: admin.py:304 38 | #, python-format 39 | msgid "Reverted to previous version, saved on %(datetime)s" 40 | msgstr "Revertido a una versión anterior, guardada el %(datetime)s" 41 | 42 | #: admin.py:306 43 | #, python-format 44 | msgid "" 45 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 46 | "below." 47 | msgstr "" 48 | "El %(model)s \"%(name)s\" fue revertido con éxito. Puede editarlo " 49 | "nuevamente a continuación." 50 | 51 | #: admin.py:392 52 | #, python-format 53 | msgid "Recover %(name)s" 54 | msgstr "Restaurar %(name)s" 55 | 56 | #: admin.py:406 57 | #, python-format 58 | msgid "Revert %(name)s" 59 | msgstr "Revertir %(name)s" 60 | 61 | #: models.py:59 62 | msgid "date created" 63 | msgstr "fecha de creación" 64 | 65 | #: models.py:65 66 | msgid "user" 67 | msgstr "usuario" 68 | 69 | #: models.py:69 70 | msgid "comment" 71 | msgstr "comentario" 72 | 73 | #: templates/reversion/object_history.html:8 74 | msgid "" 75 | "Choose a date from the list below to revert to a previous version of this " 76 | "object." 77 | msgstr "" 78 | "Elija una fecha del listado a continuación para revertir a una versión " 79 | "anterior de este objeto" 80 | 81 | #: templates/reversion/object_history.html:15 82 | #: templates/reversion/recover_list.html:24 83 | msgid "Date/time" 84 | msgstr "Fecha/hora" 85 | 86 | #: templates/reversion/object_history.html:16 87 | msgid "User" 88 | msgstr "Usuario" 89 | 90 | #: templates/reversion/object_history.html:17 91 | msgid "Comment" 92 | msgstr "Comentario" 93 | 94 | #: templates/reversion/object_history.html:36 95 | msgid "" 96 | "This object doesn't have a change history. It probably wasn't added via this " 97 | "admin site." 98 | msgstr "" 99 | "Este objeto no tiene un historial de cambios. Es probable que no haya sido " 100 | "agregado a través del sitio de administración." 101 | 102 | #: templates/reversion/recover_form.html:8 103 | #: templates/reversion/recover_list.html:8 104 | #: templates/reversion/revision_form.html:8 105 | msgid "Home" 106 | msgstr "Inicio" 107 | 108 | #: templates/reversion/recover_form.html:18 109 | msgid "Press the save button below to recover this version of the object." 110 | msgstr "Presione el botón guardar para restaurar esta versión del objeto." 111 | 112 | #: templates/reversion/recover_list.html:18 113 | msgid "" 114 | "Choose a date from the list below to recover a deleted version of an object." 115 | msgstr "" 116 | "Elija una fecha del listado a continuación para restaurar una versión " 117 | "eliminada del objeto." 118 | 119 | #: templates/reversion/recover_list.html:38 120 | msgid "There are no deleted objects to recover." 121 | msgstr "No hay objetos eliminados para restaurar." 122 | 123 | #: templates/reversion/revision_form.html:12 124 | msgid "History" 125 | msgstr "Historial" 126 | 127 | #: templates/reversion/revision_form.html:13 128 | #, python-format 129 | msgid "Revert %(verbose_name)s" 130 | msgstr "Revertir %(verbose_name)s" 131 | 132 | #: templates/reversion/revision_form.html:26 133 | msgid "Press the save button below to revert to this version of the object." 134 | msgstr "Presione el botón guardar para revertir a esta versión del objeto." 135 | -------------------------------------------------------------------------------- /reversion/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Simon Charette , 2010. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2010-10-01 20:56-0400\n" 12 | "PO-Revision-Date: 2011-09-21 16:31-0400\n" 13 | "Last-Translator: Etienne Desautels \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: fr\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 20 | 21 | #: admin.py:143 templates/reversion/change_list.html:7 22 | #: templates/reversion/recover_form.html:10 23 | #: templates/reversion/recover_list.html:10 24 | #, python-format 25 | msgid "Recover deleted %(name)s" 26 | msgstr "Récupérer %(name)s supprimés" 27 | 28 | #: admin.py:123 29 | #, python-format 30 | msgid "Deleted %(verbose_name)s." 31 | msgstr "Supprimé %(verbose_name)s." 32 | 33 | #: admin.py:252 34 | #, python-format 35 | msgid "Reverted to previous version, saved on %(datetime)s" 36 | msgstr "Restauré depuis une version précédente, sauvée le %(datetime)s" 37 | 38 | #: admin.py:254 39 | #, python-format 40 | msgid "" 41 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 42 | "below." 43 | msgstr "" 44 | "L’élément %(model)s \"%(name)s\" a été restauré avec succès. Vous pouvez " 45 | "l’éditer à nouveau." 46 | 47 | #: admin.py:337 48 | #, python-format 49 | msgid "Recover %(name)s" 50 | msgstr "Récupérer %(name)s" 51 | 52 | #: admin.py:349 53 | #, python-format 54 | msgid "Revert %(name)s" 55 | msgstr "Restaurer %(name)s" 56 | 57 | #: admin.py:111 58 | msgid "Initial version." 59 | msgstr "Version initiale." 60 | 61 | #: templates/reversion/object_history.html:8 62 | msgid "" 63 | "Choose a date from the list below to revert to a previous version of this " 64 | "object." 65 | msgstr "Choisissez une date dans la liste ci-dessous afin de restaurer " 66 | "une version précédente de cet élément." 67 | 68 | #: templates/reversion/object_history.html:15 69 | #: templates/reversion/recover_list.html:23 70 | msgid "Date/time" 71 | msgstr "Date/heure" 72 | 73 | #: templates/reversion/object_history.html:16 74 | msgid "User" 75 | msgstr "Utilisateur" 76 | 77 | #: templates/reversion/object_history.html:17 78 | msgid "Comment" 79 | msgstr "Commentaire" 80 | 81 | #: admin.py:252 templates/reversion/object_history.html:23 82 | #: templates/reversion/recover_list.html:30 83 | msgid "DATETIME_FORMAT" 84 | msgstr "j F Y H:i:s" 85 | 86 | #: templates/reversion/object_history.html:36 87 | msgid "" 88 | "This object doesn't have a change history. It probably wasn't added via this " 89 | "admin site." 90 | msgstr "" 91 | "Cet élément ne possède pas d’historique de modifications. Il n’a " 92 | "probablement pas été ajouté à partir de ce site d’administration." 93 | 94 | #: templates/reversion/recover_form.html:7 95 | #: templates/reversion/recover_list.html:7 96 | #: templates/reversion/revision_form.html:7 97 | msgid "Home" 98 | msgstr "Accueil" 99 | 100 | #: templates/reversion/recover_form.html:17 101 | msgid "Press the save button below to recover this version of the object." 102 | msgstr "Cliquez sur le bouton Enregistrer ci-dessous afin de " 103 | "récupérer cet élément." 104 | 105 | #: templates/reversion/recover_list.html:17 106 | msgid "" 107 | "Choose a date from the list below to recover a deleted version of an object." 108 | msgstr "Choisissez une date dans la liste ci-dessous afin de récupérer un " 109 | "élément supprimé." 110 | 111 | #: templates/reversion/recover_list.html:37 112 | msgid "There are no deleted objects to recover." 113 | msgstr "Il n’y a pas d’éléments supprimés à récupérer." 114 | 115 | #: templates/reversion/revision_form.html:11 116 | msgid "History" 117 | msgstr "Historique" 118 | 119 | #: templates/reversion/revision_form.html:12 120 | #, python-format 121 | msgid "Revert %(verbose_name)s" 122 | msgstr "Restaurer %(verbose_name)s" 123 | 124 | #: templates/reversion/revision_form.html:25 125 | msgid "Press the save button below to revert to this version of the object." 126 | msgstr "Cliquez sur le bouton Enregistrer ci-dessous pour " 127 | "restaurer cette version de l’élément." 128 | -------------------------------------------------------------------------------- /reversion/locale/he/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/he/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/he/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-12-10 10:27+0200\n" 11 | "PO-Revision-Date: 2009-12-10 10:45+0200\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % " 18 | "1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n" 19 | 20 | #: admin.py:112 templates/reversion/change_list.html:7 21 | #: templates/reversion/recover_list.html:10 22 | #, python-format 23 | msgid "Recover deleted %(name)s" 24 | msgstr "שחזור %(name)s שנמחקו" 25 | 26 | #: admin.py:158 27 | #, python-format 28 | msgid "Reverted to previous version, saved on %(datetime)s" 29 | msgstr "שוחזר לגרסה קודמת, נשמרה ב-%(datetime)s" 30 | 31 | #: admin.py:160 32 | #, python-format 33 | msgid "" 34 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 35 | "below." 36 | msgstr "" 37 | "שחזור %(model)s \"%(name)s\" לגרסה קודמת הצליח. ניתן לערוך שוב " 38 | "מתחת." 39 | 40 | #: admin.py:259 41 | #, python-format 42 | msgid "Recover %(name)s" 43 | msgstr "אחזור %(name)s" 44 | 45 | #: admin.py:269 46 | #, python-format 47 | msgid "Revert %(name)s" 48 | msgstr "שחזור %(name)s" 49 | 50 | #: templates/reversion/change_list.html:9 51 | #, python-format 52 | msgid "Add %(name)s" 53 | msgstr "הוספת %(name)s" 54 | 55 | #: templates/reversion/object_history.html:8 56 | msgid "" 57 | "Choose a date from the list below to revert to a previous version of this " 58 | "object." 59 | msgstr "" 60 | "נא לבחור תאריך מהרשימה להלן כדי לשחזר לגרסה קודמת של " 61 | "אובייקט זה." 62 | 63 | #: templates/reversion/object_history.html:15 64 | #: templates/reversion/recover_list.html:23 65 | msgid "Date/time" 66 | msgstr "תאריך/שעה" 67 | 68 | #: templates/reversion/object_history.html:16 69 | msgid "User" 70 | msgstr "משתמש" 71 | 72 | #: templates/reversion/object_history.html:17 73 | msgid "Comment" 74 | msgstr "הערה" 75 | 76 | #: templates/reversion/object_history.html:23 77 | #: templates/reversion/recover_list.html:30 78 | msgid "DATETIME_FORMAT" 79 | msgstr "d.m.‏Y H:i:s" 80 | 81 | #: templates/reversion/object_history.html:31 82 | msgid "" 83 | "This object doesn't have a change history. It probably wasn't added via this " 84 | "admin site." 85 | msgstr "" 86 | "לאובייקט זה אין היסטוריית שינוי. כנראה לא התווסף דרך " 87 | "ממשק הניהול." 88 | 89 | #: templates/reversion/recover_form.html:14 90 | #: templates/reversion/recover_list.html:7 91 | #: templates/reversion/revision_form.html:14 92 | msgid "Home" 93 | msgstr "ראשי" 94 | 95 | #: templates/reversion/recover_form.html:17 96 | #, python-format 97 | msgid "Recover deleted %(verbose_name)s" 98 | msgstr "אחזור %(verbose_name)s שנמחק" 99 | 100 | #: templates/reversion/recover_form.html:24 101 | msgid "Press the save button below to recover this version of the object." 102 | msgstr "נא ללחוץ על לחצן השמירה מתחת כדי לאחזר לגרסה זו של האובייקט" 103 | 104 | #: templates/reversion/recover_list.html:17 105 | msgid "" 106 | "Choose a date from the list below to recover a deleted version of an object." 107 | msgstr "" 108 | "נא לבחור תאריך מתחת כדי לאחזר גרסה מחוקה של אובייקט" 109 | 110 | #: templates/reversion/recover_list.html:37 111 | msgid "There are no deleted objects to recover." 112 | msgstr "אין אובייקטים מחוקים לאחזור" 113 | 114 | #: templates/reversion/revision_form.html:18 115 | msgid "History" 116 | msgstr "היסטוריה" 117 | 118 | #: templates/reversion/revision_form.html:19 119 | #, python-format 120 | msgid "Revert %(verbose_name)s" 121 | msgstr "שחזור %(verbose_name)s" 122 | 123 | #: templates/reversion/revision_form.html:32 124 | msgid "Press the save button below to revert to this version of the object." 125 | msgstr "נא ללחוץ על לחצן השמירה מתחת כדי לשחזר לגרסה זו של האובייקט" 126 | -------------------------------------------------------------------------------- /reversion/locale/it/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/it/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/it/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-08-29 13:04+0200\n" 11 | "PO-Revision-Date: 2009-08-29 13:44+0100\n" 12 | "Last-Translator: Marco Beri \n" 13 | "Language-Team: LANGUAGE \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 18 | 19 | #: .\admin.py:128 20 | #: .\templates\reversion\change_list.html.py:7 21 | #: .\templates\reversion\recover_list.html.py:10 22 | msgid "Recover deleted %(name)s" 23 | msgstr "Recupera %(name)s cancellati" 24 | 25 | #: .\admin.py:173 26 | #, python-format 27 | msgid "Reverted to previous version, saved on %(datetime)s" 28 | msgstr "Ritorna alla precedente versione, salvata il %(datetime)s" 29 | 30 | #: .\admin.py:175 31 | #, python-format 32 | msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below." 33 | msgstr "%(model)s \"%(name)s\" è alla versione precedente. Puoi effettuare nuove modifiche se lo desideri." 34 | 35 | #: .\admin.py:271 36 | #, python-format 37 | msgid "Recover %(name)s" 38 | msgstr "Recupera %(name)s" 39 | 40 | #: .\admin.py:281 41 | #, python-format 42 | msgid "Revert %(name)s" 43 | msgstr "Ritorna %(name)s" 44 | 45 | #: .\templates\reversion\change_list.html.py:9 46 | #, python-format 47 | msgid "Add %(name)s" 48 | msgstr "Aggiungi %(name)s" 49 | 50 | #: .\templates\reversion\object_history.html.py:8 51 | msgid "Choose a date from the list below to revert to a previous version of this object." 52 | msgstr "Scegli una data dall'elenco qui sotto per ritornare a una precedente versione di questo oggetto." 53 | 54 | #: .\templates\reversion\object_history.html.py:15 55 | #: .\templates\reversion\recover_list.html.py:23 56 | msgid "Date/time" 57 | msgstr "Data/ora" 58 | 59 | #: .\templates\reversion\object_history.html.py:16 60 | msgid "User" 61 | msgstr "Utente" 62 | 63 | #: .\templates\reversion\object_history.html.py:17 64 | msgid "Comment" 65 | msgstr "Commento" 66 | 67 | #: .\templates\reversion\object_history.html.py:23 68 | #: .\templates\reversion\recover_list.html.py:30 69 | msgid "DATETIME_FORMAT" 70 | msgstr "d/m/Y, G:i" 71 | 72 | #: .\templates\reversion\object_history.html.py:31 73 | msgid "This object doesn't have a change history. It probably wasn't added via this admin site." 74 | msgstr "Questo oggetto non ha una storia di modifiche. Probabilmente non è stato aggiunto attraverso questo sito di Admin." 75 | 76 | #: .\templates\reversion\recover_form.html.py:14 77 | #: .\templates\reversion\recover_list.html.py:7 78 | #: .\templates\reversion\revision_form.html.py:14 79 | msgid "Home" 80 | msgstr "Home" 81 | 82 | #: .\templates\reversion\recover_form.html.py:17 83 | #, python-format 84 | msgid "Recover deleted %(verbose_name)s" 85 | msgstr "Recupera %(verbose_name)s cancellato" 86 | 87 | #: .\templates\reversion\recover_form.html.py:24 88 | msgid "Press the save button below to recover this version of the object." 89 | msgstr "Premi il pulsante Salva in basso per recuperare questa versione dell'oggetto." 90 | 91 | #: .\templates\reversion\recover_list.html.py:17 92 | msgid "Choose a date from the list below to recover a deleted version of an object." 93 | msgstr "Scegli una data dall'elenco qui sotto per recuperare una versione cancellata di questo oggetto." 94 | 95 | #: .\templates\reversion\recover_list.html.py:37 96 | msgid "There are no deleted objects to recover." 97 | msgstr "Non ci sono oggetti cancellati da recuperare." 98 | 99 | #: .\templates\reversion\revision_form.html.py:18 100 | msgid "History" 101 | msgstr "Storia" 102 | 103 | #: .\templates\reversion\revision_form.html.py:19 104 | #, python-format 105 | msgid "Revert %(verbose_name)s" 106 | msgstr "Ritorna %(verbose_name)s" 107 | 108 | #: .\templates\reversion\revision_form.html.py:32 109 | msgid "Press the save button below to revert to this version of the object." 110 | msgstr "Premi il pulsante Salva in basso per ritornare a questa versione dell'oggetto" 111 | 112 | #~ msgid "Recover %s" 113 | #~ msgstr "Recupera %s" 114 | 115 | -------------------------------------------------------------------------------- /reversion/locale/nb/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/nb/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/nb/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Norwegian translation for django-reversion 2 | # This file is distributed under the same license as the django-reversion package. 3 | # Sindre Sorhus , 2011. 4 | # 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: django-reversion\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2011-10-17 09:34+0200\n" 10 | "PO-Revision-Date: 2011-10-17 10:17+0100\n" 11 | "Last-Translator: Sindre Sorhus \n" 12 | "Language-Team: \n" 13 | "Language: \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 18 | "X-Poedit-Language: Norwegian Bokmal\n" 19 | "X-Poedit-Country: NORWAY\n" 20 | "X-Poedit-SourceCharset: utf-8\n" 21 | 22 | #: admin.py:111 23 | msgid "Initial version." 24 | msgstr "Initial versjon" 25 | 26 | #: admin.py:125 27 | #, python-format 28 | msgid "Deleted %(verbose_name)s." 29 | msgstr "Slettet %(verbose_name)s." 30 | 31 | #: admin.py:143 32 | #: templates/reversion/change_list.html:7 33 | #: templates/reversion/recover_form.html:10 34 | #: templates/reversion/recover_list.html:10 35 | #, python-format 36 | msgid "Recover deleted %(name)s" 37 | msgstr "Gjenopprett slettede %(name)s" 38 | 39 | #: admin.py:252 40 | #, python-format 41 | msgid "Reverted to previous version, saved on %(datetime)s" 42 | msgstr "Gjenopprettet til forrige versjon, lagret den %(datetime)s" 43 | 44 | #: admin.py:252 45 | #: templates/reversion/object_history.html:23 46 | #: templates/reversion/recover_list.html:30 47 | msgid "DATETIME_FORMAT" 48 | msgstr "j. F Y H:i" 49 | 50 | #: admin.py:254 51 | #, python-format 52 | msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below." 53 | msgstr "%(model)s \"%(name)s\" ble gjenopprettet. Du kan redigere den igjen nedenfor." 54 | 55 | #: admin.py:337 56 | #, python-format 57 | msgid "Recover %(name)s" 58 | msgstr "Gjenopprett %(name)s" 59 | 60 | #: admin.py:349 61 | #, python-format 62 | msgid "Revert %(name)s" 63 | msgstr "Tilbakestill %(name)s" 64 | 65 | #: templates/reversion/object_history.html:8 66 | msgid "Choose a date from the list below to revert to a previous version of this object." 67 | msgstr "Velg en dato fra listen nedenfor for å gå tilbake til en tidligere versjon av dette objektet." 68 | 69 | #: templates/reversion/object_history.html:15 70 | #: templates/reversion/recover_list.html:23 71 | msgid "Date/time" 72 | msgstr "Dato/tid" 73 | 74 | #: templates/reversion/object_history.html:16 75 | msgid "User" 76 | msgstr "Bruker" 77 | 78 | #: templates/reversion/object_history.html:17 79 | msgid "Comment" 80 | msgstr "Kommentar" 81 | 82 | #: templates/reversion/object_history.html:36 83 | msgid "This object doesn't have a change history. It probably wasn't added via this admin site." 84 | msgstr "Dette objektet har ingen endringshistorie. Objektet er sannsynligvis ikke blitt lagt inn via dette admin nettstedet." 85 | 86 | #: templates/reversion/recover_form.html:7 87 | #: templates/reversion/recover_list.html:7 88 | #: templates/reversion/revision_form.html:7 89 | msgid "Home" 90 | msgstr "Hjem" 91 | 92 | #: templates/reversion/recover_form.html:17 93 | msgid "Press the save button below to recover this version of the object." 94 | msgstr "Trykk på lagre-knappen nedenfor for å gjenopprette denne versjonen av objektet." 95 | 96 | #: templates/reversion/recover_list.html:17 97 | msgid "Choose a date from the list below to recover a deleted version of an object." 98 | msgstr "Velg en dato fra listen nedenfor for å gjenopprette en slettet versjon av et objekt." 99 | 100 | #: templates/reversion/recover_list.html:37 101 | msgid "There are no deleted objects to recover." 102 | msgstr "Finner ingen slettede objekter å gjenopprette." 103 | 104 | #: templates/reversion/revision_form.html:11 105 | msgid "History" 106 | msgstr "Historie" 107 | 108 | #: templates/reversion/revision_form.html:12 109 | #, python-format 110 | msgid "Revert %(verbose_name)s" 111 | msgstr "Tilbakestill %(verbose_name)s" 112 | 113 | #: templates/reversion/revision_form.html:25 114 | msgid "Press the save button below to revert to this version of the object." 115 | msgstr "Trykk på lagre-knappen under for å gå tilbake til denne versjonen av objektet." 116 | 117 | -------------------------------------------------------------------------------- /reversion/locale/nl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/nl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/nl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Dutch translations for django-reversion extension 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Alexander Schoemaker , 2012. 5 | # Bouke Haarsma , 2013. 6 | # 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2012-12-12 10:41+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Alexander Schoemaker \n" 14 | "Language-Team: Dutch\n" 15 | "Language: nl\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 20 | 21 | #: admin.py:141 22 | msgid "Initial version." 23 | msgstr "Eerste versie." 24 | 25 | #: admin.py:163 26 | #, python-format 27 | msgid "Deleted %(verbose_name)s." 28 | msgstr "%(verbose_name)s is verwijderd." 29 | 30 | #: admin.py:186 templates/reversion/change_list.html:7 31 | #: templates/reversion/recover_form.html:11 32 | #: templates/reversion/recover_list.html:11 33 | #, python-format 34 | msgid "Recover deleted %(name)s" 35 | msgstr "Herstel verwijderde %(name)s" 36 | 37 | #: admin.py:297 38 | #, python-format 39 | msgid "Reverted to previous version, saved on %(datetime)s" 40 | msgstr "Vorige versie van %(datetime)s is teruggeplaatst" 41 | 42 | #: admin.py:299 43 | #, python-format 44 | msgid "" 45 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 46 | "below." 47 | msgstr "" 48 | "%(model)s \"%(name)s\" is succesvol teruggeplaatst. Je kunt het nu opnieuw " 49 | "wijzigen." 50 | 51 | #: admin.py:385 52 | #, python-format 53 | msgid "Recover %(name)s" 54 | msgstr "Herstel %(name)s" 55 | 56 | #: admin.py:399 57 | #, python-format 58 | msgid "Revert %(name)s" 59 | msgstr "%(name)s terugplaatsen" 60 | 61 | #: models.py:68 62 | msgid "date created" 63 | msgstr "datum aangemaakt" 64 | 65 | #: models.py:74 66 | msgid "user" 67 | msgstr "gebruiker" 68 | 69 | #: models.py:78 70 | msgid "comment" 71 | msgstr "toelichting" 72 | 73 | #: templates/reversion/object_history.html:8 74 | msgid "" 75 | "Choose a date from the list below to revert to a previous version of this " 76 | "object." 77 | msgstr "" 78 | "Selecteer een datum uit de lijst om een vorige versie terug te plaatsen." 79 | 80 | #: templates/reversion/object_history.html:15 81 | #: templates/reversion/recover_list.html:24 82 | msgid "Date/time" 83 | msgstr "Datum/tijdstip" 84 | 85 | #: templates/reversion/object_history.html:16 86 | msgid "User" 87 | msgstr "Gebruiker" 88 | 89 | #: templates/reversion/object_history.html:17 90 | msgid "Comment" 91 | msgstr "Toelichting" 92 | 93 | #: templates/reversion/object_history.html:36 94 | msgid "" 95 | "This object doesn't have a change history. It probably wasn't added via this " 96 | "admin site." 97 | msgstr "" 98 | "Dit object bevat geen wijzigingshistorie. Vermoedelijk is het niet " 99 | "vanuit sitebeheer toegevoegd." 100 | 101 | #: templates/reversion/recover_form.html:8 102 | #: templates/reversion/recover_list.html:8 103 | #: templates/reversion/revision_form.html:8 104 | msgid "Home" 105 | msgstr "" 106 | 107 | #: templates/reversion/recover_form.html:18 108 | msgid "Press the save button below to recover this version of the object." 109 | msgstr "" 110 | "Klik op onderstaande knop om deze versie van het object te herstellen." 111 | 112 | #: templates/reversion/recover_list.html:18 113 | msgid "" 114 | "Choose a date from the list below to recover a deleted version of an object." 115 | msgstr "" 116 | "Selecteer een datum uit de lijst om een verwijderde versie van het object " 117 | "te herstellen." 118 | 119 | #: templates/reversion/recover_list.html:38 120 | msgid "There are no deleted objects to recover." 121 | msgstr "Er zijn geen verwijderde objecten die hersteld kunnen worden." 122 | 123 | #: templates/reversion/revision_form.html:12 124 | msgid "History" 125 | msgstr "Geschiedenis" 126 | 127 | #: templates/reversion/revision_form.html:13 128 | #, python-format 129 | msgid "Revert %(verbose_name)s" 130 | msgstr "%(verbose_name)s terugplaatsen" 131 | 132 | #: templates/reversion/revision_form.html:26 133 | msgid "Press the save button below to revert to this version of the object." 134 | msgstr "" 135 | "Klik op onderstaande knop om deze versie van het object terug te plaatsen." 136 | -------------------------------------------------------------------------------- /reversion/locale/pl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/pl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/pl/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: reversion\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2011-03-21 20:05+0100\n" 11 | "PO-Revision-Date: 2011-03-21 20:12+0100\n" 12 | "Last-Translator: Zbigniew Siciarz \n" 13 | "Language-Team: LANGUAGE \n" 14 | "Language: pl\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n" 19 | "%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n" 20 | "%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" 21 | 22 | #: admin.py:100 23 | msgid "Initial version." 24 | msgstr "Pierwsza wersja." 25 | 26 | #: admin.py:115 27 | #, python-format 28 | msgid "Deleted %(verbose_name)s." 29 | msgstr "Usunięto %(verbose_name)s" 30 | 31 | #: admin.py:127 32 | #: templates/reversion/change_list.html:8 33 | #: templates/reversion/recover_list.html:10 34 | #, python-format 35 | msgid "Recover deleted %(name)s" 36 | msgstr "Odzyskaj usunięte %(name)s" 37 | 38 | #: admin.py:218 39 | #, python-format 40 | msgid "Reverted to previous version, saved on %(datetime)s" 41 | msgstr "Przywrócono poprzednią wersję, zapisaną %(datetime)s" 42 | 43 | #: admin.py:220 44 | #, python-format 45 | msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below." 46 | msgstr "%(model)s \"%(name)s\" został pomyślnie przywrócony. Możesz go ponownie edytować poniżej." 47 | 48 | #: admin.py:321 49 | #, python-format 50 | msgid "Recover %(name)s" 51 | msgstr "Przywróć %(name)s" 52 | 53 | #: admin.py:332 54 | #, python-format 55 | msgid "Revert %(name)s" 56 | msgstr "Przywróć %(name)s" 57 | 58 | #: templates/reversion/change_list.html:11 59 | #, python-format 60 | msgid "Add %(name)s" 61 | msgstr "Dodaj %(name)s" 62 | 63 | #: templates/reversion/object_history.html:8 64 | msgid "Choose a date from the list below to revert to a previous version of this object." 65 | msgstr "Wybierz datę z poniższej listy, by przywrócić ten obiekt do poprzedniej wersji." 66 | 67 | #: templates/reversion/object_history.html:15 68 | #: templates/reversion/recover_list.html:23 69 | msgid "Date/time" 70 | msgstr "Data/czas" 71 | 72 | #: templates/reversion/object_history.html:16 73 | msgid "User" 74 | msgstr "Użytkownik" 75 | 76 | #: templates/reversion/object_history.html:17 77 | msgid "Comment" 78 | msgstr "Komentarz" 79 | 80 | #: templates/reversion/object_history.html:23 81 | #: templates/reversion/recover_list.html:30 82 | msgid "DATETIME_FORMAT" 83 | msgstr "j. N Y, H:i" 84 | 85 | #: templates/reversion/object_history.html:31 86 | msgid "This object doesn't have a change history. It probably wasn't added via this admin site." 87 | msgstr "Ten obiekt nie posiada historii zmian. Prawdopodobnie nie został dodany za pomocą tego panelu administracyjnego." 88 | 89 | #: templates/reversion/recover_form.html:7 90 | #: templates/reversion/recover_list.html:7 91 | #: templates/reversion/revision_form.html:10 92 | msgid "Home" 93 | msgstr "Strona główna" 94 | 95 | #: templates/reversion/recover_form.html:10 96 | #, python-format 97 | msgid "Recover deleted %(verbose_name)s" 98 | msgstr "Przywróć usunięte %(verbose_name)s" 99 | 100 | #: templates/reversion/recover_form.html:17 101 | msgid "Press the save button below to recover this version of the object." 102 | msgstr "Naciśnij przycisk Zapisz poniżej, by przywrócić tę wersję obiektu." 103 | 104 | #: templates/reversion/recover_list.html:17 105 | msgid "Choose a date from the list below to recover a deleted version of an object." 106 | msgstr "Wybierz datę z poniższej listy, by przywrócić usuniętą wersję obiektu. " 107 | 108 | #: templates/reversion/recover_list.html:37 109 | msgid "There are no deleted objects to recover." 110 | msgstr "Nie ma żadnych usuniętych obiektów do przywrócenia." 111 | 112 | #: templates/reversion/revision_form.html:14 113 | msgid "History" 114 | msgstr "Historia" 115 | 116 | #: templates/reversion/revision_form.html:15 117 | #, python-format 118 | msgid "Revert %(verbose_name)s" 119 | msgstr "Przywróć %(verbose_name)s" 120 | 121 | #: templates/reversion/revision_form.html:28 122 | msgid "Press the save button below to revert to this version of the object." 123 | msgstr "Naciśnij przycisk Zapisz poniżej, by przywrócić tę wersję obiektu." 124 | 125 | #~ msgid "Recover %s" 126 | #~ msgstr "Przywróć %s" 127 | 128 | -------------------------------------------------------------------------------- /reversion/locale/pt_BR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/pt_BR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/pt_BR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-08-29 13:04+0200\n" 11 | "PO-Revision-Date: 2009-08-29 13:44+0100\n" 12 | "Last-Translator: Partec \n" 13 | "Language-Team: Tangerina Lab \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 18 | 19 | #: .\admin.py:128 20 | #: .\templates\reversion\change_list.html.py:7 21 | #: .\templates\reversion\recover_list.html.py:10 22 | msgid "Recover deleted %(name)s" 23 | msgstr "Recuperar %(name)s excluído" 24 | 25 | #: .\admin.py:173 26 | #, python-format 27 | msgid "Reverted to previous version, saved on %(datetime)s" 28 | msgstr "Revertido para versão anterior, salva em %(datetime)s" 29 | 30 | #: .\admin.py:175 31 | #, python-format 32 | msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below." 33 | msgstr "%(model)s \"%(name)s\" foi revertido com sucesso. Você pode editar novamente abaixo." 34 | 35 | #: .\admin.py:271 36 | #, python-format 37 | msgid "Recover %(name)s" 38 | msgstr "Recuperar %(name)s" 39 | 40 | #: .\admin.py:281 41 | #, python-format 42 | msgid "Revert %(name)s" 43 | msgstr "Reverter %(name)s" 44 | 45 | #: .\templates\reversion\change_list.html.py:9 46 | #, python-format 47 | msgid "Add %(name)s" 48 | msgstr "Adicionar %(name)s" 49 | 50 | #: .\templates\reversion\object_history.html.py:8 51 | msgid "Choose a date from the list below to revert to a previous version of this object." 52 | msgstr "Escolha uma data da lista abaixo para reverter para uma versão anterior deste objeto." 53 | 54 | #: .\templates\reversion\object_history.html.py:15 55 | #: .\templates\reversion\recover_list.html.py:23 56 | msgid "Date/time" 57 | msgstr "Data/hora" 58 | 59 | #: .\templates\reversion\object_history.html.py:16 60 | msgid "User" 61 | msgstr "Usuário" 62 | 63 | #: .\templates\reversion\object_history.html.py:17 64 | msgid "Comment" 65 | msgstr "Comentário" 66 | 67 | #: .\templates\reversion\object_history.html.py:23 68 | #: .\templates\reversion\recover_list.html.py:30 69 | msgid "DATETIME_FORMAT" 70 | msgstr "d/m/Y, G:i" 71 | 72 | #: .\templates\reversion\object_history.html.py:31 73 | msgid "This object doesn't have a change history. It probably wasn't added via this admin site." 74 | msgstr "Este objeto não possui um histórico de mudanças. Ele provavelmente não foi adicionado por este site de admin." 75 | 76 | #: .\templates\reversion\recover_form.html.py:14 77 | #: .\templates\reversion\recover_list.html.py:7 78 | #: .\templates\reversion\revision_form.html.py:14 79 | msgid "Home" 80 | msgstr "Home" 81 | 82 | #: .\templates\reversion\recover_form.html.py:17 83 | #, python-format 84 | msgid "Recover deleted %(verbose_name)s" 85 | msgstr "Recuperar %(verbose_name)s excluído" 86 | 87 | #: .\templates\reversion\recover_form.html.py:24 88 | msgid "Press the save button below to recover this version of the object." 89 | msgstr "Pressione o botão salvar, abaixo, para recuperar essa versão do objeto." 90 | 91 | #: .\templates\reversion\recover_list.html.py:17 92 | msgid "Choose a date from the list below to recover a deleted version of an object." 93 | msgstr "Escolha uma data da lista abaixo para recuperar uma versão excluída de um objeto." 94 | 95 | #: .\templates\reversion\recover_list.html.py:37 96 | msgid "There are no deleted objects to recover." 97 | msgstr "Não há objetos excluídos para recuperar." 98 | 99 | #: .\templates\reversion\revision_form.html.py:18 100 | msgid "History" 101 | msgstr "Histórico" 102 | 103 | #: .\templates\reversion\revision_form.html.py:19 104 | #, python-format 105 | msgid "Revert %(verbose_name)s" 106 | msgstr "Reverter %(verbose_name)s" 107 | 108 | #: .\templates\reversion\revision_form.html.py:32 109 | msgid "Press the save button below to revert to this version of the object." 110 | msgstr "Pressione o botão salvar, abaixo, para reverter para essa versão do objeto." 111 | 112 | #~ msgid "Recover %s" 113 | #~ msgstr "Recuperar %s" 114 | 115 | -------------------------------------------------------------------------------- /reversion/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: reversion\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2009-02-03 08:31+0100\n" 11 | "PO-Revision-Date: 2009-10-14 22:21+0300\n" 12 | "Last-Translator: Alexander Yakovlev \n" 13 | "Language-Team: Russian \n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | "X-Poedit-Language: Russian\n" 18 | "X-Poedit-Country: RUSSIAN FEDERATION\n" 19 | "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 20 | "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" 21 | "%100>=11 && n%100<=14)? 2 : 3);\n" 22 | 23 | #: admin.py:122 24 | #: templates/reversion/change_list.html:8 25 | #: templates/reversion/recover_list.html:9 26 | #, python-format 27 | msgid "Recover deleted %(name)s" 28 | msgstr "Восстановить удаленный %(name)s" 29 | 30 | #: admin.py:155 31 | #, python-format 32 | msgid "Reverted to previous version, saved on %(datetime)s" 33 | msgstr "Возвращено к предыдущей версии, сохраненной %(datetime)s" 34 | 35 | #: admin.py:157 36 | #, python-format 37 | msgid "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again below." 38 | msgstr "%(model)s \"%(name)s\" возвращен. Можете продолжить его редактирование." 39 | 40 | #: admin.py:227 41 | #, python-format 42 | msgid "Recover %s" 43 | msgstr "Восстановить %s" 44 | 45 | #: admin.py:243 46 | #, python-format 47 | msgid "Revert %(name)s" 48 | msgstr "Вернуть %(name)s" 49 | 50 | #: templates/reversion/change_list.html:11 51 | #, python-format 52 | msgid "Add %(name)s" 53 | msgstr "Добавить %(name)s" 54 | 55 | #: templates/reversion/object_history.html:8 56 | msgid "Choose a date from the list below to revert to a previous version of this object." 57 | msgstr "Выберите дату из списка, чтобы вернуть предыдущую версию этого объекта." 58 | 59 | #: templates/reversion/object_history.html:15 60 | #: templates/reversion/recover_list.html:21 61 | msgid "Date/time" 62 | msgstr "Дата/время" 63 | 64 | #: templates/reversion/object_history.html:16 65 | msgid "User" 66 | msgstr "Пользователь" 67 | 68 | #: templates/reversion/object_history.html:17 69 | msgid "Comment" 70 | msgstr "Комментарий" 71 | 72 | #: templates/reversion/object_history.html:23 73 | #: templates/reversion/recover_list.html:28 74 | msgid "DATETIME_FORMAT" 75 | msgstr "d.m.Y H:i" 76 | 77 | #: templates/reversion/object_history.html:31 78 | msgid "This object doesn't have a change history. It probably wasn't added via this admin site." 79 | msgstr "У этого объекта нет истории изменений. Возможно, он был добавлен не через администраторский сайт." 80 | 81 | #: templates/reversion/recover_form.html:14 82 | #: templates/reversion/recover_list.html:6 83 | #: templates/reversion/revision_form.html:14 84 | msgid "Home" 85 | msgstr "Начало" 86 | 87 | #: templates/reversion/recover_form.html:17 88 | #, python-format 89 | msgid "Recover deleted %(verbose_name)s" 90 | msgstr "Восстановить удаленный %(verbose_name)s" 91 | 92 | #: templates/reversion/recover_form.html:18 93 | #, python-format 94 | msgid "Recover %(name)s" 95 | msgstr "Восстановить %(name)s" 96 | 97 | #: templates/reversion/recover_form.html:24 98 | msgid "Press the save button below to recover this version of the object." 99 | msgstr "Нажмите кнопку \"Сохранить\" далее, чтобы восстановить эту версию объекта." 100 | 101 | #: templates/reversion/recover_list.html:15 102 | msgid "Choose a date from the list below to recover a deleted version of an object." 103 | msgstr "Выберите дату из списка, чтобы восстановить удаленную версию объекта." 104 | 105 | #: templates/reversion/recover_list.html:35 106 | msgid "There are no deleted objects to recover." 107 | msgstr "Не найдено удаленных объектов для восстановления." 108 | 109 | #: templates/reversion/revision_form.html:18 110 | msgid "History" 111 | msgstr "История" 112 | 113 | #: templates/reversion/revision_form.html:19 114 | #, python-format 115 | msgid "Revert %(verbose_name)s" 116 | msgstr "Вернуть %(verbose_name)s" 117 | 118 | #: templates/reversion/revision_form.html:32 119 | msgid "Press the save button below to revert to this version of the object." 120 | msgstr "Нажмите кнопку \"Сохранить\" далее, чтобы вернуть эту версию объекта." 121 | 122 | -------------------------------------------------------------------------------- /reversion/locale/sk/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/sk/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/sk/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2014-01-14 19:05+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Juraj Bubniak \n" 14 | "Language-Team: Slovak \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n " 20 | ">= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n" 21 | 22 | #: admin.py:153 23 | msgid "Initial version." 24 | msgstr "Počiatočná verzia." 25 | 26 | #: admin.py:187 templates/reversion/change_list.html:7 27 | #: templates/reversion/recover_form.html:11 28 | #: templates/reversion/recover_list.html:11 29 | #, python-format 30 | msgid "Recover deleted %(name)s" 31 | msgstr "Obnoviť vymazaný %(name)s" 32 | 33 | #: admin.py:304 34 | #, python-format 35 | msgid "Reverted to previous version, saved on %(datetime)s" 36 | msgstr "Obnovená predošlá verzia, uložená %(datetime)s" 37 | 38 | #: admin.py:306 39 | #, python-format 40 | msgid "" 41 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 42 | "below." 43 | msgstr "" 44 | "Objekt %(model)s \"%(name)s\" bol úspešne obnovený. Môžete ho znovu upraviť " 45 | "nižšie." 46 | 47 | #: admin.py:391 48 | #, python-format 49 | msgid "Recover %(name)s" 50 | msgstr "Obnoviť %(name)s" 51 | 52 | #: admin.py:405 53 | #, python-format 54 | msgid "Revert %(name)s" 55 | msgstr "Vrátiť sa k predošlej verzii %(name)s" 56 | 57 | #: models.py:55 58 | msgid "date created" 59 | msgstr "dátum vytvorenia" 60 | 61 | #: models.py:61 62 | msgid "user" 63 | msgstr "používateľ" 64 | 65 | #: models.py:65 66 | msgid "comment" 67 | msgstr "komentár" 68 | 69 | #: templates/reversion/object_history.html:8 70 | msgid "" 71 | "Choose a date from the list below to revert to a previous version of this " 72 | "object." 73 | msgstr "" 74 | "Vyberte dátum z nižšie uvedeného zoznamu pre návrat k predošlej verzii tohto " 75 | "objektu." 76 | 77 | #: templates/reversion/object_history.html:15 78 | #: templates/reversion/recover_list.html:24 79 | msgid "Date/time" 80 | msgstr "Dátum/čas" 81 | 82 | #: templates/reversion/object_history.html:16 83 | msgid "User" 84 | msgstr "Používateľ" 85 | 86 | #: templates/reversion/object_history.html:17 87 | msgid "Comment" 88 | msgstr "Komentár" 89 | 90 | #: templates/reversion/object_history.html:36 91 | msgid "" 92 | "This object doesn't have a change history. It probably wasn't added via this " 93 | "admin site." 94 | msgstr "" 95 | "Tento objekt nemá históriu zmien. Pravdepodobne nebol pridaný cez túto " 96 | "admin stránku." 97 | 98 | #: templates/reversion/recover_form.html:8 99 | #: templates/reversion/recover_list.html:8 100 | #: templates/reversion/revision_form.html:8 101 | msgid "Home" 102 | msgstr "Domov" 103 | 104 | #: templates/reversion/recover_form.html:18 105 | msgid "Press the save button below to recover this version of the object." 106 | msgstr "Pre obnovenie tejto verzie objektu kliknite na tlačidlo uložiť nižšie." 107 | 108 | #: templates/reversion/recover_list.html:18 109 | msgid "" 110 | "Choose a date from the list below to recover a deleted version of an object." 111 | msgstr "" 112 | "Pre obnovenie vymazanej verzie objektu vyberte dátum z nižšie uvedeného zoznamu." 113 | 114 | #: templates/reversion/recover_list.html:38 115 | msgid "There are no deleted objects to recover." 116 | msgstr "Niesú dostupné žiadne vymazané objekty pre obnovenie." 117 | 118 | #: templates/reversion/revision_form.html:12 119 | msgid "History" 120 | msgstr "História" 121 | 122 | #: templates/reversion/revision_form.html:13 123 | #, python-format 124 | msgid "Revert %(verbose_name)s" 125 | msgstr "Vrátiť sa k predošlej verzii %(verbose_name)s" 126 | 127 | #: templates/reversion/revision_form.html:26 128 | msgid "Press the save button below to revert to this version of the object." 129 | msgstr "Pre návrat na túto verziu objektu kliknite na tlačidlo uložiť nižšie." 130 | -------------------------------------------------------------------------------- /reversion/locale/sl_SI/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/sl_SI/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/sl_SI/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2020-02-07 23:25+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: Bor Plestenjak \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: sl_SI\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" 20 | "%100==4 ? 2 : 3);\n" 21 | 22 | #: admin.py:66 23 | msgid "Initial version." 24 | msgstr "Začetna različica." 25 | 26 | #: admin.py:170 27 | #, python-format 28 | msgid "Reverted to previous version, saved on %(datetime)s" 29 | msgstr "Povrnjeno na prejšnjo različico, shranjeno %(datetime)s" 30 | 31 | #: admin.py:194 32 | #, python-format 33 | msgid "Recover %(name)s" 34 | msgstr "Obnovi %(name)s" 35 | 36 | #: admin.py:210 37 | #, python-format 38 | msgid "Revert %(name)s" 39 | msgstr "Povrni prejšnjo različico %(name)s" 40 | 41 | #: admin.py:245 templates/reversion/change_list.html:7 42 | #: templates/reversion/recover_form.html:10 43 | #: templates/reversion/recover_list.html:10 44 | #, python-format 45 | msgid "Recover deleted %(name)s" 46 | msgstr "Obnovi izbrisane %(name)s" 47 | 48 | #: models.py:33 49 | #, python-format 50 | msgid "Could not save %(object_repr)s version - missing dependency." 51 | msgstr "Različice %(object_repr)s ni bilo možno shraniti zaradi manjkajočih odvisnosti." 52 | 53 | #: models.py:46 54 | msgid "date created" 55 | msgstr "datum nastanka" 56 | 57 | #: models.py:55 58 | msgid "user" 59 | msgstr "uporabnik" 60 | 61 | #: models.py:61 62 | msgid "comment" 63 | msgstr "opomba" 64 | 65 | #: models.py:233 66 | #, python-format 67 | msgid "Could not load %(object_repr)s version - incompatible version data." 68 | msgstr "Različice %(object_repr)s ni bilo možno naložiti zaradi nezdružljivih podatkov." 69 | 70 | #: models.py:237 71 | #, python-format 72 | msgid "Could not load %(object_repr)s version - unknown serializer %(format)s." 73 | msgstr "Različice %(object_repr)s ni bilo možno naložiti zaradi neznane serializacije %(format)s." 74 | 75 | #: templates/reversion/object_history.html:8 76 | msgid "" 77 | "Choose a date from the list below to revert to a previous version of this " 78 | "object." 79 | msgstr "Za povrnitev objekta na prejšnjo različico izberi datum iz spodnjega seznama." 80 | 81 | #: templates/reversion/object_history.html:15 82 | #: templates/reversion/recover_list.html:23 83 | msgid "Date/time" 84 | msgstr "Datum/čas" 85 | 86 | #: templates/reversion/object_history.html:16 87 | msgid "User" 88 | msgstr "Uporabnik" 89 | 90 | #: templates/reversion/object_history.html:17 91 | msgid "Action" 92 | msgstr "Dejanje" 93 | 94 | #: templates/reversion/object_history.html:38 95 | msgid "" 96 | "This object doesn't have a change history. It probably wasn't added via this " 97 | "admin site." 98 | msgstr "Ta objekt nima zgodovine sprememb. Verjetno ni bil dodan preko te strani za administracijo." 99 | 100 | #: templates/reversion/recover_form.html:7 101 | #: templates/reversion/recover_list.html:7 102 | #: templates/reversion/revision_form.html:7 103 | msgid "Home" 104 | msgstr "Domov" 105 | 106 | #: templates/reversion/recover_form.html:20 107 | msgid "Press the save button below to recover this version of the object." 108 | msgstr "Za obnovitev te različice objekta spodaj pritisni gumb shrani." 109 | 110 | #: templates/reversion/recover_list.html:17 111 | msgid "" 112 | "Choose a date from the list below to recover a deleted version of an object." 113 | msgstr "Za obnovitev izbrisanega objekta izberi datum iz spodnjega seznama." 114 | 115 | #: templates/reversion/recover_list.html:37 116 | msgid "There are no deleted objects to recover." 117 | msgstr "Ni izbrisanih objektov za obnovitev." 118 | 119 | #: templates/reversion/revision_form.html:11 120 | msgid "History" 121 | msgstr "Zgodovina" 122 | 123 | #: templates/reversion/revision_form.html:12 124 | #, python-format 125 | msgid "Revert %(verbose_name)s" 126 | msgstr "Povrni %(verbose_name)s" 127 | 128 | #: templates/reversion/revision_form.html:21 129 | msgid "Press the save button below to revert to this version of the object." 130 | msgstr "Za povrnitev te različice objekta spodaj pritisni gumb shrani." 131 | 132 | -------------------------------------------------------------------------------- /reversion/locale/sv/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/sv/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/sv/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2012-06-13 09:56+0200\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 20 | 21 | #: admin.py:139 22 | msgid "Initial version." 23 | msgstr "Första versionen." 24 | 25 | #: admin.py:161 26 | #, python-format 27 | msgid "Deleted %(verbose_name)s." 28 | msgstr "Tog bort %(verbose_name)s" 29 | 30 | #: admin.py:181 templates/reversion/change_list.html:7 31 | #: templates/reversion/recover_form.html:10 32 | #: templates/reversion/recover_list.html:10 33 | #, python-format 34 | msgid "Recover deleted %(name)s" 35 | msgstr "Återskapa bortagna %(name)s" 36 | 37 | #: admin.py:292 38 | #, python-format 39 | msgid "Reverted to previous version, saved on %(datetime)s" 40 | msgstr "Återgick till föregående version, sparad %(datetime)s" 41 | 42 | #: admin.py:292 admin.py:456 templates/reversion/object_history.html:23 43 | #: templates/reversion/recover_list.html:30 44 | msgid "DATETIME_FORMAT" 45 | msgstr "Y-m-d H:i" 46 | 47 | #: admin.py:294 48 | #, python-format 49 | msgid "" 50 | "The %(model)s \"%(name)s\" was reverted successfully. You may edit it again " 51 | "below." 52 | msgstr "" 53 | "Återställandet av %(model)s \"%(name)s\" lyckades. Du kan redigera den igen " 54 | "här nedan." 55 | 56 | #: admin.py:377 57 | #, python-format 58 | msgid "Recover %(name)s" 59 | msgstr "Återskapa %(name)s" 60 | 61 | #: admin.py:388 62 | #, python-format 63 | msgid "Revert %(name)s" 64 | msgstr "Återställ %(name)s" 65 | 66 | #: models.py:68 67 | msgid "date created" 68 | msgstr "datum skapad" 69 | 70 | #: models.py:74 71 | msgid "user" 72 | msgstr "användare" 73 | 74 | #: models.py:78 75 | msgid "comment" 76 | msgstr "kommentar" 77 | 78 | #: templates/reversion/object_history.html:8 79 | msgid "" 80 | "Choose a date from the list below to revert to a previous version of this " 81 | "object." 82 | msgstr "" 83 | "Välj ett datum från listan här under för att återställa till en tidigare " 84 | "version av det här objektet." 85 | 86 | #: templates/reversion/object_history.html:15 87 | #: templates/reversion/recover_list.html:23 88 | msgid "Date/time" 89 | msgstr "Datum/tid" 90 | 91 | #: templates/reversion/object_history.html:16 92 | msgid "User" 93 | msgstr "Användare" 94 | 95 | #: templates/reversion/object_history.html:17 96 | msgid "Comment" 97 | msgstr "Kommentar" 98 | 99 | #: templates/reversion/object_history.html:36 100 | msgid "" 101 | "This object doesn't have a change history. It probably wasn't added via this " 102 | "admin site." 103 | msgstr "" 104 | "Det här objektet saknar ändringshistorik. Det skapades förmodligen inte via " 105 | "den här admin-sajten." 106 | 107 | #: templates/reversion/recover_form.html:7 108 | #: templates/reversion/recover_list.html:7 109 | #: templates/reversion/revision_form.html:7 110 | msgid "Home" 111 | msgstr "Hem" 112 | 113 | #: templates/reversion/recover_form.html:17 114 | msgid "Press the save button below to recover this version of the object." 115 | msgstr "Tryck på spara här nedan fär att återskapa den här versionen." 116 | 117 | #: templates/reversion/recover_list.html:17 118 | msgid "" 119 | "Choose a date from the list below to recover a deleted version of an object." 120 | msgstr "" 121 | "Välj ett datum i listan här nedan för att återskapa en borttagen version." 122 | 123 | #: templates/reversion/recover_list.html:37 124 | msgid "There are no deleted objects to recover." 125 | msgstr "Det finns inga borttagna objekt att återskapa." 126 | 127 | #: templates/reversion/revision_form.html:11 128 | msgid "History" 129 | msgstr "Historik" 130 | 131 | #: templates/reversion/revision_form.html:12 132 | #, python-format 133 | msgid "Revert %(verbose_name)s" 134 | msgstr "Återställ %(verbose_name)s" 135 | 136 | #: templates/reversion/revision_form.html:25 137 | msgid "Press the save button below to revert to this version of the object." 138 | msgstr "Tryck på spara här nedan för att återställa den här versionen." 139 | -------------------------------------------------------------------------------- /reversion/locale/uk/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/uk/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/uk/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Translation of django-reversion into Ukrainian. 2 | # This file is distributed under the same license as the django-reversion package. 3 | # Illia Volochii , 2017. 4 | msgid "" 5 | msgstr "" 6 | "Project-Id-Version: django-reversion\n" 7 | "Report-Msgid-Bugs-To: https://github.com/etianen/django-reversion/issues\n" 8 | "POT-Creation-Date: 2017-11-03 12:02+0200\n" 9 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 10 | "Last-Translator: Illia Volochii \n" 11 | "Language-Team: Ukrainian\n" 12 | "Language: uk\n" 13 | "MIME-Version: 1.0\n" 14 | "Content-Type: text/plain; charset=UTF-8\n" 15 | "Content-Transfer-Encoding: 8bit\n" 16 | "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != " 17 | "11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % " 18 | "100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " 19 | "(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" 20 | #: reversion/admin.py:83 21 | msgid "Initial version." 22 | msgstr "Початкова версія." 23 | 24 | #: reversion/admin.py:197 25 | #, python-format 26 | msgid "Reverted to previous version, saved on %(datetime)s" 27 | msgstr "Повернуто до попередньої версії, яка збережена %(datetime)s" 28 | 29 | #: reversion/admin.py:221 30 | #, python-format 31 | msgid "Recover %(name)s" 32 | msgstr "Відновити %(name)s" 33 | 34 | #: reversion/admin.py:237 35 | #, python-format 36 | msgid "Revert %(name)s" 37 | msgstr "Повернути %(name)s" 38 | 39 | #: reversion/admin.py:272 reversion/templates/reversion/change_list.html:7 40 | #: reversion/templates/reversion/recover_form.html:10 41 | #: reversion/templates/reversion/recover_list.html:10 42 | #, python-format 43 | msgid "Recover deleted %(name)s" 44 | msgstr "Відновити видалені %(name)s" 45 | 46 | #: reversion/models.py:31 47 | #, python-format 48 | msgid "Could not save %(object_repr)s version - missing dependency." 49 | msgstr "Неможливо зберегти версію \"%(object_repr)s\" - відсутня залежність." 50 | 51 | #: reversion/models.py:45 52 | msgid "date created" 53 | msgstr "дата створення" 54 | 55 | #: reversion/models.py:54 56 | msgid "user" 57 | msgstr "користувач" 58 | 59 | #: reversion/models.py:60 60 | msgid "comment" 61 | msgstr "коментар" 62 | 63 | #: reversion/models.py:242 64 | #, python-format 65 | msgid "Could not load %(object_repr)s version - incompatible version data." 66 | msgstr "" 67 | "Неможливо завантажити версію \"%(object_repr)s\" - несумісні дані версій." 68 | 69 | #: reversion/models.py:246 70 | #, python-format 71 | msgid "Could not load %(object_repr)s version - unknown serializer %(format)s." 72 | msgstr "" 73 | "Неможливо завантажити версію \"%(object_repr)s\" - невідомий серіалізатор " 74 | "%(format)s." 75 | 76 | #: reversion/templates/reversion/object_history.html:8 77 | msgid "" 78 | "Choose a date from the list below to revert to a previous version of this " 79 | "object." 80 | msgstr "" 81 | "Виберіть дату із списку нижче, щоб повернутися до попередньої версії цього " 82 | "об'єкта." 83 | 84 | #: reversion/templates/reversion/object_history.html:15 85 | #: reversion/templates/reversion/recover_list.html:23 86 | msgid "Date/time" 87 | msgstr "Дата/час" 88 | 89 | #: reversion/templates/reversion/object_history.html:16 90 | msgid "User" 91 | msgstr "Користувач" 92 | 93 | #: reversion/templates/reversion/object_history.html:17 94 | msgid "Action" 95 | msgstr "Дія" 96 | 97 | #: reversion/templates/reversion/object_history.html:38 98 | msgid "" 99 | "This object doesn't have a change history. It probably wasn't added via this " 100 | "admin site." 101 | msgstr "" 102 | "Цей об'єкт не має історії змін. Напевно, він був доданий не через цей сайт " 103 | "адміністрування." 104 | 105 | #: reversion/templates/reversion/recover_form.html:7 106 | #: reversion/templates/reversion/recover_list.html:7 107 | #: reversion/templates/reversion/revision_form.html:7 108 | msgid "Home" 109 | msgstr "Домівка" 110 | 111 | #: reversion/templates/reversion/recover_form.html:20 112 | msgid "Press the save button below to recover this version of the object." 113 | msgstr "Натисніть кнопку \"Зберегти\" нижче, щоб відновити цю версію об'єкта." 114 | 115 | #: reversion/templates/reversion/recover_list.html:17 116 | msgid "" 117 | "Choose a date from the list below to recover a deleted version of an object." 118 | msgstr "Виберіть дату із списку нижче, щоб відновити видалену версію об'єкта." 119 | 120 | #: reversion/templates/reversion/recover_list.html:37 121 | msgid "There are no deleted objects to recover." 122 | msgstr "Не знайдено видалених об'єктів для відновлення." 123 | 124 | #: reversion/templates/reversion/revision_form.html:11 125 | msgid "History" 126 | msgstr "Історія" 127 | 128 | #: reversion/templates/reversion/revision_form.html:12 129 | #, python-format 130 | msgid "Revert %(verbose_name)s" 131 | msgstr "Повернути %(verbose_name)s" 132 | 133 | #: reversion/templates/reversion/revision_form.html:21 134 | msgid "Press the save button below to revert to this version of the object." 135 | msgstr "" 136 | "Натисніть кнопку \"Зберегти\" нижче, щоб повернутися до цієї версії об'єкта." 137 | -------------------------------------------------------------------------------- /reversion/locale/zh_Hans/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/locale/zh_Hans/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /reversion/locale/zh_Hans/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: PACKAGE VERSION\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2024-01-25 16:26+0800\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "Plural-Forms: nplurals=1; plural=0;\n" 20 | 21 | #: .\admin.py:70 22 | msgid "Initial version." 23 | msgstr "初始版本" 24 | 25 | #: .\admin.py:178 26 | #, python-format 27 | msgid "Reverted to previous version, saved on %(datetime)s" 28 | msgstr "恢复到 %(datetime)s 的版本" 29 | 30 | #: .\admin.py:186 31 | #, python-format 32 | msgid "Could not load %(object_repr)s version - not found" 33 | msgstr "无法载入 %(object_repr)s 版本 - 未找到" 34 | 35 | #: .\admin.py:206 36 | #, python-format 37 | msgid "Recover %(name)s" 38 | msgstr "恢复 %(name)s" 39 | 40 | #: .\admin.py:222 41 | #, python-format 42 | msgid "Revert %(name)s" 43 | msgstr "恢复 %(name)s" 44 | 45 | #: .\admin.py:259 .\templates\reversion\change_list.html:7 46 | #: .\templates\reversion\recover_form.html:10 47 | #: .\templates\reversion\recover_list.html:10 48 | #, python-format 49 | msgid "Recover deleted %(name)s" 50 | msgstr "恢复已删除的 %(name)s" 51 | 52 | #: .\apps.py:7 53 | msgid "Reversion" 54 | msgstr "版本记录" 55 | 56 | #: .\models.py:39 57 | #, python-format 58 | msgid "Could not save %(object_repr)s version - missing dependency." 59 | msgstr "无法保存 %(object_repr)s 版本 - 缺少依赖" 60 | 61 | #: .\models.py:52 62 | msgid "date created" 63 | msgstr "创建日期" 64 | 65 | #: .\models.py:61 66 | msgid "user" 67 | msgstr "用户" 68 | 69 | #: .\models.py:67 70 | msgid "comment" 71 | msgstr "评论" 72 | 73 | #: .\models.py:116 74 | msgid "revision" 75 | msgstr "修改" 76 | 77 | #: .\models.py:117 78 | msgid "revisions" 79 | msgstr "修改" 80 | 81 | #: .\models.py:275 82 | #, python-format 83 | msgid "Could not load %(object_repr)s version - incompatible version data." 84 | msgstr "无法载入 %(object_repr)s 版本 - 不兼容的版本数据。" 85 | 86 | #: .\models.py:279 87 | #, python-format 88 | msgid "Could not load %(object_repr)s version - unknown serializer %(format)s." 89 | msgstr "无法载入 %(object_repr)s 版本 - 未知的序列化 %(format)s。" 90 | 91 | #: .\models.py:335 92 | #, fuzzy 93 | msgid "version" 94 | msgstr "版本" 95 | 96 | #: .\models.py:336 97 | msgid "versions" 98 | msgstr "版本" 99 | 100 | #: .\templates\reversion\object_history.html:8 101 | msgid "" 102 | "Choose a date from the list below to revert to a previous version of this " 103 | "object." 104 | msgstr "单击下方的日期以恢复当前对象到之前的版本。" 105 | 106 | #: .\templates\reversion\object_history.html:15 107 | #: .\templates\reversion\recover_list.html:23 108 | msgid "Date/time" 109 | msgstr "时间" 110 | 111 | #: .\templates\reversion\object_history.html:16 112 | msgid "User" 113 | msgstr "用户" 114 | 115 | #: .\templates\reversion\object_history.html:17 116 | msgid "Action" 117 | msgstr "操作" 118 | 119 | #: .\templates\reversion\object_history.html:38 120 | msgid "" 121 | "This object doesn't have a change history. It probably wasn't added via this " 122 | "admin site." 123 | msgstr "此对象不存在任何变更历史,它可能不是通过管理站点添加的。" 124 | 125 | #: .\templates\reversion\recover_form.html:7 126 | #: .\templates\reversion\recover_list.html:7 127 | #: .\templates\reversion\revision_form.html:7 128 | msgid "Home" 129 | msgstr "首页" 130 | 131 | #: .\templates\reversion\recover_form.html:20 132 | msgid "Press the save button below to recover this version of the object." 133 | msgstr "单击保存按钮以恢复为此版本。" 134 | 135 | #: .\templates\reversion\recover_list.html:17 136 | msgid "" 137 | "Choose a date from the list below to recover a deleted version of an object." 138 | msgstr "单击下方的日期以恢复一个已删除的对象。" 139 | 140 | #: .\templates\reversion\recover_list.html:37 141 | msgid "There are no deleted objects to recover." 142 | msgstr "没有可供恢复的已删除对象。" 143 | 144 | #: .\templates\reversion\revision_form.html:11 145 | msgid "History" 146 | msgstr "历史" 147 | 148 | #: .\templates\reversion\revision_form.html:12 149 | #, python-format 150 | msgid "Revert %(verbose_name)s" 151 | msgstr "恢复 %(verbose_name)s" 152 | 153 | #: .\templates\reversion\revision_form.html:21 154 | msgid "Press the save button below to revert to this version of the object." 155 | msgstr "单击保存按钮将此对象恢复到此版本。" 156 | -------------------------------------------------------------------------------- /reversion/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/management/__init__.py -------------------------------------------------------------------------------- /reversion/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | from django.apps import apps 2 | from django.conf import settings 3 | from django.contrib import admin 4 | from django.core.management.base import BaseCommand, CommandError 5 | from reversion.revisions import is_registered 6 | 7 | 8 | class BaseRevisionCommand(BaseCommand): 9 | 10 | def add_arguments(self, parser): 11 | super().add_arguments(parser) 12 | parser.add_argument( 13 | "app_label", 14 | metavar="app_label", 15 | nargs="*", 16 | help="Optional app_label or app_label.model_name list.", 17 | ) 18 | parser.add_argument( 19 | "--using", 20 | default=None, 21 | help="The database to query for revision data.", 22 | ) 23 | parser.add_argument( 24 | "--model-db", 25 | default=None, 26 | help="The database to query for model data.", 27 | ) 28 | 29 | def get_models(self, options): 30 | # Load admin classes. 31 | if "django.contrib.admin" in settings.INSTALLED_APPS: 32 | admin.autodiscover() 33 | # Get options. 34 | app_labels = options["app_label"] 35 | # Parse model classes. 36 | if len(app_labels) == 0: 37 | selected_models = apps.get_models() 38 | else: 39 | selected_models = set() 40 | for label in app_labels: 41 | if "." in label: 42 | # This is an app.Model specifier. 43 | try: 44 | model = apps.get_model(label) 45 | except LookupError: 46 | raise CommandError(f"Unknown model: {label}") 47 | selected_models.add(model) 48 | else: 49 | # This is just an app - no model qualifier. 50 | app_label = label 51 | try: 52 | app = apps.get_app_config(app_label) 53 | except LookupError: 54 | raise CommandError(f"Unknown app: {app_label}") 55 | selected_models.update(app.get_models()) 56 | for model in selected_models: 57 | if is_registered(model): 58 | yield model 59 | -------------------------------------------------------------------------------- /reversion/management/commands/createinitialrevisions.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.apps import apps 4 | from django.core.management import CommandError 5 | from django.db import connections, reset_queries, transaction, router 6 | from reversion.models import Revision, Version, _safe_subquery 7 | from reversion.management.commands import BaseRevisionCommand 8 | from reversion.revisions import create_revision, set_comment, add_to_revision, add_meta 9 | 10 | 11 | class Command(BaseRevisionCommand): 12 | 13 | help = "Creates initial revisions for a given app [and model]." 14 | 15 | def add_arguments(self, parser): 16 | super().add_arguments(parser) 17 | parser.add_argument( 18 | "--comment", 19 | action="store", 20 | default="Initial version.", 21 | help="Specify the comment to add to the revisions. Defaults to 'Initial version'.") 22 | parser.add_argument( 23 | "--batch-size", 24 | action="store", 25 | type=int, 26 | default=500, 27 | help="For large sets of data, revisions will be populated in batches. Defaults to 500.", 28 | ) 29 | parser.add_argument( 30 | "--meta", 31 | action="store", 32 | default={}, 33 | type=json.loads, 34 | help=("Specify meta models and corresponding values for each initial revision as JSON" 35 | "eg. --meta \"{\"core.RevisionMeta\", {\"hello\": \"world\"}}\""), 36 | ) 37 | 38 | def handle(self, *app_labels, **options): 39 | verbosity = options["verbosity"] 40 | using = options["using"] 41 | model_db = options["model_db"] 42 | comment = options["comment"] 43 | batch_size = options["batch_size"] 44 | meta = options["meta"] 45 | meta_models = [] 46 | for label in meta.keys(): 47 | try: 48 | model = apps.get_model(label) 49 | meta_models.append(model) 50 | except LookupError: 51 | raise CommandError(f"Unknown model: {label}") 52 | meta_values = meta.values() 53 | # Determine if we should use queryset.iterator() 54 | using = using or router.db_for_write(Revision) 55 | server_side_cursors = not connections[using].settings_dict.get('DISABLE_SERVER_SIDE_CURSORS') 56 | use_iterator = connections[using].vendor in ("postgresql",) and server_side_cursors 57 | # Create revisions. 58 | with transaction.atomic(using=using): 59 | for model in self.get_models(options): 60 | # Check all models for empty revisions. 61 | if verbosity >= 1: 62 | self.stdout.write("Creating revisions for {name}".format( 63 | name=model._meta.verbose_name, 64 | )) 65 | created_count = 0 66 | live_objs = _safe_subquery( 67 | "exclude", 68 | model._default_manager.using(model_db), 69 | model._meta.pk.name, 70 | Version.objects.using(using).get_for_model( 71 | model, 72 | model_db=model_db, 73 | ), 74 | "object_id", 75 | ) 76 | live_objs = live_objs.order_by() 77 | # Save all the versions. 78 | if use_iterator: 79 | total = live_objs.count() 80 | if total: 81 | for obj in live_objs.iterator(batch_size): 82 | self.create_revision(obj, using, meta, meta_models, meta_values, comment, model_db) 83 | created_count += 1 84 | # Print out a message every batch_size if feeling extra verbose 85 | if not created_count % batch_size: 86 | self.batch_complete(verbosity, created_count, total) 87 | else: 88 | # Save all the versions. 89 | ids = list(live_objs.values_list("pk", flat=True)) 90 | total = len(ids) 91 | for i in range(0, total, batch_size): 92 | chunked_ids = ids[i:i+batch_size] 93 | objects = live_objs.in_bulk(chunked_ids) 94 | for obj in objects.values(): 95 | self.create_revision(obj, using, meta, meta_models, meta_values, comment, model_db) 96 | created_count += 1 97 | # Print out a message every batch_size if feeling extra verbose 98 | self.batch_complete(verbosity, created_count, total) 99 | 100 | # Print out a message, if feeling verbose. 101 | if verbosity >= 1: 102 | self.stdout.write("- Created {total} / {total}".format( 103 | total=total, 104 | )) 105 | 106 | def create_revision(self, obj, using, meta, meta_models, meta_values, comment, model_db): 107 | with create_revision(using=using): 108 | if meta: 109 | for model, values in zip(meta_models, meta_values): 110 | add_meta(model, **values) 111 | set_comment(comment) 112 | add_to_revision(obj, model_db=model_db) 113 | 114 | def batch_complete(self, verbosity, created_count, total): 115 | reset_queries() 116 | if verbosity >= 2: 117 | self.stdout.write("- Created {created_count} / {total}".format( 118 | created_count=created_count, 119 | total=total, 120 | )) 121 | -------------------------------------------------------------------------------- /reversion/management/commands/deleterevisions.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | from django.db import transaction, models, router 3 | from django.utils import timezone 4 | from reversion.models import Revision, Version 5 | from reversion.management.commands import BaseRevisionCommand 6 | 7 | 8 | class Command(BaseRevisionCommand): 9 | 10 | help = "Deletes revisions for a given app [and model]." 11 | 12 | def add_arguments(self, parser): 13 | super().add_arguments(parser) 14 | parser.add_argument( 15 | "--days", 16 | default=0, 17 | type=int, 18 | help="Delete only revisions older than the specified number of days.", 19 | ) 20 | parser.add_argument( 21 | "--keep", 22 | default=0, 23 | type=int, 24 | help="Keep the specified number of revisions (most recent) for each object.", 25 | ) 26 | 27 | def handle(self, *app_labels, **options): 28 | verbosity = options["verbosity"] 29 | using = options["using"] 30 | model_db = options["model_db"] 31 | days = options["days"] 32 | keep = options["keep"] 33 | # Delete revisions. 34 | using = using or router.db_for_write(Revision) 35 | with transaction.atomic(using=using): 36 | revision_query = models.Q() 37 | keep_revision_ids = set() 38 | # By default, delete nothing. 39 | can_delete = False 40 | # Get all revisions for the given revision manager and model. 41 | for model in self.get_models(options): 42 | if verbosity >= 1: 43 | self.stdout.write("Finding stale revisions for {name}".format( 44 | name=model._meta.verbose_name, 45 | )) 46 | # Find all matching revision IDs. 47 | model_query = Version.objects.using(using).get_for_model( 48 | model, 49 | model_db=model_db, 50 | ) 51 | if keep: 52 | overflow_object_ids = list(Version.objects.using(using).get_for_model( 53 | model, 54 | model_db=model_db, 55 | ).order_by().values_list("object_id").annotate( 56 | count=models.Count("object_id"), 57 | ).filter( 58 | count__gt=keep, 59 | ).values_list("object_id", flat=True).iterator()) 60 | # Only delete overflow revisions. 61 | model_query = model_query.filter(object_id__in=overflow_object_ids) 62 | for object_id in overflow_object_ids: 63 | if verbosity >= 2: 64 | self.stdout.write("- Finding stale revisions for {name} #{object_id}".format( 65 | name=model._meta.verbose_name, 66 | object_id=object_id, 67 | )) 68 | # But keep the underflow revisions. 69 | keep_revision_ids.update(Version.objects.using(using).get_for_object_reference( 70 | model, 71 | object_id, 72 | model_db=model_db, 73 | ).values_list("revision_id", flat=True)[:keep].iterator()) 74 | # Add to revision query. 75 | revision_query |= models.Q( 76 | pk__in=model_query.order_by().values_list("revision_id", flat=True) 77 | ) 78 | # If we have at least one model, then we can delete. 79 | can_delete = True 80 | if can_delete: 81 | revisions_to_delete = Revision.objects.using(using).filter( 82 | revision_query, 83 | date_created__lt=timezone.now() - timedelta(days=days), 84 | ).exclude( 85 | pk__in=keep_revision_ids 86 | ).order_by() 87 | else: 88 | revisions_to_delete = Revision.objects.using(using).none() 89 | # Print out a message, if feeling verbose. 90 | if verbosity >= 1: 91 | self.stdout.write("Deleting {total} revisions...".format( 92 | total=revisions_to_delete.count(), 93 | )) 94 | revisions_to_delete.delete() 95 | -------------------------------------------------------------------------------- /reversion/middleware.py: -------------------------------------------------------------------------------- 1 | from reversion.views import _request_creates_revision, create_revision 2 | 3 | 4 | class RevisionMiddleware: 5 | 6 | """Wraps the entire request in a revision.""" 7 | 8 | manage_manually = False 9 | 10 | using = None 11 | 12 | atomic = True 13 | 14 | def __init__(self, get_response): 15 | self.get_response = create_revision( 16 | manage_manually=self.manage_manually, 17 | using=self.using, 18 | atomic=self.atomic, 19 | request_creates_revision=self.request_creates_revision 20 | )(get_response) 21 | 22 | def request_creates_revision(self, request): 23 | return _request_creates_revision(request) 24 | 25 | def __call__(self, request): 26 | return self.get_response(request) 27 | -------------------------------------------------------------------------------- /reversion/migrations/0001_squashed_0004_auto_20160611_1202.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.9.7 on 2016-06-06 13:22 2 | from django.conf import settings 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('contenttypes', '0001_initial'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Revision', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('date_created', models.DateTimeField(db_index=True, help_text='The date and time this revision was created.', verbose_name='date created')), 22 | ('comment', models.TextField(blank=True, help_text='A text comment on this revision.', verbose_name='comment')), 23 | ('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')), 24 | ], 25 | options={ 26 | "ordering": ("-pk",), 27 | 'verbose_name': 'revision', 28 | 'verbose_name_plural': 'revisions', 29 | }, 30 | ), 31 | migrations.CreateModel( 32 | name='Version', 33 | fields=[ 34 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 35 | ('object_id', models.CharField(help_text='Primary key of the model under version control.', max_length=191)), 36 | ('format', models.CharField(help_text='The serialization format used by this model.', max_length=255)), 37 | ('serialized_data', models.TextField(help_text='The serialized form of this version of the model.')), 38 | ('object_repr', models.TextField(help_text='A string representation of the object.')), 39 | ('content_type', models.ForeignKey(help_text='Content type of the model under version control.', on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), 40 | ('revision', models.ForeignKey(help_text='The revision that contains this version.', on_delete=django.db.models.deletion.CASCADE, to='reversion.Revision')), 41 | ('db', models.CharField(help_text='The database the model under version control is stored in.', max_length=191)), 42 | ], 43 | options={ 44 | "ordering": ("-pk",), 45 | 'verbose_name': 'version', 46 | 'verbose_name_plural': 'versions', 47 | }, 48 | ), 49 | migrations.AlterUniqueTogether( 50 | name='version', 51 | unique_together={('db', 'content_type', 'object_id', 'revision')}, 52 | ), 53 | ] 54 | -------------------------------------------------------------------------------- /reversion/migrations/0002_add_index_on_version_for_content_type_and_db.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.6 on 2021-08-16 18:08 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('reversion', '0001_squashed_0004_auto_20160611_1202'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddIndex( 14 | model_name='version', 15 | index=models.Index(fields=['content_type', 'db'], name='reversion_v_content_f95daf_idx'), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /reversion/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/reversion/migrations/__init__.py -------------------------------------------------------------------------------- /reversion/signals.py: -------------------------------------------------------------------------------- 1 | from django.dispatch.dispatcher import Signal 2 | 3 | # Signal arguments: revision and versions 4 | pre_revision_commit = Signal() 5 | post_revision_commit = Signal() 6 | -------------------------------------------------------------------------------- /reversion/templates/reversion/change_list.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_list.html" %} 2 | {% load i18n admin_urls %} 3 | 4 | 5 | {% block object-tools-items %} 6 | {% if not is_popup and has_add_permission and has_change_permission %} 7 |
  • {% blocktrans with cl.opts.verbose_name_plural|escape as name %}Recover deleted {{name}}{% endblocktrans %}
  • 8 | {% endif %} 9 | {{block.super}} 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /reversion/templates/reversion/object_history.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/object_history.html" %} 2 | {% load i18n %} 3 | 4 | 5 | {% block content %} 6 |
    7 | 8 |

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

    9 | 10 |
    11 | {% if action_list %} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% for action in action_list %} 22 | 23 | 24 | 32 | 33 | 34 | {% endfor %} 35 | 36 |
    {% trans 'Date/time' %}{% trans 'User' %}{% trans 'Action' %}
    {{action.revision.date_created|date:"DATETIME_FORMAT"}} 25 | {% if action.revision.user %} 26 | {{action.revision.user.get_username}} 27 | {% if action.revision.user.get_full_name %} ({{action.revision.user.get_full_name}}){% endif %} 28 | {% else %} 29 | — 30 | {% endif %} 31 | {{action.revision.get_comment|linebreaksbr|default:""}}
    37 | {% else %} 38 |

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

    39 | {% endif %} 40 |
    41 |
    42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /reversion/templates/reversion/recover_form.html: -------------------------------------------------------------------------------- 1 | {% extends "reversion/revision_form.html" %} 2 | {% load i18n admin_urls %} 3 | 4 | 5 | {% block breadcrumbs %} 6 | 13 | {% endblock %} 14 | 15 | 16 | {% block object-tools %}{% endblock %} 17 | 18 | 19 | {% block form_top %} 20 |

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

    21 | {% endblock %} 22 | 23 | 24 | {% block submit_buttons_top %}{% with is_popup=1 %}{{block.super}}{% endwith %}{% endblock %} 25 | {% block submit_buttons_bottom %}{% with is_popup=1 %}{{block.super}}{% endwith %}{% endblock %} 26 | -------------------------------------------------------------------------------- /reversion/templates/reversion/recover_list.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n l10n admin_urls %} 3 | 4 | 5 | {% block breadcrumbs %} 6 | 12 | {% endblock %} 13 | 14 | 15 | {% block content %} 16 |
    17 |

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

    18 |
    19 | {% if deleted %} 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% for deletion in deleted %} 29 | 30 | 31 | 32 | 33 | {% endfor %} 34 | 35 |
    {% trans 'Date/time' %}{{opts.verbose_name|capfirst}}
    {{deletion.revision.date_created}}{{deletion.object_repr}}
    36 | {% else %} 37 |

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

    38 | {% endif %} 39 |
    40 |
    41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /reversion/templates/reversion/revision_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_form.html" %} 2 | {% load i18n admin_urls %} 3 | 4 | 5 | {% block breadcrumbs %} 6 | 14 | {% endblock %} 15 | 16 | 17 | {% block object-tools %}{% endblock %} 18 | 19 | 20 | {% block form_top %} 21 |

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

    22 | {% endblock %} 23 | 24 | 25 | {% block submit_buttons_top %}{% with is_popup=1 %}{{block.super}}{% endwith %}{% endblock %} 26 | {% block submit_buttons_bottom %}{% with is_popup=1 %}{{block.super}}{% endwith %}{% endblock %} 27 | -------------------------------------------------------------------------------- /reversion/views.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | from reversion.revisions import create_revision as create_revision_base, set_user, get_user 4 | 5 | 6 | def _request_creates_revision(request): 7 | return request.method not in ("OPTIONS", "GET", "HEAD") 8 | 9 | 10 | def _set_user_from_request(request): 11 | if getattr(request, "user", None) and request.user.is_authenticated and get_user() is None: 12 | set_user(request.user) 13 | 14 | 15 | def create_revision(manage_manually=False, using=None, atomic=True, request_creates_revision=None): 16 | """ 17 | View decorator that wraps the request in a revision. 18 | 19 | The revision will have it's user set from the request automatically. 20 | """ 21 | request_creates_revision = request_creates_revision or _request_creates_revision 22 | 23 | def decorator(func): 24 | @wraps(func) 25 | def do_revision_view(request, *args, **kwargs): 26 | if request_creates_revision(request): 27 | with create_revision_base(manage_manually=manage_manually, using=using, atomic=atomic): 28 | response = func(request, *args, **kwargs) 29 | _set_user_from_request(request) 30 | return response 31 | return func(request, *args, **kwargs) 32 | return do_revision_view 33 | return decorator 34 | 35 | 36 | class RevisionMixin: 37 | 38 | """ 39 | A class-based view mixin that wraps the request in a revision. 40 | 41 | The revision will have it's user set from the request automatically. 42 | """ 43 | 44 | revision_manage_manually = False 45 | 46 | revision_using = None 47 | 48 | revision_atomic = True 49 | 50 | def __init__(self, *args, **kwargs): 51 | super().__init__(*args, **kwargs) 52 | self.dispatch = create_revision( 53 | manage_manually=self.revision_manage_manually, 54 | using=self.revision_using, 55 | atomic=self.revision_atomic, 56 | request_creates_revision=self.revision_request_creates_revision 57 | )(self.dispatch) 58 | 59 | def revision_request_creates_revision(self, request): 60 | return _request_creates_revision(request) 61 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length=120 3 | exclude=venv,migrations 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | from reversion import __version__ 4 | 5 | # Load in babel support, if available. 6 | try: 7 | from babel.messages import frontend as babel 8 | 9 | cmdclass = { 10 | "compile_catalog": babel.compile_catalog, 11 | "extract_messages": babel.extract_messages, 12 | "init_catalog": babel.init_catalog, 13 | "update_catalog": babel.update_catalog, 14 | } 15 | except ImportError: 16 | cmdclass = {} 17 | 18 | 19 | def read(filepath): 20 | with open(filepath, encoding="utf-8") as f: 21 | return f.read() 22 | 23 | 24 | setup( 25 | name="django-reversion", 26 | version=".".join(str(x) for x in __version__), 27 | license="BSD", 28 | description="An extension to the Django web framework that provides version control for model instances.", 29 | long_description=read("README.rst"), 30 | author="Dave Hall", 31 | author_email="dave@etianen.com", 32 | url="https://github.com/etianen/django-reversion", 33 | zip_safe=False, 34 | packages=find_packages(), 35 | package_data={ 36 | "reversion": ["locale/*/LC_MESSAGES/django.*", "templates/reversion/*.html"] 37 | }, 38 | cmdclass=cmdclass, 39 | install_requires=[ 40 | "django>=4.2", 41 | ], 42 | python_requires=">=3.8", 43 | classifiers=[ 44 | "Development Status :: 5 - Production/Stable", 45 | "Environment :: Web Environment", 46 | "Intended Audience :: Developers", 47 | "License :: OSI Approved :: BSD License", 48 | "Operating System :: OS Independent", 49 | "Programming Language :: Python", 50 | "Programming Language :: Python :: 3.8", 51 | "Programming Language :: Python :: 3.9", 52 | "Programming Language :: Python :: 3.10", 53 | "Programming Language :: Python :: 3.11", 54 | "Programming Language :: Python :: 3.12", 55 | "Framework :: Django", 56 | ], 57 | ) 58 | -------------------------------------------------------------------------------- /tests/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /tests/test_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/tests/test_app/__init__.py -------------------------------------------------------------------------------- /tests/test_app/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from reversion.admin import VersionAdmin 3 | from test_app.models import TestModel, TestModelRelated 4 | 5 | 6 | class TestModelAdmin(VersionAdmin): 7 | 8 | filter_horizontal = ("related",) 9 | 10 | 11 | admin.site.register(TestModel, TestModelAdmin) 12 | 13 | 14 | admin.site.register(TestModelRelated, admin.ModelAdmin) 15 | -------------------------------------------------------------------------------- /tests/test_app/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1 on 2020-08-31 10:05 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ('reversion', '0001_squashed_0004_auto_20160611_1202'), 13 | ('contenttypes', '0002_remove_content_type_name'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='TestModel', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(default='v1', max_length=191)), 22 | ], 23 | ), 24 | migrations.CreateModel( 25 | name='TestModelEscapePK', 26 | fields=[ 27 | ('name', models.CharField(max_length=191, primary_key=True, serialize=False)), 28 | ], 29 | ), 30 | migrations.CreateModel( 31 | name='TestModelInline', 32 | fields=[ 33 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 34 | ('inline_name', models.CharField(default='v1', max_length=191)), 35 | ('test_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='test_app.testmodel')), 36 | ], 37 | ), 38 | migrations.CreateModel( 39 | name='TestModelRelated', 40 | fields=[ 41 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 42 | ('name', models.CharField(default='v1', max_length=191)), 43 | ], 44 | ), 45 | migrations.CreateModel( 46 | name='TestModelWithNaturalKey', 47 | fields=[ 48 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 49 | ('name', models.CharField(default='v1', max_length=191)), 50 | ], 51 | ), 52 | migrations.CreateModel( 53 | name='TestModelParent', 54 | fields=[ 55 | ('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')), 56 | ('parent_name', models.CharField(default='parent v1', max_length=191)), 57 | ], 58 | bases=('test_app.testmodel',), 59 | ), 60 | migrations.CreateModel( 61 | name='TestModelThrough', 62 | fields=[ 63 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 64 | ('name', models.CharField(default='v1', max_length=191)), 65 | ('test_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='test_app.testmodel')), 66 | ('test_model_related', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='test_app.testmodelrelated')), 67 | ], 68 | ), 69 | migrations.CreateModel( 70 | name='TestModelNestedInline', 71 | fields=[ 72 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 73 | ('nested_inline_name', models.CharField(default='v1', max_length=191)), 74 | ('test_model_inline', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='test_app.testmodelinline')), 75 | ], 76 | ), 77 | migrations.CreateModel( 78 | name='TestModelInlineByNaturalKey', 79 | fields=[ 80 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 81 | ('test_model', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='test_app.testmodelwithnaturalkey')), 82 | ], 83 | ), 84 | migrations.CreateModel( 85 | name='TestModelGenericInline', 86 | fields=[ 87 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 88 | ('object_id', models.IntegerField()), 89 | ('inline_name', models.CharField(default='v1', max_length=191)), 90 | ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), 91 | ], 92 | ), 93 | migrations.AddField( 94 | model_name='testmodel', 95 | name='related', 96 | field=models.ManyToManyField(blank=True, related_name='_testmodel_related_+', to='test_app.TestModelRelated'), 97 | ), 98 | migrations.AddField( 99 | model_name='testmodel', 100 | name='related_through', 101 | field=models.ManyToManyField(blank=True, related_name='_testmodel_related_through_+', through='test_app.TestModelThrough', to='test_app.TestModelRelated'), 102 | ), 103 | migrations.CreateModel( 104 | name='TestMeta', 105 | fields=[ 106 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 107 | ('name', models.CharField(max_length=191)), 108 | ('revision', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='reversion.revision')), 109 | ], 110 | ), 111 | migrations.CreateModel( 112 | name='TestModelWithUniqueConstraint', 113 | fields=[ 114 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 115 | ('name', models.CharField(max_length=191, unique=True)), 116 | ], 117 | ), 118 | ] 119 | -------------------------------------------------------------------------------- /tests/test_app/migrations/0002_alter_testmodel_related_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.1 on 2024-01-30 19:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('test_app', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='testmodel', 15 | name='related', 16 | field=models.ManyToManyField(blank=True, related_name='+', to='test_app.testmodelrelated'), 17 | ), 18 | migrations.AlterField( 19 | model_name='testmodel', 20 | name='related_through', 21 | field=models.ManyToManyField(blank=True, related_name='+', through='test_app.TestModelThrough', to='test_app.testmodelrelated'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /tests/test_app/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/tests/test_app/migrations/__init__.py -------------------------------------------------------------------------------- /tests/test_app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.contenttypes.fields import GenericRelation 3 | from django.contrib.contenttypes.models import ContentType 4 | from reversion.models import Revision 5 | 6 | 7 | class TestModelGenericInline(models.Model): 8 | 9 | object_id = models.IntegerField() 10 | 11 | content_type = models.ForeignKey( 12 | ContentType, 13 | on_delete=models.CASCADE, 14 | ) 15 | 16 | inline_name = models.CharField( 17 | max_length=191, 18 | default="v1", 19 | ) 20 | 21 | 22 | class TestModel(models.Model): 23 | 24 | name = models.CharField( 25 | max_length=191, 26 | default="v1", 27 | ) 28 | 29 | related = models.ManyToManyField( 30 | "TestModelRelated", 31 | blank=True, 32 | related_name="+", 33 | ) 34 | 35 | related_through = models.ManyToManyField( 36 | "TestModelRelated", 37 | blank=True, 38 | through="TestModelThrough", 39 | related_name="+", 40 | ) 41 | 42 | generic_inlines = GenericRelation(TestModelGenericInline) 43 | 44 | 45 | class TestModelEscapePK(models.Model): 46 | 47 | name = models.CharField(max_length=191, primary_key=True) 48 | 49 | 50 | class TestModelThrough(models.Model): 51 | 52 | test_model = models.ForeignKey( 53 | "TestModel", 54 | related_name="+", 55 | on_delete=models.CASCADE, 56 | ) 57 | 58 | test_model_related = models.ForeignKey( 59 | "TestModelRelated", 60 | related_name="+", 61 | on_delete=models.CASCADE, 62 | ) 63 | 64 | name = models.CharField( 65 | max_length=191, 66 | default="v1", 67 | ) 68 | 69 | 70 | class TestModelRelated(models.Model): 71 | 72 | name = models.CharField( 73 | max_length=191, 74 | default="v1", 75 | ) 76 | 77 | 78 | class TestModelParent(TestModel): 79 | 80 | parent_name = models.CharField( 81 | max_length=191, 82 | default="parent v1", 83 | ) 84 | 85 | 86 | class TestModelInline(models.Model): 87 | 88 | test_model = models.ForeignKey( 89 | TestModel, 90 | on_delete=models.CASCADE, 91 | ) 92 | 93 | inline_name = models.CharField( 94 | max_length=191, 95 | default="v1", 96 | ) 97 | 98 | 99 | class TestModelNestedInline(models.Model): 100 | test_model_inline = models.ForeignKey( 101 | TestModelInline, 102 | on_delete=models.CASCADE, 103 | ) 104 | 105 | nested_inline_name = models.CharField( 106 | max_length=191, 107 | default="v1", 108 | ) 109 | 110 | 111 | class TestMeta(models.Model): 112 | 113 | revision = models.ForeignKey( 114 | Revision, 115 | on_delete=models.CASCADE, 116 | ) 117 | 118 | name = models.CharField( 119 | max_length=191, 120 | ) 121 | 122 | 123 | class TestModelWithNaturalKeyManager(models.Manager): 124 | def get_by_natural_key(self, name): 125 | return self.get(name=name) 126 | 127 | 128 | class TestModelWithNaturalKey(models.Model): 129 | name = models.CharField( 130 | max_length=191, 131 | default="v1", 132 | ) 133 | 134 | objects = TestModelWithNaturalKeyManager() 135 | 136 | def natural_key(self): 137 | return (self.name,) 138 | 139 | 140 | class TestModelInlineByNaturalKey(models.Model): 141 | test_model = models.ForeignKey( 142 | TestModelWithNaturalKey, 143 | on_delete=models.CASCADE, 144 | ) 145 | 146 | 147 | class TestModelWithUniqueConstraint(models.Model): 148 | 149 | name = models.CharField( 150 | max_length=191, 151 | unique=True, 152 | ) 153 | -------------------------------------------------------------------------------- /tests/test_app/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/tests/test_app/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_app/tests/base.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | from importlib import import_module, reload 3 | from io import StringIO 4 | 5 | from django.conf import settings 6 | from django.contrib.auth.models import User 7 | from django.core.management import call_command 8 | from django.urls import clear_url_caches 9 | from django.test import TestCase, TransactionTestCase 10 | from django.test.utils import override_settings 11 | from django.utils import timezone 12 | 13 | import reversion 14 | from reversion.models import Revision, Version 15 | from test_app.models import TestModel, TestModelParent 16 | 17 | 18 | # Test helpers. 19 | 20 | class TestBaseMixin: 21 | 22 | databases = "__all__" 23 | 24 | def reloadUrls(self): 25 | reload(import_module(settings.ROOT_URLCONF)) 26 | clear_url_caches() 27 | 28 | def setUp(self): 29 | super().setUp() 30 | for model in list(reversion.get_registered_models()): 31 | reversion.unregister(model) 32 | 33 | def tearDown(self): 34 | super().tearDown() 35 | for model in list(reversion.get_registered_models()): 36 | reversion.unregister(model) 37 | 38 | def callCommand(self, command, *args, **kwargs): 39 | kwargs.setdefault("stdout", StringIO()) 40 | kwargs.setdefault("stderr", StringIO()) 41 | kwargs.setdefault("verbosity", 2) 42 | return call_command(command, *args, **kwargs) 43 | 44 | def assertSingleRevision(self, objects, user=None, comment="", meta_names=(), date_created=None, 45 | using=None, model_db=None): 46 | revision = Version.objects.using(using).get_for_object(objects[0], model_db=model_db).get().revision 47 | self.assertEqual(revision.user, user) 48 | if hasattr(comment, 'pattern'): 49 | self.assertRegex(revision.get_comment(), comment) 50 | elif comment is not None: # Allow a wildcard comment. 51 | self.assertEqual(revision.get_comment(), comment) 52 | self.assertAlmostEqual(revision.date_created, date_created or timezone.now(), delta=timedelta(seconds=1)) 53 | # Check meta. 54 | self.assertEqual(revision.testmeta_set.count(), len(meta_names)) 55 | for meta_name in meta_names: 56 | self.assertTrue(revision.testmeta_set.filter(name=meta_name).exists()) 57 | # Check objects. 58 | self.assertEqual(revision.version_set.count(), len(objects)) 59 | for obj in objects: 60 | self.assertTrue(Version.objects.using(using).get_for_object( 61 | obj, 62 | model_db=model_db, 63 | ).filter( 64 | revision=revision, 65 | ).exists()) 66 | 67 | def assertNoRevision(self, using=None): 68 | self.assertEqual(Revision.objects.using(using).all().count(), 0) 69 | 70 | 71 | class TestBase(TestBaseMixin, TestCase): 72 | pass 73 | 74 | 75 | class TestBaseTransaction(TestBaseMixin, TransactionTestCase): 76 | pass 77 | 78 | 79 | class TestModelMixin: 80 | 81 | def setUp(self): 82 | super().setUp() 83 | reversion.register(TestModel) 84 | 85 | 86 | class TestModelParentMixin(TestModelMixin): 87 | 88 | def setUp(self): 89 | super().setUp() 90 | reversion.register(TestModelParent, follow=("testmodel_ptr",)) 91 | 92 | 93 | @override_settings(PASSWORD_HASHERS=["django.contrib.auth.hashers.MD5PasswordHasher"]) 94 | class UserMixin(TestBase): 95 | 96 | def setUp(self): 97 | super().setUp() 98 | self.user = User(username="test", is_staff=True, is_superuser=True) 99 | self.user.set_password("password") 100 | self.user.save() 101 | 102 | 103 | class LoginMixin(UserMixin): 104 | 105 | def setUp(self): 106 | super().setUp() 107 | self.client.login(username="test", password="password") 108 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_admin.py: -------------------------------------------------------------------------------- 1 | import re 2 | from django.contrib import admin 3 | from django.contrib.contenttypes.admin import GenericTabularInline 4 | from django.shortcuts import resolve_url 5 | import reversion 6 | from reversion.admin import VersionAdmin 7 | from reversion.models import Version 8 | from test_app.models import TestModel, TestModelParent, TestModelInline, TestModelGenericInline, TestModelEscapePK 9 | from test_app.tests.base import TestBase, LoginMixin 10 | 11 | 12 | class AdminMixin(TestBase): 13 | 14 | def setUp(self): 15 | super().setUp() 16 | admin.site.register(TestModelParent, VersionAdmin) 17 | self.reloadUrls() 18 | 19 | def tearDown(self): 20 | super().tearDown() 21 | admin.site.unregister(TestModelParent) 22 | self.reloadUrls() 23 | 24 | 25 | class AdminRegisterTest(AdminMixin, TestBase): 26 | 27 | def setAutoRegister(self): 28 | self.assertTrue(reversion.is_registered(TestModelParent)) 29 | 30 | def setAutoRegisterFollowsParent(self): 31 | self.assertTrue(reversion.is_registered(TestModel)) 32 | 33 | 34 | class AdminAddViewTest(LoginMixin, AdminMixin, TestBase): 35 | 36 | def testAddView(self): 37 | self.client.post(resolve_url("admin:test_app_testmodelparent_add"), { 38 | "name": "v1", 39 | "parent_name": "parent_v1", 40 | }) 41 | obj = TestModelParent.objects.get() 42 | self.assertSingleRevision( 43 | (obj, obj.testmodel_ptr), user=self.user, comment="Added." 44 | ) 45 | 46 | 47 | class AdminUpdateViewTest(LoginMixin, AdminMixin, TestBase): 48 | 49 | def testUpdateView(self): 50 | obj = TestModelParent.objects.create() 51 | self.client.post(resolve_url("admin:test_app_testmodelparent_change", obj.pk), { 52 | "name": "v2", 53 | "parent_name": "parent v2", 54 | }) 55 | self.assertSingleRevision( 56 | (obj, obj.testmodel_ptr), user=self.user, 57 | # Django 3.0 changed formatting a bit. 58 | comment=re.compile(r"Changed [nN]ame and [pP]arent[ _]name\.") 59 | ) 60 | 61 | 62 | class AdminChangelistView(LoginMixin, AdminMixin, TestBase): 63 | 64 | def testChangelistView(self): 65 | obj = TestModelParent.objects.create() 66 | response = self.client.get(resolve_url("admin:test_app_testmodelparent_changelist")) 67 | self.assertContains(response, resolve_url("admin:test_app_testmodelparent_change", obj.pk)) 68 | 69 | 70 | class AdminRevisionViewTest(LoginMixin, AdminMixin, TestBase): 71 | 72 | def setUp(self): 73 | super().setUp() 74 | with reversion.create_revision(): 75 | self.obj = TestModelParent.objects.create() 76 | with reversion.create_revision(): 77 | self.obj.name = "v2" 78 | self.obj.parent_name = "parent v2" 79 | self.obj.save() 80 | 81 | def testRevisionView(self): 82 | response = self.client.get(resolve_url( 83 | "admin:test_app_testmodelparent_revision", 84 | self.obj.pk, 85 | Version.objects.get_for_object(self.obj)[1].pk, 86 | )) 87 | self.assertContains(response, 'value="v1"') 88 | self.assertContains(response, 'value="parent v1"') 89 | # Test that the changes were rolled back. 90 | self.obj.refresh_from_db() 91 | self.assertEqual(self.obj.name, "v2") 92 | self.assertEqual(self.obj.parent_name, "parent v2") 93 | self.assertIn("revert", response.context) 94 | self.assertTrue(response.context["revert"]) 95 | 96 | def testRevisionViewOldRevision(self): 97 | response = self.client.get(resolve_url( 98 | "admin:test_app_testmodelparent_revision", 99 | self.obj.pk, 100 | Version.objects.get_for_object(self.obj)[0].pk, 101 | )) 102 | self.assertContains(response, 'value="v2"') 103 | self.assertContains(response, 'value="parent v2"') 104 | 105 | def testRevisionViewRevertError(self): 106 | Version.objects.get_for_object(self.obj).update(format="boom") 107 | response = self.client.get(resolve_url( 108 | "admin:test_app_testmodelparent_revision", 109 | self.obj.pk, 110 | Version.objects.get_for_object(self.obj)[1].pk, 111 | )) 112 | self.assertEqual( 113 | response["Location"].replace("http://testserver", ""), 114 | resolve_url("admin:test_app_testmodelparent_changelist"), 115 | ) 116 | 117 | def testRevisionViewRevert(self): 118 | self.client.post(resolve_url( 119 | "admin:test_app_testmodelparent_revision", 120 | self.obj.pk, 121 | Version.objects.get_for_object(self.obj)[1].pk, 122 | ), { 123 | "name": "v1", 124 | "parent_name": "parent v1", 125 | }) 126 | self.obj.refresh_from_db() 127 | self.assertEqual(self.obj.name, "v1") 128 | self.assertEqual(self.obj.parent_name, "parent v1") 129 | 130 | 131 | class AdminRecoverViewTest(LoginMixin, AdminMixin, TestBase): 132 | 133 | def setUp(self): 134 | super().setUp() 135 | with reversion.create_revision(): 136 | obj = TestModelParent.objects.create() 137 | obj.delete() 138 | 139 | def testRecoverView(self): 140 | response = self.client.get(resolve_url( 141 | "admin:test_app_testmodelparent_recover", 142 | Version.objects.get_for_model(TestModelParent).get().pk, 143 | )) 144 | self.assertContains(response, 'value="v1"') 145 | self.assertContains(response, 'value="parent v1"') 146 | self.assertIn("recover", response.context) 147 | self.assertTrue(response.context["recover"]) 148 | 149 | def testRecoverViewRecover(self): 150 | self.client.post(resolve_url( 151 | "admin:test_app_testmodelparent_recover", 152 | Version.objects.get_for_model(TestModelParent).get().pk, 153 | ), { 154 | "name": "v1", 155 | "parent_name": "parent v1", 156 | }) 157 | obj = TestModelParent.objects.get() 158 | self.assertEqual(obj.name, "v1") 159 | self.assertEqual(obj.parent_name, "parent v1") 160 | 161 | 162 | class AdminRecoverlistViewTest(LoginMixin, AdminMixin, TestBase): 163 | 164 | def testRecoverlistView(self): 165 | with reversion.create_revision(): 166 | obj = TestModelParent.objects.create() 167 | obj.delete() 168 | response = self.client.get(resolve_url("admin:test_app_testmodelparent_recoverlist")) 169 | self.assertContains(response, resolve_url( 170 | "admin:test_app_testmodelparent_recover", 171 | Version.objects.get_for_model(TestModelParent).get().pk, 172 | )) 173 | 174 | 175 | class AdminHistoryViewTest(LoginMixin, AdminMixin, TestBase): 176 | 177 | def testHistorylistView(self): 178 | with reversion.create_revision(): 179 | obj = TestModelParent.objects.create() 180 | response = self.client.get(resolve_url("admin:test_app_testmodelparent_history", obj.pk)) 181 | self.assertContains(response, resolve_url( 182 | "admin:test_app_testmodelparent_revision", 183 | obj.pk, 184 | Version.objects.get_for_model(TestModelParent).get().pk, 185 | )) 186 | 187 | 188 | class AdminQuotingTest(LoginMixin, AdminMixin, TestBase): 189 | 190 | def setUp(self): 191 | super().setUp() 192 | admin.site.register(TestModelEscapePK, VersionAdmin) 193 | self.reloadUrls() 194 | 195 | def tearDown(self): 196 | super().tearDown() 197 | admin.site.unregister(TestModelEscapePK) 198 | self.reloadUrls() 199 | 200 | def testHistoryWithQuotedPrimaryKey(self): 201 | pk = 'ABC_123' 202 | quoted_pk = admin.utils.quote(pk) 203 | # test is invalid if quoting does not change anything 204 | assert quoted_pk != pk 205 | 206 | with reversion.create_revision(): 207 | obj = TestModelEscapePK.objects.create(name=pk) 208 | 209 | revision_url = resolve_url( 210 | "admin:test_app_testmodelescapepk_revision", 211 | quoted_pk, 212 | Version.objects.get_for_object(obj).get().pk, 213 | ) 214 | history_url = resolve_url( 215 | "admin:test_app_testmodelescapepk_history", 216 | quoted_pk 217 | ) 218 | response = self.client.get(history_url) 219 | self.assertContains(response, revision_url) 220 | response = self.client.get(revision_url) 221 | self.assertContains(response, f'value="{pk}"') 222 | 223 | 224 | class TestModelInlineAdmin(admin.TabularInline): 225 | 226 | model = TestModelInline 227 | 228 | 229 | class TestModelGenericInlineAdmin(GenericTabularInline): 230 | 231 | model = TestModelGenericInline 232 | 233 | 234 | class TestModelParentAdmin(VersionAdmin): 235 | 236 | inlines = (TestModelInlineAdmin, TestModelGenericInlineAdmin) 237 | 238 | 239 | class AdminRegisterInlineTest(TestBase): 240 | 241 | def setUp(self): 242 | super().setUp() 243 | admin.site.register(TestModelParent, TestModelParentAdmin) 244 | self.reloadUrls() 245 | 246 | def tearDown(self): 247 | super().tearDown() 248 | admin.site.unregister(TestModelParent) 249 | self.reloadUrls() 250 | 251 | def testAutoRegisterInline(self): 252 | self.assertTrue(reversion.is_registered(TestModelInline)) 253 | 254 | def testAutoRegisterGenericInline(self): 255 | self.assertTrue(reversion.is_registered(TestModelGenericInline)) 256 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_api.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | from unittest.mock import MagicMock 3 | 4 | from django.contrib.auth.models import User 5 | from django.db import models 6 | from django.db.transaction import get_connection 7 | from django.utils import timezone 8 | import reversion 9 | from test_app.models import TestModel, TestModelRelated, TestModelThrough, TestModelParent, TestMeta 10 | from test_app.tests.base import TestBase, TestBaseTransaction, TestModelMixin, UserMixin 11 | 12 | 13 | class SaveTest(TestModelMixin, TestBase): 14 | 15 | def testModelSave(self): 16 | TestModel.objects.create() 17 | self.assertNoRevision() 18 | 19 | 20 | class IsRegisteredTest(TestModelMixin, TestBase): 21 | 22 | def testIsRegistered(self): 23 | self.assertTrue(reversion.is_registered(TestModel)) 24 | 25 | 26 | class IsRegisterUnregisteredTest(TestBase): 27 | 28 | def testIsRegisteredFalse(self): 29 | self.assertFalse(reversion.is_registered(TestModel)) 30 | 31 | 32 | class GetRegisteredModelsTest(TestModelMixin, TestBase): 33 | 34 | def testGetRegisteredModels(self): 35 | self.assertEqual(set(reversion.get_registered_models()), {TestModel}) 36 | 37 | 38 | class RegisterTest(TestBase): 39 | 40 | def testRegister(self): 41 | reversion.register(TestModel) 42 | self.assertTrue(reversion.is_registered(TestModel)) 43 | 44 | def testRegisterDecorator(self): 45 | @reversion.register() 46 | class TestModelDecorater(models.Model): 47 | pass 48 | self.assertTrue(reversion.is_registered(TestModelDecorater)) 49 | 50 | def testRegisterAlreadyRegistered(self): 51 | reversion.register(TestModel) 52 | with self.assertRaises(reversion.RegistrationError): 53 | reversion.register(TestModel) 54 | 55 | def testRegisterM2MSThroughLazy(self): 56 | # When register is used as a decorator in models.py, lazy relations haven't had a chance to be resolved, so 57 | # will still be a string. 58 | @reversion.register() 59 | class TestModelLazy(models.Model): 60 | related = models.ManyToManyField( 61 | TestModelRelated, 62 | through="TestModelThroughLazy", 63 | ) 64 | 65 | class TestModelThroughLazy(models.Model): 66 | pass 67 | 68 | 69 | class UnregisterTest(TestModelMixin, TestBase): 70 | 71 | def testUnregister(self): 72 | reversion.unregister(TestModel) 73 | self.assertFalse(reversion.is_registered(TestModel)) 74 | 75 | 76 | class UnregisterUnregisteredTest(TestBase): 77 | 78 | def testUnregisterNotRegistered(self): 79 | with self.assertRaises(reversion.RegistrationError): 80 | reversion.unregister(User) 81 | 82 | 83 | class CreateRevisionTest(TestModelMixin, TestBase): 84 | 85 | def testCreateRevision(self): 86 | with reversion.create_revision(): 87 | obj = TestModel.objects.create() 88 | self.assertSingleRevision((obj,)) 89 | 90 | def testCreateRevisionNested(self): 91 | with reversion.create_revision(): 92 | with reversion.create_revision(): 93 | obj = TestModel.objects.create() 94 | self.assertSingleRevision((obj,)) 95 | 96 | def testCreateRevisionEmpty(self): 97 | with reversion.create_revision(): 98 | pass 99 | self.assertNoRevision() 100 | 101 | def testCreateRevisionException(self): 102 | try: 103 | with reversion.create_revision(): 104 | TestModel.objects.create() 105 | raise Exception("Boom!") 106 | except Exception: 107 | pass 108 | self.assertNoRevision() 109 | 110 | def testCreateRevisionDecorator(self): 111 | obj = reversion.create_revision()(TestModel.objects.create)() 112 | self.assertSingleRevision((obj,)) 113 | 114 | def testPreRevisionCommitSignal(self): 115 | _callback = MagicMock() 116 | reversion.signals.pre_revision_commit.connect(_callback) 117 | 118 | with reversion.create_revision(): 119 | TestModel.objects.create() 120 | self.assertEqual(_callback.call_count, 1) 121 | 122 | def testPostRevisionCommitSignal(self): 123 | _callback = MagicMock() 124 | reversion.signals.post_revision_commit.connect(_callback) 125 | 126 | with reversion.create_revision(): 127 | TestModel.objects.create() 128 | self.assertEqual(_callback.call_count, 1) 129 | 130 | 131 | class CreateRevisionAtomicTest(TestModelMixin, TestBaseTransaction): 132 | def testCreateRevisionAtomic(self): 133 | self.assertFalse(get_connection().in_atomic_block) 134 | with reversion.create_revision(): 135 | self.assertTrue(get_connection().in_atomic_block) 136 | 137 | def testCreateRevisionNonAtomic(self): 138 | self.assertFalse(get_connection().in_atomic_block) 139 | with reversion.create_revision(atomic=False): 140 | self.assertFalse(get_connection().in_atomic_block) 141 | 142 | def testCreateRevisionInOnCommitHandler(self): 143 | from django.db import transaction 144 | from reversion.models import Revision 145 | 146 | self.assertEqual(Revision.objects.all().count(), 0) 147 | 148 | with reversion.create_revision(atomic=True): 149 | model = TestModel.objects.create() 150 | 151 | def on_commit(): 152 | with reversion.create_revision(atomic=True): 153 | model.name = 'oncommit' 154 | model.save() 155 | 156 | transaction.on_commit(on_commit) 157 | 158 | self.assertEqual(Revision.objects.all().count(), 2) 159 | 160 | 161 | class CreateRevisionManageManuallyTest(TestModelMixin, TestBase): 162 | 163 | def testCreateRevisionManageManually(self): 164 | with reversion.create_revision(manage_manually=True): 165 | TestModel.objects.create() 166 | self.assertNoRevision() 167 | 168 | def testCreateRevisionManageManuallyNested(self): 169 | with reversion.create_revision(): 170 | with reversion.create_revision(manage_manually=True): 171 | TestModel.objects.create() 172 | self.assertNoRevision() 173 | 174 | 175 | class CreateRevisionDbTest(TestModelMixin, TestBase): 176 | databases = {"default", "mysql", "postgres"} 177 | 178 | def testCreateRevisionMultiDb(self): 179 | with reversion.create_revision(using="mysql"), reversion.create_revision(using="postgres"): 180 | obj = TestModel.objects.create() 181 | self.assertNoRevision() 182 | self.assertSingleRevision((obj,), using="mysql") 183 | self.assertSingleRevision((obj,), using="postgres") 184 | 185 | 186 | class CreateRevisionFollowTest(TestBase): 187 | 188 | def testCreateRevisionFollow(self): 189 | reversion.register(TestModel, follow=("related",)) 190 | reversion.register(TestModelRelated) 191 | obj_related = TestModelRelated.objects.create() 192 | with reversion.create_revision(): 193 | obj = TestModel.objects.create() 194 | obj.related.add(obj_related) 195 | self.assertSingleRevision((obj, obj_related)) 196 | 197 | def testCreateRevisionFollowThrough(self): 198 | reversion.register(TestModel, follow=("related_through",)) 199 | reversion.register(TestModelThrough, follow=("test_model", "test_model_related",)) 200 | reversion.register(TestModelRelated) 201 | obj_related = TestModelRelated.objects.create() 202 | with reversion.create_revision(): 203 | obj = TestModel.objects.create() 204 | obj_through = TestModelThrough.objects.create( 205 | test_model=obj, 206 | test_model_related=obj_related, 207 | ) 208 | self.assertSingleRevision((obj, obj_through, obj_related)) 209 | 210 | def testCreateRevisionFollowInvalid(self): 211 | reversion.register(TestModel, follow=("name",)) 212 | with reversion.create_revision(): 213 | with self.assertRaises(reversion.RegistrationError): 214 | TestModel.objects.create() 215 | 216 | 217 | class CreateRevisionIgnoreDuplicatesTest(TestBase): 218 | 219 | def testCreateRevisionIgnoreDuplicates(self): 220 | reversion.register(TestModel, ignore_duplicates=True) 221 | with reversion.create_revision(): 222 | obj = TestModel.objects.create() 223 | with reversion.create_revision(): 224 | obj.save() 225 | self.assertSingleRevision((obj,)) 226 | 227 | 228 | class CreateRevisionInheritanceTest(TestModelMixin, TestBase): 229 | 230 | def testCreateRevisionInheritance(self): 231 | reversion.register(TestModelParent, follow=("testmodel_ptr",)) 232 | with reversion.create_revision(): 233 | obj = TestModelParent.objects.create() 234 | self.assertSingleRevision((obj, obj.testmodel_ptr)) 235 | 236 | 237 | class SetCommentTest(TestModelMixin, TestBase): 238 | 239 | def testSetComment(self): 240 | with reversion.create_revision(): 241 | reversion.set_comment("comment v1") 242 | obj = TestModel.objects.create() 243 | self.assertSingleRevision((obj,), comment="comment v1") 244 | 245 | def testSetCommentNoBlock(self): 246 | with self.assertRaises(reversion.RevisionManagementError): 247 | reversion.set_comment("comment v1") 248 | 249 | 250 | class GetCommentTest(TestBase): 251 | 252 | def testGetComment(self): 253 | with reversion.create_revision(): 254 | reversion.set_comment("comment v1") 255 | self.assertEqual(reversion.get_comment(), "comment v1") 256 | 257 | def testGetCommentDefault(self): 258 | with reversion.create_revision(): 259 | self.assertEqual(reversion.get_comment(), "") 260 | 261 | def testGetCommentNoBlock(self): 262 | with self.assertRaises(reversion.RevisionManagementError): 263 | reversion.get_comment() 264 | 265 | 266 | class SetUserTest(UserMixin, TestModelMixin, TestBase): 267 | 268 | def testSetUser(self): 269 | with reversion.create_revision(): 270 | reversion.set_user(self.user) 271 | obj = TestModel.objects.create() 272 | self.assertSingleRevision((obj,), user=self.user) 273 | 274 | def testSetUserNoBlock(self): 275 | with self.assertRaises(reversion.RevisionManagementError): 276 | reversion.set_user(self.user) 277 | 278 | 279 | class GetUserTest(UserMixin, TestBase): 280 | 281 | def testGetUser(self): 282 | with reversion.create_revision(): 283 | reversion.set_user(self.user) 284 | self.assertEqual(reversion.get_user(), self.user) 285 | 286 | def testGetUserDefault(self): 287 | with reversion.create_revision(): 288 | self.assertEqual(reversion.get_user(), None) 289 | 290 | def testGetUserNoBlock(self): 291 | with self.assertRaises(reversion.RevisionManagementError): 292 | reversion.get_user() 293 | 294 | 295 | class SetDateCreatedTest(TestModelMixin, TestBase): 296 | 297 | def testSetDateCreated(self): 298 | date_created = timezone.now() - timedelta(days=20) 299 | with reversion.create_revision(): 300 | reversion.set_date_created(date_created) 301 | obj = TestModel.objects.create() 302 | self.assertSingleRevision((obj,), date_created=date_created) 303 | 304 | def testDateCreatedNoBlock(self): 305 | with self.assertRaises(reversion.RevisionManagementError): 306 | reversion.set_date_created(timezone.now()) 307 | 308 | 309 | class GetDateCreatedTest(TestBase): 310 | 311 | def testGetDateCreated(self): 312 | date_created = timezone.now() - timedelta(days=20) 313 | with reversion.create_revision(): 314 | reversion.set_date_created(date_created) 315 | self.assertEqual(reversion.get_date_created(), date_created) 316 | 317 | def testGetDateCreatedDefault(self): 318 | with reversion.create_revision(): 319 | self.assertAlmostEqual(reversion.get_date_created(), timezone.now(), delta=timedelta(seconds=1)) 320 | 321 | def testGetDateCreatedNoBlock(self): 322 | with self.assertRaises(reversion.RevisionManagementError): 323 | reversion.get_date_created() 324 | 325 | 326 | class AddMetaTest(TestModelMixin, TestBase): 327 | databases = {"default", "mysql", "postgres"} 328 | 329 | def testAddMeta(self): 330 | with reversion.create_revision(): 331 | reversion.add_meta(TestMeta, name="meta v1") 332 | obj = TestModel.objects.create() 333 | self.assertSingleRevision((obj,), meta_names=("meta v1",)) 334 | 335 | def testAddMetaNoBlock(self): 336 | with self.assertRaises(reversion.RevisionManagementError): 337 | reversion.add_meta(TestMeta, name="meta v1") 338 | 339 | def testAddMetaMultDb(self): 340 | with reversion.create_revision(using="mysql"), reversion.create_revision(using="postgres"): 341 | obj = TestModel.objects.create() 342 | reversion.add_meta(TestMeta, name="meta v1") 343 | self.assertNoRevision() 344 | self.assertSingleRevision((obj,), meta_names=("meta v1",), using="mysql") 345 | self.assertSingleRevision((obj,), meta_names=("meta v1",), using="postgres") 346 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_commands.py: -------------------------------------------------------------------------------- 1 | import json 2 | from datetime import timedelta 3 | from django.core.management import CommandError 4 | from django.utils import timezone 5 | import reversion 6 | from test_app.models import TestModel 7 | from test_app.tests.base import TestBase, TestModelMixin 8 | 9 | 10 | class CreateInitialRevisionsTest(TestModelMixin, TestBase): 11 | 12 | def testCreateInitialRevisions(self): 13 | obj = TestModel.objects.create() 14 | self.callCommand("createinitialrevisions") 15 | self.assertSingleRevision((obj,), comment="Initial version.") 16 | 17 | def testCreateInitialRevisionsAlreadyCreated(self): 18 | obj = TestModel.objects.create() 19 | self.callCommand("createinitialrevisions") 20 | self.callCommand("createinitialrevisions") 21 | self.assertSingleRevision((obj,), comment="Initial version.") 22 | 23 | 24 | class CreateInitialRevisionsAppLabelTest(TestModelMixin, TestBase): 25 | 26 | def testCreateInitialRevisionsAppLabel(self): 27 | obj = TestModel.objects.create() 28 | self.callCommand("createinitialrevisions", "test_app") 29 | self.assertSingleRevision((obj,), comment="Initial version.") 30 | 31 | def testCreateInitialRevisionsAppLabelMissing(self): 32 | with self.assertRaises(CommandError): 33 | self.callCommand("createinitialrevisions", "boom") 34 | 35 | def testCreateInitialRevisionsModel(self): 36 | obj = TestModel.objects.create() 37 | self.callCommand("createinitialrevisions", "test_app.TestModel") 38 | self.assertSingleRevision((obj,), comment="Initial version.") 39 | 40 | def testCreateInitialRevisionsModelMissing(self): 41 | with self.assertRaises(CommandError): 42 | self.callCommand("createinitialrevisions", "test_app.boom") 43 | 44 | def testCreateInitialRevisionsModelMissingApp(self): 45 | with self.assertRaises(CommandError): 46 | self.callCommand("createinitialrevisions", "boom.boom") 47 | 48 | def testCreateInitialRevisionsModelNotRegistered(self): 49 | TestModel.objects.create() 50 | self.callCommand("createinitialrevisions", "auth.User") 51 | self.assertNoRevision() 52 | 53 | 54 | class CreateInitialRevisionsDbTest(TestModelMixin, TestBase): 55 | databases = {"default", "mysql", "postgres"} 56 | 57 | def testCreateInitialRevisionsDb(self): 58 | obj = TestModel.objects.create() 59 | self.callCommand("createinitialrevisions", using="postgres") 60 | self.assertNoRevision() 61 | self.assertSingleRevision((obj,), comment="Initial version.", using="postgres") 62 | 63 | def testCreateInitialRevisionsDbMySql(self): 64 | obj = TestModel.objects.create() 65 | self.callCommand("createinitialrevisions", using="mysql") 66 | self.assertNoRevision() 67 | self.assertSingleRevision((obj,), comment="Initial version.", using="mysql") 68 | 69 | 70 | class CreateInitialRevisionsModelDbTest(TestModelMixin, TestBase): 71 | databases = {"default", "postgres"} 72 | 73 | def testCreateInitialRevisionsModelDb(self): 74 | obj = TestModel.objects.db_manager("postgres").create() 75 | self.callCommand("createinitialrevisions", model_db="postgres") 76 | self.assertSingleRevision((obj,), comment="Initial version.", model_db="postgres") 77 | 78 | 79 | class CreateInitialRevisionsCommentTest(TestModelMixin, TestBase): 80 | 81 | def testCreateInitialRevisionsComment(self): 82 | obj = TestModel.objects.create() 83 | self.callCommand("createinitialrevisions", comment="comment v1") 84 | self.assertSingleRevision((obj,), comment="comment v1") 85 | 86 | 87 | class CreateInitialRevisionsMetaTest(TestModelMixin, TestBase): 88 | def testCreateInitialRevisionsComment(self): 89 | obj = TestModel.objects.create() 90 | meta_name = "meta name" 91 | meta = json.dumps({"test_app.TestMeta": {"name": meta_name}}) 92 | self.callCommand("createinitialrevisions", "--meta", meta) 93 | self.assertSingleRevision((obj,), meta_names=(meta_name, ), comment="Initial version.") 94 | 95 | 96 | class DeleteRevisionsTest(TestModelMixin, TestBase): 97 | 98 | def testDeleteRevisions(self): 99 | with reversion.create_revision(): 100 | TestModel.objects.create() 101 | self.callCommand("deleterevisions") 102 | self.assertNoRevision() 103 | 104 | 105 | class DeleteRevisionsAppLabelTest(TestModelMixin, TestBase): 106 | 107 | def testDeleteRevisionsAppLabel(self): 108 | with reversion.create_revision(): 109 | TestModel.objects.create() 110 | self.callCommand("deleterevisions", "test_app") 111 | self.assertNoRevision() 112 | 113 | def testDeleteRevisionsAppLabelMissing(self): 114 | with self.assertRaises(CommandError): 115 | self.callCommand("deleterevisions", "boom") 116 | 117 | def testDeleteRevisionsModel(self): 118 | with reversion.create_revision(): 119 | TestModel.objects.create() 120 | self.callCommand("deleterevisions", "test_app.TestModel") 121 | self.assertNoRevision() 122 | 123 | def testDeleteRevisionsModelMissing(self): 124 | with self.assertRaises(CommandError): 125 | self.callCommand("deleterevisions", "test_app.boom") 126 | 127 | def testDeleteRevisionsModelMissingApp(self): 128 | with self.assertRaises(CommandError): 129 | self.callCommand("deleterevisions", "boom.boom") 130 | 131 | def testDeleteRevisionsModelNotRegistered(self): 132 | with reversion.create_revision(): 133 | obj = TestModel.objects.create() 134 | self.callCommand("deleterevisions", "auth.User") 135 | self.assertSingleRevision((obj,)) 136 | 137 | 138 | class DeleteRevisionsDbTest(TestModelMixin, TestBase): 139 | databases = {"default", "mysql", "postgres"} 140 | 141 | def testDeleteRevisionsDb(self): 142 | with reversion.create_revision(using="postgres"): 143 | TestModel.objects.create() 144 | self.callCommand("deleterevisions", using="postgres") 145 | self.assertNoRevision(using="postgres") 146 | 147 | def testDeleteRevisionsDbMySql(self): 148 | with reversion.create_revision(using="mysql"): 149 | TestModel.objects.create() 150 | self.callCommand("deleterevisions", using="mysql") 151 | self.assertNoRevision(using="mysql") 152 | 153 | def testDeleteRevisionsDbNoMatch(self): 154 | with reversion.create_revision(): 155 | obj = TestModel.objects.create() 156 | self.callCommand("deleterevisions", using="postgres") 157 | self.assertSingleRevision((obj,)) 158 | 159 | 160 | class DeleteRevisionsModelDbTest(TestModelMixin, TestBase): 161 | databases = {"default", "postgres"} 162 | 163 | def testDeleteRevisionsModelDb(self): 164 | with reversion.create_revision(): 165 | TestModel.objects.db_manager("postgres").create() 166 | self.callCommand("deleterevisions", model_db="postgres") 167 | self.assertNoRevision(using="postgres") 168 | 169 | 170 | class DeleteRevisionsDaysTest(TestModelMixin, TestBase): 171 | 172 | def testDeleteRevisionsDays(self): 173 | date_created = timezone.now() - timedelta(days=20) 174 | with reversion.create_revision(): 175 | TestModel.objects.create() 176 | reversion.set_date_created(date_created) 177 | self.callCommand("deleterevisions", days=19) 178 | self.assertNoRevision() 179 | 180 | def testDeleteRevisionsDaysNoMatch(self): 181 | date_created = timezone.now() - timedelta(days=20) 182 | with reversion.create_revision(): 183 | obj = TestModel.objects.create() 184 | reversion.set_date_created(date_created) 185 | self.callCommand("deleterevisions", days=21) 186 | self.assertSingleRevision((obj,), date_created=date_created) 187 | 188 | 189 | class DeleteRevisionsKeepTest(TestModelMixin, TestBase): 190 | 191 | def testDeleteRevisionsKeep(self): 192 | with reversion.create_revision(): 193 | obj_1 = TestModel.objects.create() 194 | reversion.set_comment("obj_1 v1") 195 | with reversion.create_revision(): 196 | obj_1.save() 197 | reversion.set_comment("obj_1 v2") 198 | with reversion.create_revision(): 199 | obj_2 = TestModel.objects.create() 200 | reversion.set_comment("obj_2 v1") 201 | with reversion.create_revision(): 202 | obj_2.save() 203 | reversion.set_comment("obj_2 v2") 204 | with reversion.create_revision(): 205 | obj_3 = TestModel.objects.create() 206 | self.callCommand("deleterevisions", keep=1) 207 | self.assertSingleRevision((obj_1,), comment="obj_1 v2") 208 | self.assertSingleRevision((obj_2,), comment="obj_2 v2") 209 | self.assertSingleRevision((obj_3,)) 210 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_middleware.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.test.utils import override_settings 3 | from test_app.models import TestModel 4 | from test_app.tests.base import TestBase, TestModelMixin, LoginMixin 5 | 6 | 7 | use_middleware = override_settings( 8 | MIDDLEWARE=settings.MIDDLEWARE + ["reversion.middleware.RevisionMiddleware"], 9 | ) 10 | 11 | 12 | @use_middleware 13 | class RevisionMiddlewareTest(TestModelMixin, TestBase): 14 | 15 | def testCreateRevision(self): 16 | response = self.client.post("/test-app/save-obj/") 17 | obj = TestModel.objects.get(pk=response.content) 18 | self.assertSingleRevision((obj,)) 19 | 20 | def testCreateRevisionError(self): 21 | with self.assertRaises(Exception): 22 | self.client.post("/test-app/save-obj-error/") 23 | self.assertNoRevision() 24 | 25 | def testCreateRevisionGet(self): 26 | self.client.get("/test-app/create-revision/") 27 | self.assertNoRevision() 28 | 29 | 30 | @use_middleware 31 | class RevisionMiddlewareUserTest(TestModelMixin, LoginMixin, TestBase): 32 | 33 | def testCreateRevisionUser(self): 34 | response = self.client.post("/test-app/save-obj/") 35 | obj = TestModel.objects.get(pk=response.content) 36 | self.assertSingleRevision((obj,), user=self.user) 37 | -------------------------------------------------------------------------------- /tests/test_app/tests/test_views.py: -------------------------------------------------------------------------------- 1 | from test_app.models import TestModel 2 | from test_app.tests.base import TestBase, TestModelMixin, LoginMixin 3 | 4 | 5 | class CreateRevisionTest(TestModelMixin, TestBase): 6 | 7 | def testCreateRevision(self): 8 | response = self.client.post("/test-app/create-revision/") 9 | obj = TestModel.objects.get(pk=response.content) 10 | self.assertSingleRevision((obj,)) 11 | 12 | def testCreateRevisionGet(self): 13 | self.client.get("/test-app/create-revision/") 14 | self.assertNoRevision() 15 | 16 | 17 | class CreateRevisionUserTest(LoginMixin, TestModelMixin, TestBase): 18 | 19 | def testCreateRevisionUser(self): 20 | response = self.client.post("/test-app/create-revision/") 21 | obj = TestModel.objects.get(pk=response.content) 22 | self.assertSingleRevision((obj,), user=self.user) 23 | 24 | 25 | class RevisionMixinTest(TestModelMixin, TestBase): 26 | 27 | def testRevisionMixin(self): 28 | response = self.client.post("/test-app/revision-mixin/") 29 | obj = TestModel.objects.get(pk=response.content) 30 | self.assertSingleRevision((obj,)) 31 | 32 | def testRevisionMixinGet(self): 33 | self.client.get("/test-app/revision-mixin/") 34 | self.assertNoRevision() 35 | 36 | def testRevisionMixinCustomPredicate(self): 37 | self.client.post("/test-app/revision-mixin/", HTTP_X_NOREVISION="true") 38 | self.assertNoRevision() 39 | 40 | 41 | class RevisionMixinUserTest(LoginMixin, TestModelMixin, TestBase): 42 | 43 | def testCreateRevisionUser(self): 44 | response = self.client.post("/test-app/revision-mixin/") 45 | obj = TestModel.objects.get(pk=response.content) 46 | self.assertSingleRevision((obj,), user=self.user) 47 | -------------------------------------------------------------------------------- /tests/test_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from test_app import views 3 | 4 | 5 | urlpatterns = [ 6 | path("save-obj/", views.save_obj_view), 7 | path("save-obj-error/", views.save_obj_error_view), 8 | path("create-revision/", views.create_revision_view), 9 | path("revision-mixin/", views.RevisionMixinView.as_view()), 10 | ] 11 | -------------------------------------------------------------------------------- /tests/test_app/views.py: -------------------------------------------------------------------------------- 1 | from django.db import transaction 2 | from django.http import HttpResponse 3 | from django.views.generic.base import View 4 | from reversion.views import create_revision, RevisionMixin 5 | from test_app.models import TestModel 6 | 7 | 8 | def save_obj_view(request): 9 | return HttpResponse(TestModel.objects.create().id) 10 | 11 | 12 | def save_obj_error_view(request): 13 | with transaction.atomic(): 14 | TestModel.objects.create() 15 | raise Exception("Boom!") 16 | 17 | 18 | @create_revision() 19 | def create_revision_view(request): 20 | return save_obj_view(request) 21 | 22 | 23 | class RevisionMixinView(RevisionMixin, View): 24 | 25 | def revision_request_creates_revision(self, request): 26 | silent = request.headers.get('X-Norevision', "false") == "true" 27 | return super().revision_request_creates_revision(request) and not silent 28 | 29 | def dispatch(self, request): 30 | return save_obj_view(request) 31 | -------------------------------------------------------------------------------- /tests/test_project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/etianen/django-reversion/9a7cd7419121e56d65247b050b7aa8a105aa000c/tests/test_project/__init__.py -------------------------------------------------------------------------------- /tests/test_project/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for test_project project. 3 | 4 | Generated by "django-admin startproject" using Django 1.10a1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/dev/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/dev/ref/settings/ 11 | """ 12 | 13 | import os 14 | import getpass 15 | 16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = "lzu78x^s$rit0p*vdt)$1e&hh*)4y=xv))=@zsx(am7t=7406a" 25 | 26 | # SECURITY WARNING: don"t run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = [ 35 | "django.contrib.admin", 36 | "django.contrib.auth", 37 | "django.contrib.contenttypes", 38 | "django.contrib.sessions", 39 | "django.contrib.messages", 40 | "django.contrib.staticfiles", 41 | "reversion", 42 | "test_app", 43 | ] 44 | 45 | MIDDLEWARE = [ 46 | "django.middleware.security.SecurityMiddleware", 47 | "django.contrib.sessions.middleware.SessionMiddleware", 48 | "django.middleware.common.CommonMiddleware", 49 | "django.middleware.csrf.CsrfViewMiddleware", 50 | "django.contrib.auth.middleware.AuthenticationMiddleware", 51 | "django.contrib.messages.middleware.MessageMiddleware", 52 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 53 | ] 54 | 55 | ROOT_URLCONF = "test_project.urls" 56 | 57 | TEMPLATES = [ 58 | { 59 | "BACKEND": "django.template.backends.django.DjangoTemplates", 60 | "DIRS": [], 61 | "APP_DIRS": True, 62 | "OPTIONS": { 63 | "context_processors": [ 64 | "django.template.context_processors.debug", 65 | "django.template.context_processors.request", 66 | "django.contrib.auth.context_processors.auth", 67 | "django.contrib.messages.context_processors.messages", 68 | ], 69 | }, 70 | }, 71 | ] 72 | 73 | WSGI_APPLICATION = "test_project.wsgi.application" 74 | 75 | 76 | # Database 77 | # https://docs.djangoproject.com/en/dev/ref/settings/#databases 78 | 79 | DATABASES = { 80 | "default": { 81 | "ENGINE": "django.db.backends.sqlite3", 82 | "NAME": os.path.join(BASE_DIR, "db.sqlite3"), 83 | }, 84 | "postgres": { 85 | "ENGINE": "django.db.backends.postgresql_psycopg2", 86 | "HOST": os.environ.get("DJANGO_DATABASE_HOST_POSTGRES", ""), 87 | "NAME": os.environ.get("DJANGO_DATABASE_NAME_POSTGRES", "test_project"), 88 | "USER": os.environ.get("DJANGO_DATABASE_USER_POSTGRES", getpass.getuser()), 89 | "PASSWORD": os.environ.get("DJANGO_DATABASE_PASSWORD_POSTGRES", ""), 90 | }, 91 | "mysql": { 92 | "ENGINE": "django.db.backends.mysql", 93 | "HOST": os.environ.get("DJANGO_DATABASE_HOST_MYSQL", ""), 94 | "NAME": os.environ.get("DJANGO_DATABASE_NAME_MYSQL", "test_project"), 95 | "USER": os.environ.get("DJANGO_DATABASE_USER_MYSQL", getpass.getuser()), 96 | "PASSWORD": os.environ.get("DJANGO_DATABASE_PASSWORD_MYSQL", ""), 97 | }, 98 | } 99 | 100 | DEFAULT_AUTO_FIELD = "django.db.models.AutoField" 101 | 102 | # Password validation 103 | # https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators 104 | 105 | AUTH_PASSWORD_VALIDATORS = [ 106 | { 107 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 108 | }, 109 | { 110 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 111 | }, 112 | { 113 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 114 | }, 115 | { 116 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 117 | }, 118 | ] 119 | 120 | 121 | # Internationalization 122 | # https://docs.djangoproject.com/en/dev/topics/i18n/ 123 | 124 | LANGUAGE_CODE = "en-us" 125 | 126 | TIME_ZONE = "UTC" 127 | 128 | USE_I18N = True 129 | 130 | USE_L10N = True 131 | 132 | USE_TZ = True 133 | 134 | 135 | # Static files (CSS, JavaScript, Images) 136 | # https://docs.djangoproject.com/en/dev/howto/static-files/ 137 | 138 | STATIC_URL = "/static/" 139 | -------------------------------------------------------------------------------- /tests/test_project/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import include 2 | from django.urls import path 3 | from django.contrib import admin 4 | 5 | admin.autodiscover() 6 | 7 | urlpatterns = [ 8 | 9 | path("admin/", admin.site.urls), 10 | path("test-app/", include("test_app.urls")), 11 | 12 | ] 13 | -------------------------------------------------------------------------------- /tests/test_project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for test_project project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") 15 | 16 | application = get_wsgi_application() 17 | --------------------------------------------------------------------------------