Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-django-reversion for 
openSUSE:Factory checked in at 2026-03-31 16:26:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-reversion (Old)
 and      /work/SRC/openSUSE:Factory/.python-django-reversion.new.1999 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-django-reversion"

Tue Mar 31 16:26:36 2026 rev:18 rq:1343784 version:6.1.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-django-reversion/python-django-reversion.changes
  2024-11-12 19:22:51.539079826 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-django-reversion.new.1999/python-django-reversion.changes
        2026-03-31 16:26:37.577740330 +0200
@@ -1,0 +2,14 @@
+Mon Mar 30 21:57:16 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 6.1.0:
+  * Added history_order_by_date and get_version_ordering to
+    VersionAdmin (@micmarc).
+  * Prevent Django signals firing when viewing historical
+    versions in admin. Django model signals (pre_save, post_save,
+    pre_delete, post_delete, m2m_changed) are now muted during
+    GET requests to revision views, preventing unintended side
+    effects from signal handlers when users view historical data.
+    (@romanek-adam-b2c2)
+- refresh only-sqlite-test-db.patch
+
+-------------------------------------------------------------------

Old:
----
  django_reversion-5.1.0.tar.gz

New:
----
  django_reversion-6.1.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-django-reversion.spec ++++++
--- /var/tmp/diff_new_pack.siYzei/_old  2026-03-31 16:26:38.581782427 +0200
+++ /var/tmp/diff_new_pack.siYzei/_new  2026-03-31 16:26:38.581782427 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-django-reversion
 #
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,22 +18,22 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-django-reversion
-Version:        5.1.0
+Version:        6.1.0
 Release:        0
 Summary:        A Django extension that provides version control for model 
instances
 License:        BSD-3-Clause
 URL:            https://github.com/etianen/django-reversion
 Source:         
https://files.pythonhosted.org/packages/source/d/django_reversion/django_reversion-%{version}.tar.gz
-Patch0:         only-sqlite-test-db.patch
-BuildRequires:  %{python_module Django > 2.0}
-BuildRequires:  %{python_module base > 3.7}
+Patch:          only-sqlite-test-db.patch
+BuildRequires:  %{python_module Django >= 4.2}
+BuildRequires:  %{python_module base >= 3.9}
 BuildRequires:  %{python_module pip}
 BuildRequires:  %{python_module pytest-django}
 BuildRequires:  %{python_module setuptools}
 BuildRequires:  %{python_module wheel}
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
-Requires:       python-Django > 2.0
+Requires:       python-Django >= 4.2
 Obsoletes:      python-django-reversion-doc
 Obsoletes:      python-django-reversion-lang
 BuildArch:      noarch

++++++ django_reversion-5.1.0.tar.gz -> django_reversion-6.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_reversion-5.1.0/CHANGELOG.rst 
new/django_reversion-6.1.0/CHANGELOG.rst
--- old/django_reversion-5.1.0/CHANGELOG.rst    2024-08-09 23:30:19.000000000 
+0200
+++ new/django_reversion-6.1.0/CHANGELOG.rst    2025-12-12 21:23:38.000000000 
+0100
@@ -3,11 +3,26 @@
 django-reversion changelog
 ==========================
 
+6.1.0 - 2025-12-12
+------------------
+
+- Added ``history_order_by_date`` and ``get_version_ordering`` to 
``VersionAdmin`` (@micmarc).
+
+
+6.0.0 - 2025-09-20
+------------------
+
+- Prevent Django signals firing when viewing historical versions in admin.
+  Django model signals (pre_save, post_save, pre_delete, post_delete, 
m2m_changed)
+  are now muted during GET requests to revision views, preventing unintended
+  side effects from signal handlers when users view historical data. 
(@romanek-adam-b2c2)
+
+
 5.1.0 - 2024-08-09
 ------------------
 
 - Django 5 support (@jeremy-engel).
-- Use bulk_create`` on supported databases (@stianjensen).
+- Use ``bulk_create`` on supported databases (@stianjensen).
 
 
 5.0.12 - 2024-01-30
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_reversion-5.1.0/PKG-INFO 
new/django_reversion-6.1.0/PKG-INFO
--- old/django_reversion-5.1.0/PKG-INFO 2024-08-09 23:30:27.057682300 +0200
+++ new/django_reversion-6.1.0/PKG-INFO 2025-12-12 21:23:49.294037300 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: django-reversion
-Version: 5.1.0
+Version: 6.1.0
 Summary: An extension to the Django web framework that provides version 
