django-autogfk


Namedjango-autogfk JSON
Version 0.1.0 PyPI version JSON
download
home_pageNone
SummaryAutoGenericForeignKey: Admin-friendly GenericForeignKey with Select2 autocomplete for Django.
upload_time2025-09-04 00:17:04
maintainerNone
docs_urlNone
authorSandro Salles
requires_python>=3.10
licenseMIT
keywords django admin genericforeignkey contenttypes select2 autocomplete
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # django-autogfk

**Admin-friendly GenericForeignKey for Django — Select2 autocomplete, smart field creation, and an example project.**

[![CI](https://github.com/sandro-salles/django-autogfk/actions/workflows/ci.yml/badge.svg)](https://github.com/sandro-salles/django-autogfk/actions)
[![PyPI version](https://img.shields.io/pypi/v/django-autogfk.svg)](https://pypi.org/project/django-autogfk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](#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[![CI](https://github.com/sandro-salles/django-autogfk/actions/workflows/ci.yml/badge.svg)](https://github.com/sandro-salles/django-autogfk/actions)\n[![PyPI version](https://img.shields.io/pypi/v/django-autogfk.svg)](https://pypi.org/project/django-autogfk/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](#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"
}
        
Elapsed time: 0.73905s