Metadata-Version: 2.4
Name: gmcm-django-superadmin
Version: 2.2.1
Summary: A Django app for build admin sites like django admin.
Home-page: https://gitlab.com/mmorona/gmcm-django-superadmin
Author: Denis Siavichay
Author-email: dbsiavichay@gmail.com
License: MIT
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 3.2
Classifier: Framework :: Django :: 4.0
Classifier: Framework :: Django :: 4.1
Classifier: Framework :: Django :: 4.2
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENCE
Requires-Dist: Django>=3.2
Requires-Dist: pyyaml
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# gmcm-django-superadmin

Framework para construir sitios de administración en Django de forma declarativa,
al estilo de `django.contrib.admin` pero pensado para portales internos/públicos
propios (no el `/admin`). Registras un modelo, declaras qué campos mostrar y la
librería genera automáticamente las vistas CRUD, el listado con filtros y
búsqueda, el detalle, los formularios, el menú dinámico y los permisos.

---

## Instalación

```bash
pip install gmcm-django-superadmin
```

```python
# settings.py
INSTALLED_APPS = [
    # ...
    "superadmin",
]

TEMPLATES = [
    {
        # ...
        "OPTIONS": {
            "context_processors": [
                # ...
                "superadmin.context_processors.menu",  # menú lateral por usuario
            ],
        },
    },
]
```

```python
# urls.py
import superadmin

urlpatterns = [
    # ...
    path("", superadmin.site.urls),
]
```

```bash
python manage.py migrate
```

---

## Quick start

```python
# myapp/sites.py
from superadmin import ModelSite, register


@register("myapp.Product")
class ProductSite(ModelSite):
    list_fields = ("code", "name", "category__name:Categoría", "price", "is_active")
    detail_fields = (
        ("code", "name"),
        ("category", "price"),
        "description",
    )
    search_params = ("name__icontains", "code__icontains")
    filter_fields = ("category", "is_active", "price")
```

Los módulos `sites.py` de cada app se autodescubren. Eso genera, para `Product`:

| URL | Vista |
|-----|-------|
| `/myapp/product/` | Listado (filtros + búsqueda + paginación + export) |
| `/myapp/product/create/` | Alta |
| `/myapp/product/<pk>/update/` | Edición |
| `/myapp/product/<pk>/detail/` | Detalle |
| `/myapp/product/<pk>/delete/` | Baja |
| `/myapp/product/export/` | Exportar listado (CSV/XLSX) |

---

## Referencia de `ModelSite`

### Campos y presentación

| Atributo | Descripción |
|----------|-------------|
| `list_fields` | Columnas del listado. Soporta traversal (`a__b__c`), métodos del modelo y alias `"campo:Etiqueta"`. |
| `detail_fields` | Campos del detalle. Tupla (filas), tuplas anidadas (columnas Bootstrap automáticas) o `dict` (secciones con título). |
| `list_formatters` | `{campo: callable(valor, instancia)}` para pintar badges/moneda/colores sin tocar el modelo. |
| `detail_formatters` | Igual que `list_formatters`, para el detalle. |
| `fields` / `form_class` | Campos o formulario para crear/editar. Si no se define, usa `__all__`. |

### Listado, filtros y búsqueda

| Atributo | Descripción |
|----------|-------------|
| `search_params` | Campos para la búsqueda de texto (ej. `("name__icontains",)`). |
| `filter_fields` | Campos filtrables en el panel lateral. Soporta alias `"campo:Etiqueta"`. |
| `paginate_by` | Tamaño de página. |
| `order_by` | Campos de ordenamiento. |

### Rendimiento

| Atributo | Descripción |
|----------|-------------|
| `queryset` | Queryset base. |
| `list_queryset` / `detail_queryset` / ... | Queryset por acción (override del base solo para esa vista). |
| `auto_optimize_queryset` | `True` por defecto: deriva `select_related`/`prefetch_related` automáticamente desde `list_fields`/`detail_fields` (evita N+1). |

### Comportamiento