control for model instances.
 Home-page: https://github.com/etianen/django-reversion
 Author: Dave Hall
@@ -12,15 +12,25 @@
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
 Classifier: Framework :: Django
-Requires-Python: >=3.8
+Requires-Python: >=3.9
 License-File: LICENSE
 Requires-Dist: django>=4.2
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
 
 ================
 django-reversion
@@ -35,8 +45,8 @@
 Requirements
 ============
 
-- Python 3.7 or later
-- Django 3.2 or later
+- Python 3.8 or later
+- Django 4.2 or later
 
 Features
 ========
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_reversion-5.1.0/README.rst 
new/django_reversion-6.1.0/README.rst
--- old/django_reversion-5.1.0/README.rst       2024-08-09 23:30:19.000000000 
+0200
+++ new/django_reversion-6.1.0/README.rst       2025-12-12 21:23:38.000000000 
+0100
@@ -11,8 +11,8 @@
 Requirements
 ============
 
-- Python 3.7 or later
-- Django 3.2 or later
+- Python 3.8 or later
+- Django 4.2 or later
 
 Features
 ========
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_reversion-5.1.0/django_reversion.egg-info/PKG-INFO 
new/django_reversion-6.1.0/django_reversion.egg-info/PKG-INFO
--- old/django_reversion-5.1.0/django_reversion.egg-info/PKG-INFO       
2024-08-09 23:30:26.000000000 +0200
+++ new/django_reversion-6.1.0/django_reversion.egg-info/PKG-INFO       
2025-12-12 21:23:49.000000000 +0100
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.4
 Name: django-reversion
-Version: 5.1.0
+Version: 6.1.0
 Summary: An extension to the Django web framework that provides version 
control for model instances.
 Home-page: https://github.com/etianen/django-reversion
 Author: Dave Hall
@@ -12,15 +12,25 @@
 Classifier: License :: OSI Approved :: BSD License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
 Classifier: Framework :: Django
-Requires-Python: >=3.8
+Requires-Python: >=3.9
 License-File: LICENSE
 Requires-Dist: django>=4.2
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: home-page
+Dynamic: license
+Dynamic: license-file
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
 
 ================
 django-reversion
@@ -35,8 +45,8 @@
 Requirements
 ============
 
-- Python 3.7 or later
-- Django 3.2 or later
+- Python 3.8 or later
+- Django 4.2 or later
 
 Features
 ========
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_reversion-5.1.0/django_reversion.egg-info/SOURCES.txt 
new/django_reversion-6.1.0/django_reversion.egg-info/SOURCES.txt
--- old/django_reversion-5.1.0/django_reversion.egg-info/SOURCES.txt    
2024-08-09 23:30:27.000000000 +0200
+++ new/django_reversion-6.1.0/django_reversion.egg-info/SOURCES.txt    
2025-12-12 21:23:49.000000000 +0100
@@ -40,6 +40,7 @@
 reversion/models.py
 reversion/revisions.py
 reversion/signals.py
+reversion/utils.py
 reversion/views.py
 reversion/locale/ar/LC_MESSAGES/django.mo
 reversion/locale/ar/LC_MESSAGES/django.po
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_reversion-5.1.0/docs/admin.rst 
new/django_reversion-6.1.0/docs/admin.rst
--- old/django_reversion-5.1.0/docs/admin.rst   2024-08-09 23:30:19.000000000 
+0200
+++ new/django_reversion-6.1.0/docs/admin.rst   2025-12-12 21:23:38.000000000 
+0100
@@ -5,16 +5,6 @@
 
 django-reversion can be used to add rollback and recovery to your admin site.
 
-.. Important::
-    Using the admin integration's preview feature will restore your model 
inside a temporary transaction, then roll
-    back the transaction once the preview is rendered.
-
-    The ``Model.save()`` method, along with any ``pre_save`` and ``post_save`` 
signals, will be run as part of the preview transaction. Any non-transactional 
side-effects of these functions (e.g. filesystem, cache) will **not be rolled 
back** at the end of the preview.
-
-    The ``raw=True`` flag will be set in ``pre_save`` and ``post_save`` 
signals, allowing you to distinguish preview transactions from regular database 
transactions and avoid non-transactional side-effects.
-
-    Alternatively, use `transaction.on_commit() 
<https://docs.djangoproject.com/en/4.2/topics/db/transactions/#django.db.transaction.on_commit>`_
 to register side-effects to be carried out only on committed transactions.
-
 .. Warning::
     The admin integration requires that your database engine supports 
