{"id":15486143,"url":"https://github.com/escaped/django-admin-display","last_synced_at":"2025-04-22T15:25:22.554Z","repository":{"id":47615049,"uuid":"170325983","full_name":"escaped/django-admin-display","owner":"escaped","description":"Simplifies the use of function attributes (eg. `short_description`) for the ModelAdmin and makes mypy happy :)","archived":false,"fork":false,"pushed_at":"2023-04-21T21:12:15.000Z","size":99,"stargazers_count":23,"open_issues_count":3,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-17T04:19:11.395Z","etag":null,"topics":["decorators","django","django-admin","django-helpers","hacktoberfest","helper","mypy"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/escaped.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["escaped"]}},"created_at":"2019-02-12T13:49:10.000Z","updated_at":"2022-01-06T02:31:17.000Z","dependencies_parsed_at":"2024-06-19T02:48:26.572Z","dependency_job_id":"644b3608-842f-4fae-acab-e314822d8da6","html_url":"https://github.com/escaped/django-admin-display","commit_stats":{"total_commits":38,"total_committers":4,"mean_commits":9.5,"dds":"0.10526315789473684","last_synced_commit":"77a4c3c306ce1e7c63ec1c7557109fc4fdbf7c8a"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escaped%2Fdjango-admin-display","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escaped%2Fdjango-admin-display/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escaped%2Fdjango-admin-display/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escaped%2Fdjango-admin-display/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/escaped","download_url":"https://codeload.github.com/escaped/django-admin-display/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250265922,"owners_count":21402196,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["decorators","django","django-admin","django-helpers","hacktoberfest","helper","mypy"],"created_at":"2024-10-02T06:06:44.165Z","updated_at":"2025-04-22T15:25:22.528Z","avatar_url":"https://github.com/escaped.png","language":"Python","funding_links":["https://github.com/sponsors/escaped"],"categories":[],"sub_categories":[],"readme":"# django-admin-display\n\n![PyPI](https://img.shields.io/pypi/v/django-admin-display?style=flat-square)\n![GitHub Workflow Status (master)](https://img.shields.io/github/workflow/status/escaped/django-admin-display/Test%20\u0026%20Lint/master?style=flat-square)\n![Coveralls github branch](https://img.shields.io/coveralls/github/escaped/django-admin-display/master?style=flat-square)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-admin-display?style=flat-square)\n![PyPI - License](https://img.shields.io/pypi/l/django-admin-display?style=flat-square)\n\n\nSimplifies the use of function attributes (eg. `short_description`) for the django admin and makes mypy happy :)\n\n**Note:** Django 3.2+ has [`@display`](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#the-display-decorator) and [`@action`](https://docs.djangoproject.com/en/stable/ref/contrib/admin/actions/#the-action-decorator) decorators built-in.\n\n## Requirements\n\n* Python 3.6.1 or newer\n* Django \u003e= 1.11\n\n## Installation\n\n```sh\npip install django-admin-display\n```\n\n## Usage\n\nIf you want to change the behaviour of how Django displays a read-only value in the admin interface,\nyou can add some [special attributes](\u003ehttps://docs.djangoproject.com/en/2.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display) to the corresponding method.\nSupported values are\n\n`short_description`  \n    Customize the column’s title of the callable.\n\n`empty_value_display`  \n    Show this value instead, if the value of a field is `None`, an empty string, or an iterable without elements.\n\n`admin_order_field`  \n    Indicate that the value is represented by a certain database field.\n\n`boolean`  \n    Display a pretty “on” or “off” icon if the method returns a boolean.\n\n`allow_tags` (deprecated since Django 1.9)  \n    Disable auto-escaping.\n\nThe following example shows, how you normally apply these attributes to an `AdminModel` or a `Model` method.\n\n```python\nclass Company(models.Model):\n    ...\n\n    def owner(self) -\u003e bool:\n        return self.owner.last_name\n    owner.short_description = \"Company owner\"\n    owner.admin_order_field = 'owner__last_name'\n```\n\nThis module replaces the way of defining these attributes by providing a handy decorator.\n\n```python\nfrom django_admin_display import admin_display\n\n\nclass Company(models.Model):\n    ...\n\n    @admin_display(\n        short_description=\"Company owner\",\n        admin_order_field='owner__last_name',\n    )\n    def owner(self) -\u003e bool:\n        return self.owner.last_name\n```\n\n## Why?\n\nThere are mainly two reasons why this module exists.\n\n### Usage with `@property`\n\nIt is quite common that a calculated model property is displayed in the admin interface:\n\n```python\nclass Company(models.Model):\n    ...\n\n    @property\n    def created_on(self) -\u003e datetime.date:\n        return self.created_at.date()\n```\n\nIn order to add special attributes, you have to create a protected method,\nattach the attributes and wrap that method using `property()`:\n\n```python\nclass Company(models.Model):\n    ...\n\n    def _created_on(self) -\u003e datetime.date:\n        return self.created_at.date()\n    _created_on.short_description = \"Created on\"\n    created_on = property(_created_on)\n```\n\nThis is quite cumbersome, hard to read and most people don't know that this is even possible.\nTo overcome these downsides you can achieve the same result using the `@admin_display` decorator:\n\n```python\nfrom django_admin_display import admin_display\n\n\nclass Company(models.Model):\n    ...\n\n    @property\n    @admin_display(\n        short_description = \"Created on\",\n    )\n    def created_on(self) -\u003e datetime.date:\n        return self.created_at.date()\n```\n\n### mypy\n\nIf you are using [mypy](http://mypy-lang.org/), you have probably stumbled over an error similar to this one\n\n\u003e \"Callable[[Any, Any], Any]\" has no attribute \"short_description\"\n\nA common solution is to ignore the type checking by adding `# type: ignore` to the end of the line:\n\n```python\nclass CompanyAdmin(admin.ModelAdmin):\n    ...\n\n    def created_on(self, company: models.Company) -\u003e datetime.date:\n        return company.created_at.date()\n    created_on.short_description = \"Created on\"  # type: ignore\n```\n\nThe issue is already known and heavily discussed on [github](https://github.com/python/mypy/issues/2087).\n\nThis decorator solves the issue by internally using `# type: ignore` and providing a well-defined signature for setting the attributes.\nIt is not an optimal solution but works well until the issue has been resolved.\n\n## Development\n\nThis project uses [poetry](https://poetry.eustace.io/) for packaging and\nmanaging all dependencies and [pre-commit](https://pre-commit.com/) to run\n[flake8](http://flake8.pycqa.org/), [isort](https://pycqa.github.io/isort/),\n[mypy](http://mypy-lang.org/) and [black](https://github.com/python/black).\n\nAdditionally, [pdbpp](https://github.com/pdbpp/pdbpp) and [better-exceptions](https://github.com/qix-/better-exceptions) are installed to provide a better debugging experience.\nTo enable `better-exceptions` you have to run `export BETTER_EXCEPTIONS=1` in your current session/terminal.\n\nClone this repository and run\n\n```bash\npoetry install\npoetry run pre-commit install\n```\n\nto create a virtual enviroment containing all dependencies.\nAfterwards, you can run the test suite using\n\n```bash\npoetry run pytest\n```\n\nThis repository follows the [Conventional Commits](https://www.conventionalcommits.org/)\nstyle.\n\n### Cookiecutter template\n\nThis project was created using [cruft](https://github.com/cruft/cruft) and the\n[cookiecutter-pyproject](https://github.com/escaped/cookiecutter-pypackage) template.\nIn order to update this repository to the latest template version run\n\n```sh\ncruft update\n```\n\nin the root of this repository.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fescaped%2Fdjango-admin-display","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fescaped%2Fdjango-admin-display","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fescaped%2Fdjango-admin-display/lists"}