# django-autogfk
**Admin-friendly GenericForeignKey for Django — Select2 autocomplete, smart field creation, and an example project.**
[](https://github.com/sandro-salles/django-autogfk/actions)
[](https://pypi.org/project/django-autogfk/)
[](#license)
`django-autogfk` makes working with `GenericForeignKey` in Django a first-class experience.
Instead of clunky dropdowns and raw IDs, it gives you a **Select2-based interface** with **autocomplete, filtering, and pagination**, just like `autocomplete_fields` for normal `ForeignKey`s.
## ✨ Features
- **Drop-in**: `AutoGenericForeignKey` automatically creates the underlying `*_content_type` and `*_object_id` fields.
- **Beautiful Select2 widget** in the Admin (content type ➜ object) with AJAX search and pagination.
- Supports `limit_choices_to`, `null/blank`, `related_name` just like regular fields.
- `AutoGenericAdminMixin` integrates seamlessly with Django Admin.
- **Example project** included (`example/`) so you can try it out instantly.
**Requirements:** Django **4.2+** (also tested on 5.0)
**License:** MIT
---
## 📦 Installation
From PyPI (recommended):
```bash
pip install django-autogfk
```
From source (editable):
```bash
git clone https://github.com/sandro-salles/django-autogfk.git
cd django-autogfk
pip install -e .[dev]
```
Add `autogfk` to `INSTALLED_APPS`:
```python
INSTALLED_APPS = [
# ...
"django.contrib.admin",
"django.contrib.contenttypes",
"autogfk",
]
```
Include URLs (for the autocomplete endpoint):
```python
# urls.py (project)
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("_autogfk/", include("autogfk.urls")), # ← required
]
```
> The package uses Select2 shipped with Django Admin — no extra frontend deps.
---
## 🚀 Quickstart
### 1) Define your model using `AutoGenericForeignKey`
```python
# models.py
from django.db import models
from autogfk.fields import AutoGenericForeignKey
# Example: allow only content types from specific apps
OWNER_LIMIT_CHOICES_TO = {"app_label__in": ["auth", "bots"]}
class IntelligenceCredentials(models.Model):
owner = AutoGenericForeignKey(
null=True,
blank=True,
limit_choices_to=OWNER_LIMIT_CHOICES_TO,
related_name="intelligence_credentials",
label="Owner",
)
name = models.CharField(max_length=120, blank=True, default="")
```
This automatically creates the concrete fields:
- `owner_content_type = ForeignKey(ContentType, …)`
- `owner_object_id = PositiveIntegerField(…)`
Run migrations:
```bash
python manage.py makemigrations
python manage.py migrate
```
### 2) Hook into the Admin
```python
# admin.py
from django.contrib import admin
from autogfk.admin import AutoGenericAdminMixin
from .models import IntelligenceCredentials
@admin.register(IntelligenceCredentials)
class IntelligenceCredentialsAdmin(AutoGenericAdminMixin, admin.ModelAdmin):
list_display = ("id", "owner", "name")
```
Open the Django Admin and edit an object:
You’ll see **two linked Select2 dropdowns**:
1) pick a **ContentType** (already filtered if you set `limit_choices_to`),
2) search/select the **object** of that model via **AJAX autocomplete**.
---
## 🧩 Example project (runnable)
A fully working Django project lives in `example/`:
```bash
cd example
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
```
Navigate to http://127.0.0.1:8000/admin/ and test the **Example** model.
It contains:
- `ModelA` and `ModelB` (sample models),
- `Example` with **two** `AutoGenericForeignKey` fields: `field_a` and `field_b`.
---
## ⚙️ Configuration & Tips
### `limit_choices_to`
You can restrict selectable ContentTypes:
```python
AutoGenericForeignKey(limit_choices_to={"app_label__in": ["exampleapp"]})
```
> For advanced filtering (e.g., per user or per model), subclass the autocomplete view or add permission checks in `views.py`.
### Labels and Related Names
- `label="Owner"` controls form label/placeholder in the Admin.
- `related_name="intelligence_credentials"` is propagated to the generated `content_type` field.
### Migrations
Because the field **creates the concrete fields in `contribute_to_class`**, the migration system will pick them up after you add the `AutoGenericForeignKey`. Always run `makemigrations` after changes.
---
## 🔒 Permissions & Security
The built-in autocomplete view is protected by `@staff_member_required`.
For multi-tenant or per-object permissions:
- Override the queryset in `views.autocomplete` to filter objects by `request.user`.
- Optionally mount a custom URL (e.g., `path("autocomplete/", my_view, name="autocomplete")`).
---
## 🧰 API Reference (short)
### `AutoGenericForeignKey(...)`
**Args (selected):**
- `ct_field: str | None` — name for the `ContentType` FK (auto: `<name>_content_type`)
- `oid_field: str | None` — name for the object ID field (auto: `<name>_object_id`)
- `null: bool` / `blank: bool`
- `limit_choices_to: dict | None`
- `related_name: str | None`
- `label: str | None` — Admin form label
### `AutoGenericAdminMixin`
- Auto-injects a **MultiWidget** with **Select2 + AJAX** for every `AutoGenericForeignKey` on the model.
- Handles initial values and saving back to `<name>_content_type` / `<name>_object_id`.
---
## 🧪 Tests
Run the test suite:
```bash
pytest
```
What’s covered (high level):
- Field auto-creation of `*_content_type` / `*_object_id`
- Autocomplete view (AJAX response, pagination basics)
- Admin form integration (initials and save path)
---
## 🐛 Troubleshooting
- **“The widget isn’t loading as Select2”**
Make sure you’re using Django Admin’s default assets. The package relies on Admin’s bundled Select2.
- **“No results appear in the object dropdown”**
Confirm the selected ContentType actually has instances. Try typing to trigger autocomplete.
- **“I need different search fields per model”**
Fork/override the `autocomplete` view and tailor the `search_fields` heuristic for your models.
- **“I want to restrict by user/team”**
Add checks in the `autocomplete` view using `request.user` and filter the queryset accordingly.
---
## 🤝 Contributing
PRs welcome!
Ideas:
- Registry to configure **per-model** search fields.
- Per-model permission hooks.
- Optional support for non-integer identifiers (e.g., UUID) in a future minor version.
Steps:
```bash
git clone https://github.com/sandro-salles/django-autogfk.git
cd django-autogfk
python -m venv .venv && source .venv/bin/activate
pip install -e .[dev]
pytest
```
---
## 📄 License
**MIT** — permissive and simple. See [LICENSE](./LICENSE).
---
## 💡 Credits
Built to make `GenericForeignKey` feel as smooth as a regular `ForeignKey` in the Django Admin. Enjoy!
Raw data
{
"_id": null,
"home_page": null,
"name": "django-autogfk",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "django, admin, genericforeignkey, contenttypes, select2, autocomplete",
"author": "Sandro Salles",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/19/be/914d71ef1f8f6f6618a0dd7cb5e4698280030a3fd8a1d4c386c58f223673/django_autogfk-0.1.0.tar.gz",
"platform": null,
"description": "# django-autogfk\n\n**Admin-friendly GenericForeignKey for Django \u2014 Select2 autocomplete, smart field creation, and an example project.**\n\n[](https://github.com/sandro-salles/django-autogfk/actions)\n[](https://pypi.org/project/django-autogfk/)\n[](#license)\n\n`django-autogfk` makes working with `GenericForeignKey` in Django a first-class experience. \nInstead of clunky dropdowns and raw IDs, it gives you a **Select2-based interface** with **autocomplete, filtering, and pagination**, just like `autocomplete_fields` for normal `ForeignKey`s.\n\n## \u2728 Features\n- **Drop-in**: `AutoGenericForeignKey` automatically creates the underlying `*_content_type` and `*_object_id` fields.\n- **Beautiful Select2 widget** in the Admin (content type \u279c object) with AJAX search and pagination.\n- Supports `limit_choices_to`, `null/blank`, `related_name` just like regular fields.\n- `AutoGenericAdminMixin` integrates seamlessly with Django Admin.\n- **Example project** included (`example/`) so you can try it out instantly.\n\n**Requirements:** Django **4.2+** (also tested on 5.0) \n**License:** MIT\n\n---\n\n## \ud83d\udce6 Installation\n\nFrom PyPI (recommended):\n```bash\npip install django-autogfk\n```\n\nFrom source (editable):\n```bash\ngit clone https://github.com/sandro-salles/django-autogfk.git\ncd django-autogfk\npip install -e .[dev]\n```\n\nAdd `autogfk` to `INSTALLED_APPS`:\n```python\nINSTALLED_APPS = [\n # ...\n \"django.contrib.admin\",\n \"django.contrib.contenttypes\",\n \"autogfk\",\n]\n```\n\nInclude URLs (for the autocomplete endpoint):\n```python\n# urls.py (project)\nfrom django.urls import include, path\n\nurlpatterns = [\n path(\"admin/\", admin.site.urls),\n path(\"_autogfk/\", include(\"autogfk.urls\")), # \u2190 required\n]\n```\n\n> The package uses Select2 shipped with Django Admin \u2014 no extra frontend deps.\n\n---\n\n## \ud83d\ude80 Quickstart\n\n### 1) Define your model using `AutoGenericForeignKey`\n```python\n# models.py\nfrom django.db import models\nfrom autogfk.fields import AutoGenericForeignKey\n\n# Example: allow only content types from specific apps\nOWNER_LIMIT_CHOICES_TO = {\"app_label__in\": [\"auth\", \"bots\"]}\n\nclass IntelligenceCredentials(models.Model):\n owner = AutoGenericForeignKey(\n null=True,\n blank=True,\n limit_choices_to=OWNER_LIMIT_CHOICES_TO,\n related_name=\"intelligence_credentials\",\n label=\"Owner\",\n )\n\n name = models.CharField(max_length=120, blank=True, default=\"\")\n```\n\nThis automatically creates the concrete fields:\n- `owner_content_type = ForeignKey(ContentType, \u2026)`\n- `owner_object_id = PositiveIntegerField(\u2026)`\n\nRun migrations:\n```bash\npython manage.py makemigrations\npython manage.py migrate\n```\n\n### 2) Hook into the Admin\n```python\n# admin.py\nfrom django.contrib import admin\nfrom autogfk.admin import AutoGenericAdminMixin\nfrom .models import IntelligenceCredentials\n\n@admin.register(IntelligenceCredentials)\nclass IntelligenceCredentialsAdmin(AutoGenericAdminMixin, admin.ModelAdmin):\n list_display = (\"id\", \"owner\", \"name\")\n```\n\nOpen the Django Admin and edit an object: \nYou\u2019ll see **two linked Select2 dropdowns**:\n1) pick a **ContentType** (already filtered if you set `limit_choices_to`), \n2) search/select the **object** of that model via **AJAX autocomplete**.\n\n---\n\n## \ud83e\udde9 Example project (runnable)\n\nA fully working Django project lives in `example/`:\n\n```bash\ncd example\npython -m venv .venv && source .venv/bin/activate\npip install -r requirements.txt\npython manage.py migrate\npython manage.py createsuperuser\npython manage.py runserver\n```\n\nNavigate to http://127.0.0.1:8000/admin/ and test the **Example** model. \nIt contains:\n- `ModelA` and `ModelB` (sample models),\n- `Example` with **two** `AutoGenericForeignKey` fields: `field_a` and `field_b`.\n\n---\n\n## \u2699\ufe0f Configuration & Tips\n\n### `limit_choices_to`\nYou can restrict selectable ContentTypes:\n```python\nAutoGenericForeignKey(limit_choices_to={\"app_label__in\": [\"exampleapp\"]})\n```\n> For advanced filtering (e.g., per user or per model), subclass the autocomplete view or add permission checks in `views.py`.\n\n### Labels and Related Names\n- `label=\"Owner\"` controls form label/placeholder in the Admin.\n- `related_name=\"intelligence_credentials\"` is propagated to the generated `content_type` field.\n\n### Migrations\nBecause the field **creates the concrete fields in `contribute_to_class`**, the migration system will pick them up after you add the `AutoGenericForeignKey`. Always run `makemigrations` after changes.\n\n---\n\n## \ud83d\udd12 Permissions & Security\n\nThe built-in autocomplete view is protected by `@staff_member_required`. \nFor multi-tenant or per-object permissions:\n- Override the queryset in `views.autocomplete` to filter objects by `request.user`.\n- Optionally mount a custom URL (e.g., `path(\"autocomplete/\", my_view, name=\"autocomplete\")`).\n\n---\n\n## \ud83e\uddf0 API Reference (short)\n\n### `AutoGenericForeignKey(...)`\n**Args (selected):**\n- `ct_field: str | None` \u2014 name for the `ContentType` FK (auto: `<name>_content_type`)\n- `oid_field: str | None` \u2014 name for the object ID field (auto: `<name>_object_id`)\n- `null: bool` / `blank: bool`\n- `limit_choices_to: dict | None`\n- `related_name: str | None`\n- `label: str | None` \u2014 Admin form label\n\n### `AutoGenericAdminMixin`\n- Auto-injects a **MultiWidget** with **Select2 + AJAX** for every `AutoGenericForeignKey` on the model.\n- Handles initial values and saving back to `<name>_content_type` / `<name>_object_id`.\n\n---\n\n## \ud83e\uddea Tests\n\nRun the test suite:\n```bash\npytest\n```\n\nWhat\u2019s covered (high level):\n- Field auto-creation of `*_content_type` / `*_object_id`\n- Autocomplete view (AJAX response, pagination basics)\n- Admin form integration (initials and save path)\n\n---\n\n## \ud83d\udc1b Troubleshooting\n\n- **\u201cThe widget isn\u2019t loading as Select2\u201d** \n Make sure you\u2019re using Django Admin\u2019s default assets. The package relies on Admin\u2019s bundled Select2.\n\n- **\u201cNo results appear in the object dropdown\u201d** \n Confirm the selected ContentType actually has instances. Try typing to trigger autocomplete.\n\n- **\u201cI need different search fields per model\u201d** \n Fork/override the `autocomplete` view and tailor the `search_fields` heuristic for your models.\n\n- **\u201cI want to restrict by user/team\u201d** \n Add checks in the `autocomplete` view using `request.user` and filter the queryset accordingly.\n\n---\n\n## \ud83e\udd1d Contributing\n\nPRs welcome! \nIdeas:\n- Registry to configure **per-model** search fields.\n- Per-model permission hooks.\n- Optional support for non-integer identifiers (e.g., UUID) in a future minor version.\n\nSteps:\n```bash\ngit clone https://github.com/sandro-salles/django-autogfk.git\ncd django-autogfk\npython -m venv .venv && source .venv/bin/activate\npip install -e .[dev]\npytest\n```\n\n---\n\n## \ud83d\udcc4 License\n\n**MIT** \u2014 permissive and simple. See [LICENSE](./LICENSE).\n\n---\n\n## \ud83d\udca1 Credits\n\nBuilt to make `GenericForeignKey` feel as smooth as a regular `ForeignKey` in the Django Admin. Enjoy!\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "AutoGenericForeignKey: Admin-friendly GenericForeignKey with Select2 autocomplete for Django.",
"version": "0.1.0",
"project_urls": {
"Homepage": "https://github.com/sandro-salles/django-autogfk",
"Issues": "https://github.com/sandro-salles/django-autogfk/issues",
"Repository": "https://github.com/sandro-salles/django-autogfk"
},
"split_keywords": [
"django",
" admin",
" genericforeignkey",
" contenttypes",
" select2",
" autocomplete"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "57aa2ce0bb7b51ba3a9ccfe3f059a621d1a8d7d927e853938ebcd2c38acd0004",
"md5": "6b2ec28458e35be62a33322a9adcdc08",
"sha256": "c7a19ea135d2452ebeec67297ee23042403749f8e6f5db0e1774b588ef99b887"
},
"downloads": -1,
"filename": "django_autogfk-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6b2ec28458e35be62a33322a9adcdc08",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 9690,
"upload_time": "2025-09-04T00:17:03",
"upload_time_iso_8601": "2025-09-04T00:17:03.455431Z",
"url": "https://files.pythonhosted.org/packages/57/aa/2ce0bb7b51ba3a9ccfe3f059a621d1a8d7d927e853938ebcd2c38acd0004/django_autogfk-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "19be914d71ef1f8f6f6618a0dd7cb5e4698280030a3fd8a1d4c386c58f223673",
"md5": "43c66dea223927a9315d732087b55110",
"sha256": "1ccd84f89ac6669b28b976e86a21d0dc359d0aa717cd21cd0f5a8934a61183a1"
},
"downloads": -1,
"filename": "django_autogfk-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "43c66dea223927a9315d732087b55110",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 11573,
"upload_time": "2025-09-04T00:17:04",
"upload_time_iso_8601": "2025-09-04T00:17:04.645648Z",
"url": "https://files.pythonhosted.org/packages/19/be/914d71ef1f8f6f6618a0dd7cb5e4698280030a3fd8a1d4c386c58f223673/django_autogfk-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-04 00:17:04",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "sandro-salles",
"github_project": "django-autogfk",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "django-autogfk"
}