transactions. This is the case for PostgreSQL, SQLite and MySQL InnoDB. If you 
are using MySQL MyISAM, upgrade your database tables to InnoDB!
 
@@ -100,6 +90,11 @@
     If ``True``, revisions will be displayed with the most recent revision 
first.
 
 
+``history_order_by_date = False``
+
+    If ``True``, revisions will be ordered by ``date_created`` instead of the 
numeric version ID.
+
+
 .. _VersionAdmin_register:
 
 ``reversion_register(model, **options)``
@@ -117,3 +112,21 @@
 
     ``options``
         Registration options, see :ref:`reversion.register() <register>`.
+
+.. _VersionAdmin_get_version_ordering:
+
+``get_version_ordering(request)``
+
+    Method that returns a tuple specifying the field names (relative to the 
``Version`` model) for ordering. Semantics are similar to the built-in 
``get_ordering`` method in Django's ``ModelAdmin``.
+
+    Implementations may override this method to achieve custom or dynamic 
ordering of the version queryset. The return value must be a list or tuple. 
Calling ``super()`` returns the default ordering which takes 
``history_latest_first`` and ``history_order_by_date`` into account; this call 
may be omitted if the default ordering is not required.
+
+    .. code:: python
+
+        def get_version_ordering(self, request):
+            if request.user.is_superuser:
+                return ("-revision__date_created", "revision__comment")
+            return super().get_version_ordering(request)
+
+    ``request``
+        The current request.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_reversion-5.1.0/docs/conf.py 
new/django_reversion-6.1.0/docs/conf.py
--- old/django_reversion-5.1.0/docs/conf.py     2024-08-09 23:30:19.000000000 
+0200
+++ new/django_reversion-6.1.0/docs/conf.py     2025-12-12 21:23:38.000000000 
+0100
@@ -21,6 +21,7 @@
 # sys.path.insert(0, os.path.abspath('.'))
 
 import os
+
 from reversion import __version__
 
 # -- General configuration ------------------------------------------------
@@ -45,35 +46,35 @@
 # You can specify multiple suffix as a list of string:
 #
 # source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
 #
 # source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = 'django-reversion'
-copyright = '2016, Dave Hall'
-author = 'Dave Hall'
+project = "django-reversion"
+copyright = "2016, Dave Hall"
+author = "Dave Hall"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = '.'.join(str(x) for x in __version__[:2])
+version = ".".join(str(x) for x in __version__[:2])
 # The full version, including alpha/beta/rc tags.
-release = '.'.join(str(x) for x in __version__)
+release = ".".join(str(x) for x in __version__)
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
 #
 # This is also used if you do content translation via gettext catalogs.
 # Usually you set "language" from the command line for these cases.
-language = 'en'
+language = "en"
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
@@ -87,7 +88,7 @@
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # This patterns also effect to html_static_path and html_extra_path
-exclude_patterns = ['_build', '_include', 'Thumbs.db', '.DS_Store']
+exclude_patterns = ["_build", "_include", "Thumbs.db", ".DS_Store"]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
@@ -109,7 +110,7 @@
 # show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
 # modindex_common_prefix = []
@@ -126,10 +127,8 @@
 # -- Options for HTML output ----------------------------------------------
 
 # Use RTD theme locally.
-if not os.environ.get('READTHEDOCS', None) == 'True':
-    import sphinx_rtd_theme
+if not os.environ.get("READTHEDOCS", None) == "True":
     html_theme = "sphinx_rtd_theme"
-    html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
 
 # The name for this set of Sphinx documents.
 # "<project> v<release> documentation" by default.
@@ -234,34 +233,36 @@
 # html_search_scorer = 'scorer.js'
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'django-reversiondoc'
+htmlhelp_basename = "django-reversiondoc"
 
 # -- Options for LaTeX output ---------------------------------------------
 
 latex_elements = {
-     # The paper size ('letterpaper' or 'a4paper').
-     #
-     # 'papersize': 'letterpaper',
-
-     # The font size ('10pt', '11pt' or '12pt').
-     #
-     # 'pointsize': '10pt',
-
-     # Additional stuff for the LaTeX preamble.
-     #
-     # 'preamble': '',
-
-     # Latex figure (float) alignment
-     #
-     # 'figure_align': 'htbp',
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
 }
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    (master_doc, 'django-reversion.tex', 'django-reversion Documentation',
-     'Dave Hall', 'manual'),
+    (
+        master_doc,
+        "django-reversion.tex",
+        "django-reversion Documentation",
+        "Dave Hall",
+        "manual",
+    ),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -296,8 +297,7 @@
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    (master_doc, 'django-reversion', 'django-reversion Documentation',
-     [author], 1)
+    (master_doc, "django-reversion", "django-reversion Documentation", 
[author], 1)
 ]
 
 # If true, show URL addresses after external links.
