Skip to content

Commit

Permalink
Merge pull request #368 from AndreyMZ/pr
Browse files Browse the repository at this point in the history
Add support for ordering with `OrderBy` or `F`
  • Loading branch information
jrief authored Jul 31, 2023
2 parents 396aada + 42297ad commit d7978be
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 18 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,9 @@ max_line_length = 100
indent_style = space
indent_size = 2

[*.yml]
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false
5 changes: 4 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ jobs:

strategy:
matrix:
python-version: ["3.8", "3.9", "3.10"]
python-version: ["3.8", "3.9", "3.10", "3.11"]
node-version: ["16.x"]
django-version: ["4.0.*", "4.1.*", "4.2.*"]
exclude: # https://docs.djangoproject.com/en/4.2/faq/install/#what-python-version-can-i-use-with-django
- python-version: "3.11"
django-version: "4.0.*"

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion adminsortable2/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.1.8'
__version__ = '2.1.9'
39 changes: 30 additions & 9 deletions adminsortable2/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations

import json
from pathlib import Path
from itertools import chain
from types import MethodType
from typing import Optional

from django import VERSION as DJANGO_VERSION
from django.conf import settings
Expand All @@ -11,8 +14,9 @@
from django.core.exceptions import ImproperlyConfigured
from django.core.paginator import EmptyPage
from django.db import router, transaction
from django.db.models import OrderBy
from django.db.models.aggregates import Max
from django.db.models.expressions import F
from django.db.models.expressions import BaseExpression, F
from django.db.models.functions import Coalesce
from django.db.models.signals import post_save, pre_save
from django.forms import widgets
Expand All @@ -26,24 +30,39 @@
__all__ = ['SortableAdminMixin', 'SortableInlineAdminMixin']


def _parse_ordering_part(part: OrderBy | BaseExpression | F | str) -> tuple[str, Optional[str]]:
if isinstance(part, str):
return ('-', part[1:]) if part.startswith('-') else ('', part)
elif isinstance(part, OrderBy) and isinstance(part.expression, F):
return ('-' if part.descending else ''), part.expression.name
elif isinstance(part, F):
return '', part.name
else:
return '', None


def _get_default_ordering(model, model_admin):
try:
# first try with the model admin ordering
_, prefix, field_name = model_admin.ordering[0].rpartition('-')
prefix, field_name = _parse_ordering_part(model_admin.ordering[0])
except (AttributeError, IndexError, TypeError):
pass
else:
return prefix, field_name
if field_name is not None:
return prefix, field_name

try:
# then try with the model ordering
_, prefix, field_name = model._meta.ordering[0].rpartition('-')
prefix, field_name = _parse_ordering_part(model._meta.ordering[0])
except (AttributeError, IndexError):
raise ImproperlyConfigured(
f"Model {model.__module__}.{model.__name__} requires a list or tuple 'ordering' in its Meta class"
)
pass
else:
return prefix, field_name
if field_name is not None:
return prefix, field_name

raise ImproperlyConfigured(
f"Model {model.__module__}.{model.__name__} requires a list or tuple 'ordering' in its Meta class"
)


