Skip to content

Commit

Permalink
Annotate admin_order_field to also accept a BaseExpression
Browse files Browse the repository at this point in the history
  • Loading branch information
brianhelba committed Nov 5, 2020
1 parent 52dbef4 commit 4098c26
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 7 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

* [@adamchainz](https://github.com/adamchainz/)
* [@escaped](https://github.com/escaped/)

* [@brianhelba](https://github.com/brianhelba)
5 changes: 3 additions & 2 deletions django_admin_display/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from typing import Callable, Optional, TypeVar
from typing import Callable, Optional, TypeVar, Union

import django
from django.db.models.expressions import BaseExpression

ReturnType = TypeVar('ReturnType')
FuncType = Callable[..., ReturnType]
Func = TypeVar('Func', bound=FuncType)


def admin_display(
admin_order_field: Optional[str] = None,
admin_order_field: Optional[Union[str, BaseExpression]] = None,
allow_tags: Optional[bool] = None, # deprecated in django >= 2.0
boolean: Optional[bool] = None,
empty_value_display: Optional[str] = None,
Expand Down
3 changes: 3 additions & 0 deletions tests/test_decorator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import django
import pytest
from django.db.models import F
from django.db.models.functions import Lower

from django_admin_display import admin_display

Expand All @@ -9,6 +11,7 @@

OPTIONS = [
('admin_order_field', 'radius'),
('admin_order_field', Lower(F('person_name'))),
('boolean', True),
('empty_value_display', 'Undefined'),
('short_description', 'Is big?'),
Expand Down
32 changes: 28 additions & 4 deletions tests/test_mypy.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
import pytest
from django.db.models import F, Func
from mypy import api

from .test_decorator import OPTIONS


def safe_repr(value):
if isinstance(value, Func):
# Django's repr for expressions does not quote string parameters
# Copy all expressions, wrapping their value in a "repr"
value = value.copy()
value.set_source_expressions(
[
expression.__class__(
repr(
expression.name
if isinstance(expression, F)
else expression.value
)
)
for expression in value.source_expressions
]
)

return repr(value)


@pytest.mark.parametrize('attribute, value', OPTIONS)
def test_failure(attribute, value):
value = f'"{value}"' if isinstance(value, str) else value
code = f'''
from django import admin
from django.db import models
from django.db.models import F
from django.db.models.functions import Lower
class SampleAdmin(admin.ModelAdmin):
def foo(self, obj: models.Model) -> int:
return 1
foo.{attribute} = {value}
foo.{attribute} = {safe_repr(value)}
'''

result = api.run(['-c', code])
Expand All @@ -25,15 +48,16 @@ def foo(self, obj: models.Model) -> int:

@pytest.mark.parametrize('attribute, value', OPTIONS)
def test_success(attribute, value):
value = f'"{value}"' if isinstance(value, str) else value
code = f'''
from django import admin
from django.db import models
from django_admin_display import admin_display
from django.db.models import F
from django.db.models.functions import Lower
class SampleAdmin(admin.ModelAdmin):
@admin_display({attribute}={value})
@admin_display({attribute}={safe_repr(value)})
def foo(self, obj: models.Model) -> int:
return 1
'''
Expand Down

0 comments on commit 4098c26

Please sign in to comment.