@@ -311,9 +311,15 @@
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-    (master_doc, 'django-reversion', 'django-reversion Documentation',
-     author, 'django-reversion', 'One line description of project.',
-     'Miscellaneous'),
+    (
+        master_doc,
+        "django-reversion",
+        "django-reversion Documentation",
+        author,
+        "django-reversion",
+        "One line description of project.",
+        "Miscellaneous",
+    ),
 ]
 
 # Documents to append as an appendix to all manuals.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_reversion-5.1.0/reversion/__init__.py 
new/django_reversion-6.1.0/reversion/__init__.py
--- old/django_reversion-5.1.0/reversion/__init__.py    2024-08-09 
23:30:19.000000000 +0200
+++ new/django_reversion-6.1.0/reversion/__init__.py    2025-12-12 
21:23:38.000000000 +0100
@@ -36,4 +36,4 @@
         get_registered_models,
     )
 
-__version__ = VERSION = (5, 1, 0)
+__version__ = VERSION = (6, 1, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_reversion-5.1.0/reversion/admin.py 
new/django_reversion-6.1.0/reversion/admin.py
--- old/django_reversion-5.1.0/reversion/admin.py       2024-08-09 
23:30:19.000000000 +0200
+++ new/django_reversion-6.1.0/reversion/admin.py       2025-12-12 
21:23:38.000000000 +0100
@@ -1,21 +1,25 @@
-from contextlib import contextmanager
-from django.db import models, transaction, connections
+from contextlib import contextmanager, nullcontext
+
 from django.contrib import admin, messages
 from django.contrib.admin import options
 from django.contrib.admin.utils import unquote, quote
 from django.contrib.contenttypes.admin import GenericInlineModelAdmin
 from django.contrib.contenttypes.fields import GenericRelation
 from django.core.exceptions import PermissionDenied, ImproperlyConfigured
+from django.db import models, transaction, connections
+from django.db.models.signals import pre_save, post_save, pre_delete, 
post_delete, m2m_changed
 from django.shortcuts import get_object_or_404, render, redirect
 from django.urls import reverse, re_path
+from django.utils.encoding import force_str
+from django.utils.formats import localize
 from django.utils.text import capfirst
 from django.utils.timezone import template_localtime
 from django.utils.translation import gettext as _
-from django.utils.encoding import force_str
-from django.utils.formats import localize
+
 from reversion.errors import RevertError
 from reversion.models import Version
 from reversion.revisions import is_active, register, is_registered, 
set_comment, create_revision, set_user
+from reversion.utils import mute_signals
 
 
 class _RollBackRevisionView(Exception):
@@ -38,10 +42,25 @@
 
     history_latest_first = False
 
+    history_order_by_date = False
+
     def reversion_register(self, model, **kwargs):
         """Registers the model with reversion."""
         register(model, **kwargs)
 
+    def get_version_ordering(self, request):
+        """Hook for specifying custom field ordering for the version 
queryset."""
+        # Default ordering logic uses version ID only
+        order_fields = ["pk"]
+        # Setting history_order_by_date causes revision date to be used as the 
primary sort key
+        # Keep version ID as secondary in case of identical revision dates
+        if self.history_order_by_date:
+            order_fields.insert(0, "revision__date_created")
+        # Setting history_latest_first causes order to be reversed on all 
fields
+        if self.history_latest_first:
+            order_fields = [f"-{field}" for field in order_fields]
+        return tuple(order_fields)
+
     @contextmanager
     def create_revision(self, request):
         with create_revision():
@@ -58,11 +77,10 @@
             "reversion/%s" % template_name,
         )
 
-    def _reversion_order_version_queryset(self, queryset):
+    def _reversion_order_version_queryset(self, request, queryset):
         """Applies the correct ordering to the given version queryset."""
-        if not self.history_latest_first:
-            queryset = queryset.order_by("pk")
-        return queryset
+        ordering = self.get_version_ordering(request) or ()
+        return queryset.order_by(*ordering)
 
     # Messages.
 