class MovePageActionForm(admin.helpers.ActionForm):
Expand Down Expand Up @@ -174,7 +193,9 @@ def get_actions(self, request):
def get_changelist_instance(self, request):
cl = super().get_changelist_instance(request)
qs = self.get_queryset(request)
_, order_direction, order_field = cl.get_ordering(request, qs)[0].rpartition('-')
ordering = cl.get_ordering(request, qs)
assert len(ordering) > 0 # `ChangeList.get_ordering` always returns deterministic ordering.
order_direction, order_field = _parse_ordering_part(ordering[0])
if order_field == self.default_order_field:
self.enable_sorting = True
self.order_by = f'{order_direction}{order_field}'
Expand Down
17 changes: 11 additions & 6 deletions docs/source/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ version of NodeJS.
# we use the default template files and patch them, rather than using our own modified one
django_version=$(python -c 'from django import VERSION; print("{0}.{1}".format(*VERSION))')
curl --silent --output adminsortable2/templates/adminsortable2/edit_inline/stacked-django-$django_version.html https://raw.githubusercontent.com/django/django/stable/$django_version.x/django/contrib/admin/templates/admin/edit_inline/stacked.html
curl --silent --output adminsortable2/templates/adminsortable2/edit_inline/tabular-django-$django_version.html https://raw.githubusercontent.com/django/django/stable/$django_version.x/django/contrib/admin/templates/admin/edit_inline/tabular.html
mkdir adminsortable2/templates/adminsortable2/edit_inline
curl --no-progress-meter --output adminsortable2/templates/adminsortable2/edit_inline/stacked-django-$django_version.html https://raw.githubusercontent.com/django/django/stable/$django_version.x/django/contrib/admin/templates/admin/edit_inline/stacked.html
curl --no-progress-meter --output adminsortable2/templates/adminsortable2/edit_inline/tabular-django-$django_version.html https://raw.githubusercontent.com/django/django/stable/$django_version.x/django/contrib/admin/templates/admin/edit_inline/tabular.html
patch -p0 adminsortable2/templates/adminsortable2/edit_inline/stacked-django-$django_version.html patches/stacked-django-4.0.patch
patch -p0 adminsortable2/templates/adminsortable2/edit_inline/tabular-django-$django_version.html patches/tabular-django-4.0.patch
Expand Down Expand Up @@ -103,19 +104,23 @@ Follow these steps to run all unit- and end-to-end tests.
git clone https://github.com/jrief/django-admin-sortable2.git
cd django-admin-sortable2
npm install --also=dev
npm install --include=dev
npm run build
python -m pip install Django
python -m pip install -r testapp/requirements.txt
python -m playwright install
python -m playwright install-deps
python -m pytest testapp
# we use the default template files and patch them, rather than using our own modified one
django_version=$(python -c 'from django import VERSION; print("{0}.{1}".format(*VERSION))')
curl --silent --output adminsortable2/templates/adminsortable2/edit_inline/stacked-django-$django_version.html https://raw.githubusercontent.com/django/django/stable/$django_version.x/django/contrib/admin/templates/admin/edit_inline/stacked.html
curl --silent --output adminsortable2/templates/adminsortable2/edit_inline/tabular-django-$django_version.html https://raw.githubusercontent.com/django/django/stable/$django_version.x/django/contrib/admin/templates/admin/edit_inline/tabular.html
mkdir adminsortable2/templates/adminsortable2/edit_inline
curl --no-progress-meter --output adminsortable2/templates/adminsortable2/edit_inline/stacked-django-$django_version.html https://raw.githubusercontent.com/django/django/stable/$django_version.x/django/contrib/admin/templates/admin/edit_inline/stacked.html
curl --no-progress-meter --output adminsortable2/templates/adminsortable2/edit_inline/tabular-django-$django_version.html https://raw.githubusercontent.com/django/django/stable/$django_version.x/django/contrib/admin/templates/admin/edit_inline/tabular.html
patch -p0 adminsortable2/templates/adminsortable2/edit_inline/stacked-django-$django_version.html patches/stacked-django-4.0.patch
patch -p0 adminsortable2/templates/adminsortable2/edit_inline/tabular-django-$django_version.html patches/tabular-django-4.0.patch
python -m pytest testapp
.. _Playwright-Python: https://playwright.dev/python/
.. _pytest-django: https://pytest-django.readthedocs.io/en/latest/

Expand Down
1 change: 0 additions & 1 deletion testapp/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from django.contrib import admin
from django.template.response import TemplateResponse

from adminsortable2.admin import SortableAdminBase, SortableAdminMixin, SortableInlineAdminMixin, SortableStackedInline, SortableTabularInline

Expand Down
18 changes: 18 additions & 0 deletions testapp/test_parse_ordering_part.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from django.db.models import F, OrderBy

# noinspection PyProtectedMember
from adminsortable2.admin import _parse_ordering_part as parse


def test():
assert parse('my_order') == ('', 'my_order')
assert parse(F('my_order')) == ('', 'my_order')
assert parse(F('my_order').asc()) == ('', 'my_order')
assert parse(OrderBy(F('my_order'))) == ('', 'my_order')
assert parse(OrderBy(F('my_order'), descending=False)) == ('', 'my_order')

assert parse('-my_order') == ('-', 'my_order')
assert parse(F('my_order').desc()) == ('-', 'my_order')
assert parse(OrderBy(F('my_order'), descending=True)) == ('-', 'my_order')

assert parse(F("foo") + F("bar")) == ('', None)

0 comments on commit d7978be

Please sign in to comment.