| Atributo | Descripción |
|----------|-------------|
| `allow_views` | Vistas a generar: `("list","create","update","detail","delete")`. |
| `readonly_check` | `callable(instancia) -> bool`. Si devuelve `True`, edición y borrado se bloquean y redirigen al detalle con un mensaje. |
| `allow_export` | Habilita el endpoint de export (CSV/XLSX). |
| `export_fields` | Campos a exportar (por defecto, `list_fields`). |
| `inlines` | Formsets inline para create/update. |
| `prepopulate_slug` | Campos desde los que autogenerar el `slug`. |
| `*_mixins` | `list_mixins`, `form_mixins`, `create_mixins`, `update_mixins`, `detail_mixins`, `delete_mixins`. |
| `*_template_name` | Plantilla por acción. |

---

## Filtros lazy (Select2 / FK grandes)

Para listados con ForeignKeys a tablas grandes, además del `FilterView` clásico
hay endpoints optimizados que cargan opciones **por AJAX paginado** (Select2 los
cachea al hacer scroll) con un mínimo de caracteres antes de buscar:

| Endpoint | Uso |
|----------|-----|
| `GET filtering/metadata/<app>/<model>/` | Metadata de todos los filtros del sitio (cacheada). Los FK/O2O/M2M vienen con `lazy: true` y `minimum_input_length`, sin opciones inline. |
| `GET filtering/search/<app>/<model>/<field>/?q=&page=` | Búsqueda paginada (formato Select2 `{results, pagination:{more}}`) para los campos lazy. |
| `POST filtering/count/<app>/<model>/` | Conteo en vivo de cuántas filas devolverían los filtros candidatos. |

El frontend conecta un `<select>` lazy a `search` vía la config `ajax` de Select2
con `minimumInputLength`. El `FilterView` clásico (`filter/<app>/<model>/<field>/`)
sigue disponible y ahora también acepta `?q=` para buscar.

---

## Exportar listados

Con `allow_export = True` (por defecto) el listado expone `export/`, que respeta
los **filtros y la búsqueda activos** de la sesión:

- `?format=csv` (sin dependencias)
- `?format=xlsx` (requiere `openpyxl`)

Requiere el permiso `view_<model>`.

---

## Acciones masivas

- `POST <model>/update/` — actualiza un campo en varios registros (valida el
  campo y castea el valor con `to_python`, dentro de una transacción).
- `POST <model>/delete/` — elimina varios registros.

---

## Menú dinámico

El menú lateral se modela en base de datos (`Menu` / `Action`), respeta permisos
por usuario y se renderiza con el context processor `superadmin.context_processors.menu`.
El árbol por usuario se **cachea** y se invalida automáticamente al cambiar un `Menu`.

---

## Settings disponibles

| Setting | Default | Descripción |
|---------|---------|-------------|
| `SUPERADMIN_SAVE_SUCCESS_MESSAGE` | "Se ha guardado correctamente." | Mensaje al guardar. |
| `SUPERADMIN_DELETE_SUCCESS_MESSAGE` | "Se ha eliminado correctamente." | Mensaje al eliminar. |
| `SUPERADMIN_READONLY_MESSAGE` | "Este registro no puede modificarse…" | Mensaje del guard `readonly_check`. |
| `SUPERADMIN_MENU_CACHE_TIMEOUT` | `300` | Segundos de caché del menú por usuario. |
| `SUPERADMIN_FILTER_PAGE_SIZE` | `20` | Tamaño de página del search lazy. |
| `SUPERADMIN_FILTER_MIN_INPUT_LENGTH` | `2` | Caracteres mínimos para buscar en filtros lazy. |
| `SUPERADMIN_FILTER_METADATA_CACHE_TIMEOUT` | `300` | Caché de la metadata de filtros. |
| `BOOLEAN_YES` / `BOOLEAN_NO` | "Yes" / "No" | Etiquetas booleanas. |

---

## System checks

`python manage.py check` valida que los `list_fields` / `detail_fields` de cada
sitio apunten a campos resolubles (atrapa typos en deploy, no en runtime con un
`AttributeError` en cara del usuario).

---

## Desarrollo y tests

```bash
python runtests.py
```

Suite con el test runner nativo de Django (sin dependencias extra). El CI
(`.gitlab-ci.yml`) corre los tests en Python 3.8/Django 3.2 y Python 3.11/Django 4.2,
y **publica automáticamente al hacer push a `master`** (usando las variables
`TWINE_USERNAME` / `TWINE_PASSWORD` de CI/CD).

---

## Compatibilidad

- Python 3.8 – 3.12
- Django 3.2 – 4.2