@@ -165,9 +183,19 @@
         # Check that database transactions are supported.
         if not connections[version.db].features.uses_savepoints:
             raise ImproperlyConfigured("Cannot use VersionAdmin with a 
database that does not support savepoints.")
+
+        # Determine whether to mute signals based on request method
+        if request.method == "GET":
+            # For GET requests (viewing revisions), mute all Django model 
signals
+            # to prevent unintended side effects from signal handlers
+            signal_context = mute_signals(pre_save, post_save, pre_delete, 
post_delete, m2m_changed)
+        else:
+            # For POST requests (actual reverts), allow signals to fire 
normally
+            signal_context = nullcontext()
+
         # Run the view.
         try:
-            with transaction.atomic(using=version.db):
+            with transaction.atomic(using=version.db), signal_context:
                 # Revert the revision.
                 version.revision.revert(delete=True)
                 # Run the normal changeform view.
@@ -246,6 +274,7 @@
         model = self.model
         opts = model._meta
         deleted = self._reversion_order_version_queryset(
+            request,
             Version.objects.get_deleted(self.model).select_related("revision")
         )
         # Set the app name.
@@ -286,10 +315,10 @@
                 ),
             }
             for version
-            in 
self._reversion_order_version_queryset(Version.objects.get_for_object_reference(
+            in self._reversion_order_version_queryset(request, 
Version.objects.get_for_object_reference(
                 self.model,
                 unquote(object_id),  # Underscores in primary key get quoted 
to "_5F"
-            ).select_related("revision__user"))
+            ).select_related("revision", "revision__user"))
         ]
         # Compile the context.
         context = {"action_list": action_list}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_reversion-5.1.0/reversion/utils.py 
new/django_reversion-6.1.0/reversion/utils.py
--- old/django_reversion-5.1.0/reversion/utils.py       1970-01-01 
01:00:00.000000000 +0100
+++ new/django_reversion-6.1.0/reversion/utils.py       2025-12-12 
21:23:38.000000000 +0100
@@ -0,0 +1,47 @@
+"""
+Utility functions for django-reversion.
+"""
+from contextlib import contextmanager
+
+
+@contextmanager
+def mute_signals(*signals):
+    """
+    Context manager that temporarily disables and then restores Django signals.
+
+    This is useful when performing operations that shouldn't trigger signal 
handlers,
+    such as viewing historical revisions where the signals were already fired 
when
+    the data was originally saved.
+
+    Args:
+        *signals (django.dispatch.dispatcher.Signal): any Django signals to 
mute
+
+    Example:
+        from django.db.models.signals import pre_save, post_save
+
+        with mute_signals(pre_save, post_save):
+            # Any save operations here won't fire pre_save or post_save signals
+            obj.save()
+    """
+    paused = {}
+
+    # Store current receivers and mute signals (thread-safe)
+    for signal in signals:
+        with signal.lock:
+            # Store the current receivers for restoration later
+            paused[signal] = signal.receivers[:]
+            # Clear the receivers list to mute the signal
+            signal.receivers = []
+            # Clear cache since we're bypassing connect/disconnect
+            signal.sender_receivers_cache.clear()
+
+    try:
+        yield
+    finally:
+        # Restore the original receivers (thread-safe)
+        for signal, original_receivers in paused.items():
+            with signal.lock:
+                # Restore original receivers, preserving any new ones added 
during muting
+                signal.receivers = original_receivers + signal.receivers
+                # Clear cache to ensure consistency
+                signal.sender_receivers_cache.clear()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_reversion-5.1.0/setup.py 
new/django_reversion-6.1.0/setup.py
--- old/django_reversion-5.1.0/setup.py 2024-08-09 23:30:19.000000000 +0200
+++ new/django_reversion-6.1.0/setup.py 2025-12-12 21:23:38.000000000 +0100
@@ -39,7 +39,7 @@
     install_requires=[
         "django>=4.2",
     ],
-    python_requires=">=3.8",
+    python_requires=">=3.9",
     classifiers=[
         "Development Status :: 5 - Production/Stable",
         "Environment :: Web Environment",
@@ -47,11 +47,11 @@
         "License :: OSI Approved :: BSD License",
         "Operating System :: OS Independent",
         "Programming Language :: Python",
-        "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: 3.12",
+        "Programming Language :: Python :: 3.13",
         "Framework :: Django",
     ],
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_reversion-5.1.0/tests/test_app/tests/test_admin.py 
new/django_reversion-6.1.0/tests/test_app/tests/test_admin.py
--- old/django_reversion-5.1.0/tests/test_app/tests/test_admin.py       
2024-08-09 23:30:19.000000000 +0200
+++ new/django_reversion-6.1.0/tests/test_app/tests/test_admin.py       
2025-12-12 21:23:38.000000000 +0100
@@ -1,7 +1,11 @@
 import re
