Eneza CLI Framework — Developer Guide
Table of Contents
- Overview
- Installation
- Quick Start
- Command Reference
- Project Structure
- App Structure
- Menu Behavior
- URL Configuration
- Deployment
- Best Practices
- Troubleshooting
- Extending the Templates
- Validation
Overview
The Eneza CLI creates Eneza-based Django projects and apps with the current framework conventions. It also adds production Docker/Coolify scaffolding to new or existing projects.
Features:
eneza startprojectcreates a dashboard-first project with the current framework settings.eneza startappcreates a models/views scaffold with no sample model or sample view.eneza adddeploymentadds Docker/Coolify files to an existing project.- Projects reference framework apps through one import:
ENEZA_APPS. - Projects include an ASGI entry point, websocket routing, Docker Compose, init/worker/periodic scripts, and an environment sample.
- The CLI supports the current Eneza app conventions: models/ and views/ packages, dynamic URL registration, and dashboard-first menu entries.
Installation
Prerequisites
- Python 3.12 or newer
- Django 6.1 or newer
- PostgreSQL 16 for Docker/Coolify deployments
- Optional: Redis 7 for multiple web workers or cross-container websockets
Create a virtual environment
python -m venv eneza-env
# Windows
eneza-env\Scripts\activate
# macOS/Linux
source eneza-env/bin/activate
Install the framework and CLI
python -m pip install enezaframework
For development from a checkout:
python -m pip install -e .
The eneza console script is installed with the package. python -m eneza also works.
If the eneza command is not found, the editable install is stale. Regenerate the console script:
python -m pip install -e .
Without the console script, run the module directly:
python -m eneza startproject myproject
Do not use python eneza; eneza is a package and requires the -m flag or the installed console script.
Generated projects pin enezaframework in requirements.txt. If the package is served from a private index, set PIP_INDEX_URL and credentials in CI or Coolify.
Quick Start
Local development with SQLite
eneza startproject myproject
cd myproject
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS/Linux
python -m pip install -r requirements.txt
python manage.py migrate
python manage.py createsuperuser
python manage.py rbac_bootstrap
python manage.py seed_dashboards
python manage.py runserver
No .env file is required for local development. The settings default to SQLite, DEBUG=True, ALLOWED_HOSTS=["*"], and the development secret key.
If you create a .env for local development, set:
DB_ENGINE=sqlite
DATABASE_URL=
DJANGO_DEBUG=1
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
DJANGO_SECURE_SSL_REDIRECT=0
DJANGO_SESSION_COOKIE_SECURE=0
DJANGO_CSRF_COOKIE_SECURE=0
Docker and Coolify
eneza startproject myproject
cd myproject
cp .env.sample .env
# edit .env: set DJANGO_SECRET_KEY, POSTGRES_PASSWORD, hosts and superuser
docker compose up --build
The Compose file does not publish a host port because Coolify routes to container port 8000. For local browser access, add a ports mapping to the web service:
ports:
- "${WEB_PORT:-8000}:8000"
Add an app
eneza startapp myapp --project-dir .
Then add myapp to INSTALLED_APPS and include myapp.urls in the project URL configuration when you add views.
Command Reference
eneza startproject <project_name>
Creates a new Eneza project.
Arguments:
<project_name>- Name of the project to create.
Generated structure:
project_name/
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
├── .env.sample
├── requirements.txt
├── manage.py
├── docker/
│ ├── init.sh
│ ├── web.sh
│ ├── worker.sh
│ └── periodic.sh
├── project_name/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ ├── wsgi.py
│ ├── routing.py
│ ├── config.py
│ └── workflowconfig/
└── core/
├── __init__.py
├── admin.py
├── apps.py
├── models/
│ └── __init__.py
├── views/
│ └── __init__.py
├── urls.py
├── tests.py
└── migrations/
└── __init__.py
The project root URL resolves to the Enezacommon dashboard flow. core is included last at / and contains no sample root view.
eneza startapp <app_name>
Creates an app with the standard Eneza structure.
Arguments:
<app_name>- Name of the app to create.
Options:
--project-dir- Project directory (default: current directory).
Generated structure:
app_name/
├── __init__.py
├── admin.py
├── apps.py
├── models/
│ └── __init__.py
├── views/
│ └── __init__.py
├── urls.py
├── tests.py
└── migrations/
└── __init__.py
The app scaffold contains no sample model and no sample view. Both models/ and views/ directories exist with their __init__.py files.
eneza adddeployment [project_dir]
Adds Docker/Coolify deployment artifacts to an existing project.
Arguments:
[project_dir]- Existing project directory (default: current directory).
Options:
--project-name- Django project package name. When omitted, the CLI readsDJANGO_SETTINGS_MODULEfrommanage.py, then falls back to the directory name.--force- Overwrite existing deployment artifacts.
Copied artifacts:
Dockerfile
.dockerignore
docker-compose.yml
.env.sample
requirements.txt (only when absent, or with --force)
docker/init.sh
docker/web.sh
docker/worker.sh
docker/periodic.sh
The command:
- Validates that
manage.pyexists. - Replaces
PROJECT_NAME_PLACEHOLDERandPROJECT_DB_NAME_PLACEHOLDER. - Skips existing files by default.
- Never creates or overwrites
.env. - Never patches
settings.pyorurls.py.
After running it, review the printed checklist:
- Add
from enezaframework.conf import ENEZA_APPSand*ENEZA_APPStoINSTALLED_APPS. - Include
enezaframework.urlsin the project URL configuration. - Merge the deployment requirements into the existing requirements file.
- Create
.envfrom.env.sample. - Run the init commands, or start the Compose stack.
Project Structure
Framework app reference
Projects import the framework app list once:
from enezaframework.conf import ENEZA_APPS, ENEZA_CONTEXT_PROCESSORS
INSTALLED_APPS = [
# Django and third-party apps...
*ENEZA_APPS,
# Project apps...
"core",
]
TEMPLATES = [
{
"OPTIONS": {
"context_processors": [
# Django and third-party processors...
*ENEZA_CONTEXT_PROCESSORS,
],
},
},
]
ENEZA_APPS currently contains:
enezaframework, enezacommon, enezarbac, enezareporting, enezadashboard,
enezacommunication, enezaadminstrator, enezaworkflowengine,
enezanotification, enezaagents, humanresource
Adding or removing a framework app is a one-line change in enezaframework/conf.py. Projects do not list the framework apps individually.
URL configuration
The generated urls.py includes the framework once:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("accounts/", include("allauth.urls")),
path("", include("enezaframework.urls")),
path("", include("core.urls")),
]
enezaframework.urls includes the framework modules, the dashboard entry, media serving when enabled, and the existing path prefixes:
page-views/administration/administration/rbac/workflow/communication/reporting/dashboards/agents/humanresource/
Optional apps are included only when installed.
Settings
The generated settings are environment-driven:
DJANGO_SECRET_KEYis required whenDJANGO_DEBUG=0.DJANGO_ALLOWED_HOSTSfalls back toCOOLIFY_FQDN.DJANGO_CSRF_TRUSTED_ORIGINSfalls back toCOOLIFY_URL.DB_ENGINE=postgresusesDATABASE_URL; SQLite is the default for local development.DJANGO_STATIC_ROOTandDJANGO_MEDIA_ROOTcontrol the shared volumes.REDIS_URLenables the Redis channel layer and cache.TASKSdeclares thedefaultandworkflow_tasksqueues.SERVE_MEDIAenables the media route whenDEBUG=0.
Security settings are environment-driven: DJANGO_SECURE_SSL_REDIRECT, DJANGO_SESSION_COOKIE_SECURE, DJANGO_CSRF_COOKIE_SECURE, DJANGO_SECURE_HSTS_SECONDS, DJANGO_SECURE_PROXY_SSL_HEADER, DJANGO_X_FRAME_OPTIONS, and DJANGO_SECURE_CONTENT_TYPE_NOSNIFF.
App Structure
myapp/
├── __init__.py
├── admin.py
├── apps.py
├── models/
│ └── __init__.py
├── views/
│ └── __init__.py
├── urls.py
├── tests.py
└── migrations/
└── __init__.py
apps.py
A generated app starts with an app config that is menu-eligible but has no sample menu items:
from django.apps import AppConfig
class myappConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "myapp"
menu_verbose_name = "Myapp"
app_entry_menu = "myapp:myapp_dashboard"
app_icon = "app-placeholder-icon"
menu_position = 10
menus = []
app_entry_menu marks the app as menu-eligible. The module menu resolves the real entry URL to enezadashboard:module_entry for the app label. The dashboard app then shows its empty state until a dashboard is designed for the module.
The menus list stays empty until real views exist. Add fast tabs and menu items when you add views.
urls.py
The generated URL module contains only dynamic registration:
from enezaframework.utils import get_registered_urls
app_name = "myapp"
dynamic_urls = get_registered_urls(app_name)
urlpatterns = [
*dynamic_urls,
]
Views decorated with @auto_url_register are picked up automatically. No sample dashboard route is generated.
models/ and views/
Both packages are created empty with __init__.py. Add one file per model or view group and re-export the symbols from the package __init__.py:
# myapp/models/__init__.py
from .customertable import CustomerTable
# myapp/views/__init__.py
from .customerviews import *
Menu Behavior
The first menu destination for a new app is the dashboard app:
/dashboards/<app_label>/
If no dashboard is defined for the module, Enezadashboard renders its empty state instead of a sample dashboard view. This is the intended behavior for a fresh CLI scaffold.
The app appears in the module menu because it defines menu_verbose_name, app_icon, and app_entry_menu. The fast tabs remain empty until menus is populated.
If a project removes enezadashboard from ENEZA_APPS, the scaffold must define a real app_entry_menu URL or omit the menu attributes.
URL Configuration
Dynamic URL registration
Each app uses get_registered_urls():
from enezaframework.utils import get_registered_urls
app_name = "myapp"
dynamic_urls = get_registered_urls(app_name)
urlpatterns = [
*dynamic_urls,
]
Auto URL registration
Views can be registered automatically:
from enezaframework.utils import auto_url_register
from enezaframework.views import BaseListView, BaseDetailView
@auto_url_register
class MyAppListView(BaseListView):
model = MyAppModel
def set_list_fields(self):
return ["name", "description", "created_at"]
@auto_url_register
class MyAppDetailView(BaseDetailView):
model = MyAppModel
def detail_ui_view(self):
return [
{
"name": "Basic Information",
"icon": "Home",
"Fieldcolumns": [
{"fields": ["name", "description"]},
],
},
]
Generated URL names include:
myapp:myapp_listmyapp:myapp_createmyapp:myapp_detailmyapp:myapp_updatemyapp:myapp_delete
Add the app's URL module to the project URL configuration before using these names.
Supported view types
The registry supports list, detail, create, delete, action, parameter, dashboard, autocomplete, workflow and document report views.
Deployment
Compose services
| Service | Purpose |
|---|---|
db |
PostgreSQL 16 with a pg_isready healthcheck. |
init |
One-shot migrations, cache table, site update, superuser bootstrap, RBAC bootstrap, dashboard seeding and static collection. |
web |
Uvicorn serving <project>.asgi:application on port 8000. |
worker |
python manage.py db_worker --queue-name=default,workflow_tasks. |
periodic |
python manage.py run_periodic. |
redis |
Optional Redis 7 service under the redis Compose profile. |
web, worker and periodic wait for init to complete successfully.
Init sequence
python manage.py migrate --noinput
python manage.py createcachetable
python manage.py ensure_site
python manage.py ensure_superuser
python manage.py rbac_bootstrap
python manage.py seed_dashboards
python manage.py collectstatic --noinput
ensure_superusercreates the bootstrap admin fromDJANGO_SUPERUSER_*and never resets an existing password.ensure_siteupdates thedjango.contrib.sitesrow fromDJANGO_SITE_DOMAINorCOOLIFY_FQDN.rbac_bootstrapseeds the security roles and permissions.seed_dashboardsloads the declarative dashboards for installed framework apps.
Set SEED_HR_ROLES=1 to also run python manage.py seed_hr_roles.
Set SEED_HR_SAMPLE=1 to create a small sample HR organization and a linked demo user. The init script runs seed_hr_roles and then:
python manage.py seed_hr_sample --role HR_MANAGER
The sample command creates or reuses the Sample Company entity, a Human Resources department, job and position, an employee, and a user linked to that employee. It grants the entity to the user and assigns the requested roles. It is idempotent and disabled by default.
Useful options:
--no-user- create HR data only.--no-entity-access- do not grant the sample entity to the user.--reset-password- reset an existing sample user's password.--dry-runand--json.
A newly created sample user gets the login sample.hr@example.com and password Sample@123. Existing users keep their current password unless --reset-password is used. Change the demo password before sharing. Sample data is for demos; do not enable SEED_HR_SAMPLE in production.
Required environment
Copy .env.sample to .env and set at least:
DJANGO_SECRET_KEY=<long-random-value>
POSTGRES_PASSWORD=<url-safe-password>
DJANGO_ALLOWED_HOSTS=
DJANGO_SUPERUSER_USERNAME=admin
DJANGO_SUPERUSER_EMAIL=admin@example.com
DJANGO_SUPERUSER_PASSWORD=<strong-password>
Use a URL-safe POSTGRES_PASSWORD (no @, :, or /) because it is embedded in DATABASE_URL.
Leave DJANGO_ALLOWED_HOSTS and DJANGO_CSRF_TRUSTED_ORIGINS blank on Coolify to use COOLIFY_FQDN and COOLIFY_URL.
Coolify
- Create a Docker Compose resource from the repository.
- Set the environment variables from
.env.sample. - Configure the domain on the
webservice at container port8000. - Keep the
pgdata,media_dataandstatic_datavolumes persistent. - Set
SERVE_MEDIA=1unless media is served by object storage or a reverse proxy. - Keep
UVICORN_WORKERS=1unlessREDIS_URLis configured.
For local HTTP Docker testing, set:
DJANGO_SECURE_SSL_REDIRECT=0
DJANGO_SESSION_COOKIE_SECURE=0
DJANGO_CSRF_COOKIE_SECURE=0
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
Async work
Background jobs require the database task worker:
python manage.py db_worker --queue-name=default,workflow_tasks
Scheduled workflow work requires the periodic runner:
python manage.py run_periodic
The Compose stack starts both. Scheduling alone does not execute work.
Media and static files
initcollects static files into the sharedstatic_datavolume; WhiteNoise serves them in production.SERVE_MEDIA=1serves/media/through Django from the sharedmedia_datavolume.- For larger deployments, move media to S3-compatible storage or an nginx sidecar and set
SERVE_MEDIA=0.
Optional Redis
In-memory channels work only with a single web worker. To scale workers or run websockets across containers:
- Enable the
redisCompose profile, or add a Redis service. - Set
REDIS_URL=redis://redis:6379/0. - Raise
UVICORN_WORKERSabove1.
Best Practices
Naming conventions
- Use
snake_casefor app names. - Use
PascalCasefor model and view class names. - Keep
app_nameinurls.pyequal to the Django app label.
App organization
- Keep
models/andviews/as packages with__init__.py. - Re-export models and views from the package
__init__.pyas you add files. - Keep
admin.py,apps.py,tests.pyat the app root. - Add migrations with
python manage.py makemigrations <app>.
Menus
- Leave
menus=[]until real views exist. - Set
app_entry_menuto a real URL when the app no longer uses the dashboard entry. - Use
load_iconfor icons and confirm the SVG exists understatic/icons/.
URLs
- Use
@auto_url_registerfor framework CRUD views. - Include the app's URL module in the project URL configuration.
- Use the generated URL names in
menusonly after the routes exist.
Troubleshooting
eneza command is not recognized
Reinstall the editable package so pip regenerates the console script:
python -m pip install -e .
Then verify:
eneza --help
If the script still is not available, use python -m eneza .... Do not run python eneza ...; Python treats the package directory as a script.
App does not appear in the module menu
Verify that the app is in INSTALLED_APPS and its AppConfig defines menu_verbose_name, app_icon, and app_entry_menu.
App menu entry opens the dashboard empty state
This is expected for a fresh scaffold. Design a dashboard for the app, or point app_entry_menu at a real view after adding one.
URLs do not resolve
Confirm the app's urls.py spreads *dynamic_urls and that the app URL module is included in the project URL configuration. Verify views are decorated with @auto_url_register.
NoReverseMatch for app_entry_menu
The default scaffold expects enezadashboard to be installed. Keep it in ENEZA_APPS, or define a real app_entry_menu URL.
Sample HR user cannot see HR menus
Run python manage.py seed_hr_roles, then rerun python manage.py seed_hr_sample --role HR_MANAGER. The sample command warns and skips roles that do not exist or are inactive.
Docker build cannot find Dockerfile
Use the standard filename Dockerfile. On Windows, a case-only Git rename from DockerFile may need:
git mv DockerFile Dockerfile.tmp
git mv Dockerfile.tmp Dockerfile
Init fails because the secret key is a placeholder
Set a real DJANGO_SECRET_KEY when DJANGO_DEBUG=0.
Host validation fails on Coolify
Leave DJANGO_ALLOWED_HOSTS blank so settings use COOLIFY_FQDN, or set the real domain explicitly.
Websockets fail with multiple workers
Set REDIS_URL and enable Redis, or keep UVICORN_WORKERS=1.
Extending the Templates
- Edit the project templates under
eneza/templates/project/. - Edit the app templates under
eneza/templates/app/. - Update placeholder replacement and deployment assets in
eneza/cli.py. - Ensure new dotfiles are listed explicitly in
setup.pypackage_dataandMANIFEST.in. - Rebuild and reinstall the package, then verify the generated output.
The deployment assets are shared by startproject and adddeployment; keep both paths working when adding a new artifact.
Validation
Per project rules, the developer or agent does not run or claim test results. The user is solely responsible for all testing and validation of generated projects, Docker images, migrations, seeding and Coolify deployments.