{"id":15568631,"url":"https://github.com/jayvdb/django-dunder","last_synced_at":"2025-06-10T12:40:06.649Z","repository":{"id":57419828,"uuid":"272995493","full_name":"jayvdb/django-dunder","owner":"jayvdb","description":"Django app to attach usable __repr__ and __str__ to any and all Django models, using unmigrated __unicode__ on Python 3","archived":false,"fork":false,"pushed_at":"2021-10-27T01:36:32.000Z","size":67,"stargazers_count":2,"open_issues_count":12,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-29T18:17:03.962Z","etag":null,"topics":["django-admin","django-check","django-meta","django-models","django-testing","dunder-methods","monkey-patching","python2","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jayvdb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-17T14:26:55.000Z","updated_at":"2021-10-27T01:36:35.000Z","dependencies_parsed_at":"2022-09-01T04:51:42.074Z","dependency_job_id":null,"html_url":"https://github.com/jayvdb/django-dunder","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayvdb%2Fdjango-dunder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayvdb%2Fdjango-dunder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayvdb%2Fdjango-dunder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayvdb%2Fdjango-dunder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jayvdb","download_url":"https://codeload.github.com/jayvdb/django-dunder/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jayvdb%2Fdjango-dunder/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259078426,"owners_count":22802127,"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":["django-admin","django-check","django-meta","django-models","django-testing","dunder-methods","monkey-patching","python2","python3"],"created_at":"2024-10-02T17:19:51.618Z","updated_at":"2025-06-10T12:40:06.546Z","avatar_url":"https://github.com/jayvdb.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# django-dunder\n\ndjango-dunder is an installable app to automatically provide customisable\n`__repr__` and `__str__` for other Django third-party installable apps\nand for project apps.\n\nOn Python 3, it can use `__unicode__` if present and `__str__` is missing,\nsuch as apps that work on Python 3, but havent been fully updated.\n\nIt emits warnings whenever `__unicode__` is encountered on an active model.\nIt does this even on Python 2 when the `__unicode__`\nis identical to the `__str__`, providing custom warnings to indicate what\ncode changes are needed to finish the Python 3 port.\n\nThe motivation for this app came while consulting for\nhttps://github.com/viper-development/ , which builds apps quickly,\ndeveloping custom functionality instead of writing boilerplate code.\n\nBy default, it will detect and attach to existing models that are using\nthe default `__repr__` and `__str__`.\n\nIt can be instructed to overwrite the `__repr__` and `__str__` of specific\nmodels, useful if the model has dedicated methods, but the output is not\ndesirable.\n\nIt will look for primary and unique keys, in an attempt to show the minimum\nnecessary for the user to recognise the record.\n\n*Note*: On Python 2, it will add a `__str__` which may emit non-ascii for\nmodel instances containing Unicode values in the fields it decides to\ndisplay.  De-select any such models using the settings.  I am willing to\nreview a PR to add proper Python 2 support, but I will reject any partial\nPython 2 support.  I can be convinced to build it to my own standards,\nbut it isnt sensible to do it in 2020 without a good reason.\n\n## Install\n\n1. `pip install django-dunder`\n2. Add `django_dunder` to `INSTALLED_APPS` *before* models that need\n   dunders added.\n\nTo catch all models missing `__str__` or `__repr__`, put it at the top\nof `INSTALLED_APPS`.\n\nTo quickly see how it improves the Django admin, install\n[`django-data-browser`](https://github.com/tolomea/django-data-browser)\nin development before and after, and click its \"admin\" column on\nany model that was using the Django default string representation.\n\n## Configure\n\nTo disable unicode warnings, set `DUNDER_WARN_UNICODE = False`.\n\nFor more control, in your `settings`, toggle these \"auto\", \"force\" and\n\"exclude\" settings.\n\nTo disable the automatic attaching for one or both of `__str__` and `__repr__`,\nset one of\n\n- `DUNDER_AUTO = False`\n- `DUNDER_AUTO_REPR = False`\n- `DUNDER_AUTO_STR = False`\n- `DUNDER_COPY_UNICODE = False`\n\nTo force all models to use these methods, use `DUNDER_FORCE = True`, or\nset `DUNDER_FORCE_REPR` or `DUNDER_FORCE_STR` to `True`.\n\nTo force specific models only, `DUNDER_FORCE_REPR` and `DUNDER_FORCE_STR`\nmay be defined as a list of model labels, e.g. `auth.User`.\n\nWhen using either auto or force modes, specific models can be excluded\nby providing a list of model labels to the exclude settings:\n\n```py\nDUNDER_REPR_EXCLUDE = ['auth.User']\nDUNDER_STR_EXCLUDE = ['myapp.Person']\n```\n\n*Note* When the copying of `__unicode__` is disabled on Python 3, and the\nDjango setting `DEBUG` is also disabled, this app will raise\n`ImproperlyConfigured` if it finds a `__unicode__`, as it assumes the app\nintended for the `__unicode__` to be used, and running in production with\nthe default Django `__str__` would result in incorrect behaviour.\nTo disable this, re-enable copying of the unicode, or set\n\n- `DUNDER_REJECT_UNICODE = False`\n\nThere is also a Django [check](https://docs.djangoproject.com/en/3.0/topics/checks/)\nfor inactive custom `__unicode__`, which runs seperately from the model\nregistration process, so it is safer to use.  It defaults to emit errors,\nhowever it can be set to emit errors, or disabled by setting it to `False`.\n\n- `DUNDER_CHECK_INACTIVE_UNICODE = 'warn'`\n\n### Formatting\n\nThe default formatting of `__str__` and `__repr__` given below can be modified\nglobally in the settings.\n\n- `DUNDER_REPR_ATTR_FMT = '{name}={value!r}'`\n- `DUNDER_REPR_FMT = '{}({})'`\n\n- `DUNDER_STR_ATTR_FMT = '{name}={value}'`\n- `DUNDER_STR_FMT = '\u003c{}: {}\u003e'`\n\nIn addition to standard Python string Formatter syntax, some experimental magic\nbehind the scenes allows the chaining together of attribute modifiers.\nThis is only active for the two attribute formatters.  Methods of types are\ntransparently invoked, and as are builtins.\n\ne.g. `DUNDER_STR_ATTR_FMT = '{name}={value.round__title}'` will apply\nround up numbers and apply title case to strings.\n\nIn addition, there is one extra modifier `ellipsis` that can be used to truncate\nlong text fields, appending an `...` ellipsis.  It defaults to 100 characters.\n\ne.g. `DUNDER_STR_ATTR_FMT = '{name}={value.round__ellipsis_20}'` will apply\nround up numbers and shorten strings to at most 20 characters.\n\nOn CPython, it is possible to add methods to core types using the `forbiddenfruit`\nlibrary.  For example, if [`datatype-tools`](https://github.com/edmundpf/datatype_tools)\nis installed as directed, with imports in `settings.py` or some other early\nloading Django code, use `()` syntax to use method names containing a `_`.\n\n- `DUNDER_STR_ATTR_FMT = '{name}={value.round__ellipsis_20__format_date()}'`\n\nWhen installing custom methods for core types from libraries, be aware they\noften reuse existing core methods or builtin names.\nIn the case of `datatype-tools`, it provides a `float.round()` which uses\ntwo significant places by default while `round(float)` has zero as default.\n\nWhen building custom methods for core types, avoid using method names\nwhich conflict with Python names or conflict with Django names.  Otherwise\nproblems like https://github.com/havocesp/typext/issues/1 arise.\n\nWant more?  If you can find the experimental magic, extend it and activate with:\n\n- `DUNDER_WRAPPER_CLASS = 'your_magic.Wrapper'`\n\nAnd please submit PRs to add your magic here for others to use.\n\n## Explicit fields\n\nTo show specific fields in either `str()` or `repr()`, two extra\n[model meta options](https://docs.djangoproject.com/en/dev/ref/models/options)\nare automatically added by django-dunder:\n\n```py\nfrom django.db import models\n\nclass MyModel(models.Model):\n    uuid = models.TextField()\n    first_name = models.TextField()\n    last_name = models.TextField()\n    ...\n\n    Meta:\n        str_fields = ('first_name', 'last_name')\n        repr_fields = ('uuid', )\n```\n\n## Explicit mixins\n\nAlternatively disable auto mode (`DUNDER_AUTO = False`), and use the\nmixins, and set the :\n\n```py\nfrom django_dunder.mixins import DunderModel\n\nclass MyModel(DunderModel):\n    first_name = models.TextField()\n    last_name = models.TextField()\n    ...\n\n    Meta:\n        repr_fields = ('first_name', 'last_name')\n```\n\nAdding Meta options can cause exceptions if django-dunders is removed\nfrom `INSTALLED_APPS`.\n\nTo avoid that, use [djsommo](https://github.com/jayvdb/djsommo)\n\n## Extending to other types\n\nIt should be possible to apply the functionality here to types other than\nDjango models and instances.  Some other way of identifying the appropriate\nclasses to patch is need, perhaps with additional configuration.\n\nThe ultimate solution for CPython-only would be if\n[its type dunders could be 'curse'd](https://github.com/clarete/forbiddenfruit/issues/11),\nespecially if `object.__str__` and `object.__repr__` could be replaced.\n\n## Alternatives\n\ndjango-dunder is especially useful when a project uses third-party apps\nthat do not provide these dunder methods that are suitable for the project.\nIn fact, several `django.contrib` models do not provide these dunder methods.\n\nInspiration was drawn from\n- [django-model-repr](https://github.com/relip/django-model-repr)\n- [django-auto-repr](https://github.com/dan-passaro/django-auto-repr)\n\nThey may be sufficient for some projects.\n\nIf that is not relevant, and if the project is using sentry, and the project\nonly wants a sane `__repr__`, incorporate the decorator in\n[`sentry.db.models`](https://github.com/getsentry/sentry/blob/master/src/sentry/db/models/base.py)\ninto a base mixin model used throughout the project.\n\nStarting a new project, and only interested in your own models?\n[pydantic](https://github.com/samuelcolvin/pydantic) provides default and customisable\ndunders, and\n[django-ninja](https://github.com/vitalik/django-ninja) provides a Django REST\ninterface on top, and there are lots of other tools layered on top of pydantic, like\n[pydantic-ui](https://github.com/prismaticd/pydantic-ui) providing a Django Admin-like\ninterface.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjayvdb%2Fdjango-dunder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjayvdb%2Fdjango-dunder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjayvdb%2Fdjango-dunder/lists"}