+from datetime import datetime, timedelta
+
 from django.contrib import admin
 from django.contrib.contenttypes.admin import GenericTabularInline
+from django.db.models.signals import pre_save, post_save, pre_delete, 
post_delete, m2m_changed
 from django.shortcuts import resolve_url
+
 import reversion
 from reversion.admin import VersionAdmin
 from reversion.models import Version
@@ -128,6 +132,69 @@
         self.assertEqual(self.obj.parent_name, "parent v1")
 
 
+class AdminRevisionViewSignalsTest(LoginMixin, AdminMixin, TestBase):
+    """Test that Django model signals are muted during GET requests to 
revision views."""
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.signal_fired = None
+
+    def signal_receiver(self, sender, **kwargs):
+        """Receiver that tracks which signal was fired."""
+        self.signal_fired = sender
+        raise RuntimeError(f"Django signal was fired for {sender}!")
+
+    def setUp(self):
+        super().setUp()
+        with reversion.create_revision():
+            self.obj = TestModelParent.objects.create()
+
+        # Connect all the model signals that should be muted
+        self.signals_to_test = [pre_save, post_save, pre_delete, post_delete, 
m2m_changed]
+        for signal in self.signals_to_test:
+            signal.connect(receiver=self.signal_receiver, 
sender=TestModelParent)
+
+    def tearDown(self):
+        # Disconnect all signals
+        for signal in self.signals_to_test:
+            signal.disconnect(receiver=self.signal_receiver, 
sender=TestModelParent)
+        super().tearDown()
+
+    def testGetForRevisionViewDoesntFireDjangoSignals(self):
+        """Test that viewing a revision (GET request) doesn't fire Django 
model signals."""
+        self.signal_fired = None
+
+        # This should NOT fire any signals since it's a GET request
+        response = self.client.get(resolve_url(
+            "admin:test_app_testmodelparent_revision",
+            self.obj.pk,
+            Version.objects.get_for_object(self.obj)[0].pk,
+        ))
+
+        # The request should succeed (no signal should have been fired)
+        self.assertEqual(response.status_code, 200)
+        self.assertIsNone(self.signal_fired, "No signals should fire during 
GET request")
+
+    def testPostForRevisionViewFiresDjangoSignals(self):
+        """Test that reverting a revision (POST request) properly fires Django 
model signals."""
+        self.signal_fired = None
+
+        # This SHOULD fire signals since it's a POST request (actual revert)
+        with self.assertRaises(RuntimeError) as cm:
+            self.client.post(resolve_url(
+                "admin:test_app_testmodelparent_revision",
+                self.obj.pk,
+                Version.objects.get_for_object(self.obj)[0].pk,
+            ), {
+                "name": "v1",
+                "parent_name": "parent v1",
+            })
+
+        # Verify that signals were indeed fired during POST
+        self.assertIn("Django signal was fired", str(cm.exception))
+        self.assertIsNotNone(self.signal_fired, "Signals should fire during 
POST request")
+
+
 class AdminRecoverViewTest(LoginMixin, AdminMixin, TestBase):
 
     def setUp(self):
@@ -184,6 +251,255 @@
             Version.objects.get_for_model(TestModelParent).get().pk,
         ))
 
+    def testHistorylistViewOrderDefault(self):
+        # Create an object and multiple revisions.
+        with reversion.create_revision():
+            obj = TestModelParent.objects.create(name="v1", parent_name="p1")
+        with reversion.create_revision():
+            obj.name = "v2"
+            obj.save()
+        with reversion.create_revision():
+            obj.name = "v3"
+            obj.save()
+
+        # Fetch history page.
+        response = 
self.client.get(resolve_url("admin:test_app_testmodelparent_history", obj.pk))
+        content = response.content.decode()
+
+        # Compute expected order: default VersionAdmin orders by pk ascending 
(oldest first).
+        version_ids = 
list(Version.objects.get_for_object(obj).values_list("pk", flat=True))
+        expected_order = sorted(version_ids)
+
+        # Build the URLs as rendered in the history list and assert their 
order.
+        urls_in_order = [
+            resolve_url(
+                "admin:test_app_testmodelparent_revision",
+                obj.pk,
+                vid,
+            )
+            for vid in expected_order
+        ]
+
+        # Ensure each subsequent URL appears later in the content than the 
previous one.
+        last_index = -1
+        for url in urls_in_order:
+            index = content.find(url)
+            self.assertNotEqual(index, -1, f"Expected to find {url} in history 
page")
+            self.assertGreater(index, last_index, "History list is not ordered 
by ascending version pk (oldest first)")
+            last_index = index
+
+
+class AdminHistoryViewLatestFirstTest(LoginMixin, TestBase):
+
+    class TestModelParentAdminLatestFirst(VersionAdmin):
+        history_latest_first = True
+
+    def setUp(self):
+        super().setUp()
+        # Register a custom admin with history_latest_first enabled
+        admin.site.register(TestModelParent, 
self.TestModelParentAdminLatestFirst)
+        self.reloadUrls()
+
+    def tearDown(self):
+        super().tearDown()
+        admin.site.unregister(TestModelParent)
+        self.reloadUrls()
+
+    def testHistorylistViewOrderLatestFirst(self):
+        # Create an object and multiple revisions.
+        with reversion.create_revision():
+            obj = TestModelParent.objects.create(name="v1", parent_name="p1")
+        with reversion.create_revision():
+            obj.name = "v2"
+            obj.save()
+        with reversion.create_revision():
+            obj.name = "v3"
+            obj.save()
+
+        # Fetch history page.
+        response = 
self.client.get(resolve_url("admin:test_app_testmodelparent_history", obj.pk))
+        content = response.content.decode()
+
+        # Expected order: with history_latest_first=True, versions are ordered 
by pk descending (newest first).
+        version_ids = 
list(Version.objects.get_for_object(obj).values_list("pk", flat=True))
+        expected_order = sorted(version_ids, reverse=True)
+
+        urls_in_order = [
+            resolve_url("admin:test_app_testmodelparent_revision", obj.pk, vid)
+            for vid in expected_order
+        ]
+
+        last_index = -1
+        for url in urls_in_order:
+            index = content.find(url)
+            self.assertNotEqual(index, -1, f"Expected to find {url} in history 
page")
+            self.assertGreater(index, last_index, "History list is not ordered 
by descending version pk (newest first)")
+            last_index = index
+
+
+class AdminHistoryViewOrderByDateTest(LoginMixin, TestBase):
+
+    class TestModelParentAdminOrderByDate(VersionAdmin):
+        history_order_by_date = True
+
+    def setUp(self):
+        super().setUp()
+        # Register a custom admin with history_order_by_date enabled
+        admin.site.register(TestModelParent, 
self.TestModelParentAdminOrderByDate)
+        self.reloadUrls()
+
+    def tearDown(self):
+        super().tearDown()
+        admin.site.unregister(TestModelParent)
+        self.reloadUrls()
+
+    def testHistorylistViewOrderByDate(self):
+        # Create an object and multiple revisions with increasing timestamps.
+        with reversion.create_revision():
+            obj = TestModelParent.objects.create(name="v1", parent_name="p1")
+            # Use an out-of-sequence date to verify correct ordering
+            reversion.set_date_created(datetime.now() + timedelta(days=1))
+        with reversion.create_revision():
+            obj.name = "v2"
+            obj.save()
+        with reversion.create_revision():
+            obj.name = "v3"
+            obj.save()
+
+        # Fetch history page.
+        response = 
self.client.get(resolve_url("admin:test_app_testmodelparent_history", obj.pk))
+        content = response.content.decode()
+
+        # Expected order: ordered by revision creation date ascending (oldest 
date first).
+        versions = (
+            Version.objects.get_for_object(obj)
+            .select_related("revision")
+            .order_by("revision__date_created", "pk")
+        )
+        expected_order = list(versions.values_list("pk", flat=True))
+
+        urls_in_order = [
+            resolve_url("admin:test_app_testmodelparent_revision", obj.pk, vid)
+            for vid in expected_order
+        ]
+
+        last_index = -1
+        for url in urls_in_order:
+            index = content.find(url)
+            self.assertNotEqual(index, -1, f"Expected to find {url} in history 
page")
+            self.assertGreater(index, last_index, "History list is not ordered 
by revision date (oldest first)")
+            last_index = index
+
+
+class AdminHistoryViewLatestFirstOrderByDateTest(LoginMixin, TestBase):
+
+    class TestModelParentAdminLatestFirstOrderByDate(VersionAdmin):
+        history_latest_first = True
+        history_order_by_date = True
+
+    def setUp(self):
+        super().setUp()
+        # Register a custom admin with both flags enabled
+        admin.site.register(TestModelParent, 
self.TestModelParentAdminLatestFirstOrderByDate)
+        self.reloadUrls()
+
+    def tearDown(self):
+        super().tearDown()
+        admin.site.unregister(TestModelParent)
+        self.reloadUrls()
+
+    def testHistorylistViewOrderLatestFirstByDate(self):
+        # Create an object and multiple revisions with increasing timestamps.
+        with reversion.create_revision():
+            obj = TestModelParent.objects.create(name="v1", parent_name="p1")
+            # Use an out-of-sequence date to verify correct ordering
+            reversion.set_date_created(datetime.now() + timedelta(days=1))
+        with reversion.create_revision():
+            obj.name = "v2"
+            obj.save()
+        with reversion.create_revision():
+            obj.name = "v3"
+            obj.save()
+
+        # Fetch history page.
+        response = 
self.client.get(resolve_url("admin:test_app_testmodelparent_history", obj.pk))
+        content = response.content.decode()
+
+        # Expected order: ordered by revision creation date descending (newest 
date first).
+        versions = (
+            Version.objects.get_for_object(obj)
+            .select_related("revision")
+            .order_by("-revision__date_created", "-pk")
+        )
+        expected_order = list(versions.values_list("pk", flat=True))
+
+        urls_in_order = [
+            resolve_url("admin:test_app_testmodelparent_revision", obj.pk, vid)
+            for vid in expected_order
+        ]
+
+        last_index = -1
+        for url in urls_in_order:
+            index = content.find(url)
+            self.assertNotEqual(index, -1, f"Expected to find {url} in history 
page")
+            self.assertGreater(index, last_index, "History list is not ordered 
by revision date (newest first)")
+            last_index = index
+
+
+class AdminHistoryViewCustomOrderingTest(LoginMixin, TestBase):
+    class TestModelParentAdminCustomOrdering(VersionAdmin):
+        def get_version_ordering(self, request):
+            return ("revision__comment",)
+
+    def setUp(self):
+        super().setUp()
+        # Register a custom admin with history_order_by_date enabled
+        admin.site.register(TestModelParent, 
self.TestModelParentAdminCustomOrdering)
+        self.reloadUrls()
+
+    def tearDown(self):
+        super().tearDown()
+        admin.site.unregister(TestModelParent)
+        self.reloadUrls()
+
+    def testHistorylistViewCustomOrdering(self):
+        # Create an object and multiple revisions with increasing timestamps.
+        with reversion.create_revision():
+            obj = TestModelParent.objects.create(name="v1", parent_name="p1")
+            reversion.set_comment("B")
+        with reversion.create_revision():
+            obj.name = "v2"
+            obj.save()
+            reversion.set_comment("A")
+        with reversion.create_revision():
+            obj.name = "v3"
+            obj.save()
+            reversion.set_comment("C")
+
+        # Fetch history page.
+        response = 
self.client.get(resolve_url("admin:test_app_testmodelparent_history", obj.pk))
+        content = response.content.decode()
+
+        # Expected order: ordered by revision comment ascending.
+        versions = (
+            Version.objects.get_for_object(obj)
+            .select_related("revision")
+            .order_by("revision__comment")
+        )
+        expected_order = list(versions.values_list("pk", flat=True))
+
+        urls_in_order = [
+            resolve_url("admin:test_app_testmodelparent_revision", obj.pk, vid)
+            for vid in expected_order
+        ]
+
+        last_index = -1
+        for url in urls_in_order:
+            index = content.find(url)
+            self.assertNotEqual(index, -1, f"Expected to find {url} in history 
page")
+            self.assertGreater(index, last_index, "History list is not ordered 
by comment ascending")
+            last_index = index
+
 
 class AdminQuotingTest(LoginMixin, AdminMixin, TestBase):
 

++++++ only-sqlite-test-db.patch ++++++
--- /var/tmp/diff_new_pack.siYzei/_old  2026-03-31 16:26:38.829792826 +0200
+++ /var/tmp/diff_new_pack.siYzei/_new  2026-03-31 16:26:38.829792826 +0200
@@ -1,14 +1,6 @@
-commit 89982858fd1d7b070acba079dac8d35872541d4e
-Author: John Vandenberg <[email protected]>
-Date:   Tue Dec 28 09:52:48 2021 +0800
-
-    Remove mysql and postgres databases
-
-Index: django-reversion-5.0.12/tests/test_project/settings.py
-===================================================================
---- django-reversion-5.0.12.orig/tests/test_project/settings.py
-+++ django-reversion-5.0.12/tests/test_project/settings.py
-@@ -81,20 +81,6 @@ DATABASES = {
+--- a/tests/test_project/settings.py
++++ b/tests/test_project/settings.py
+@@ -81,20 +81,6 @@
          "ENGINE": "django.db.backends.sqlite3",
          "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
      },

Reply via email to