Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6fa789804 | ||
|
|
8b48a9b9bb | ||
|
|
9793da4dbf | ||
|
|
9b6edfec0e | ||
|
|
dfcd598da3 | ||
|
|
d64ca9ddb2 | ||
|
|
3e6cb2f055 | ||
|
|
5ce5ebbb09 | ||
|
|
ca79f1968e | ||
|
|
ca077e380c | ||
|
|
44299ed8a9 | ||
|
|
916d97ffdf | ||
|
|
66927b988a | ||
|
|
df68383b44 | ||
|
|
d3066a7ceb | ||
|
|
72e6b0df0f | ||
|
|
7b8d6f3342 | ||
|
|
fe9eec70d5 | ||
|
|
ad09fa0b79 | ||
|
|
5984961878 | ||
|
|
5b755b789c | ||
|
|
382d2a0f56 | ||
|
|
c38d4b3832 | ||
|
|
3200c9f9b2 | ||
|
|
fafe29c2cb | ||
|
|
9e3f5ead8f | ||
|
|
260d41b10d | ||
|
|
2d7d712ecf | ||
|
|
3ffd85d2ca | ||
|
|
8c1195880d | ||
|
|
c1262d02e9 | ||
|
|
63989b7f67 | ||
|
|
b3b0c660e6 | ||
|
|
3de5d577a1 | ||
|
|
e5f93e829b | ||
|
|
ce5cda9330 | ||
|
|
5b76b76811 | ||
|
|
5f80a574b6 | ||
|
|
7bac6b176e | ||
|
|
e8176a1ff5 | ||
|
|
3cfeb99946 | ||
|
|
10ff50d841 | ||
|
|
b09a77592d | ||
|
|
bf97ba0dec | ||
|
|
501c1d1159 | ||
|
|
da6b60ee24 | ||
|
|
c14abdb03e | ||
|
|
7a9563957d | ||
|
|
197e35948f | ||
|
|
4025f6acf2 | ||
|
|
2a5480ff05 | ||
|
|
5084b351a5 | ||
|
|
a8af4674a0 | ||
|
|
86906070aa | ||
|
|
d96db6b313 | ||
|
|
0472a6f00b | ||
|
|
29306ef865 | ||
|
|
90da64612f | ||
|
|
4609ff6eb1 | ||
|
|
b6e1d39132 | ||
|
|
05c329b595 | ||
|
|
abb5bd2aa1 | ||
|
|
e0d686ade3 | ||
|
|
3d86539db6 | ||
|
|
4e2869a8f6 | ||
|
|
d44a906bab | ||
|
|
175592f82a | ||
|
|
221a5d7d03 | ||
|
|
fde8c0b5e2 | ||
|
|
fb48a7bd17 |
No files matched your search
@@ -16,10 +16,10 @@ jobs:
|
||||
python-version: ["3.13"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
|
||||
@@ -19,4 +19,4 @@ jobs:
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
use-github-auto-merge: true
|
||||
target: minor
|
||||
target: squash
|
||||
@@ -12,10 +12,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ jobs:
|
||||
permissions:
|
||||
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#--------------------------------------------------#
|
||||
# The following was generated by djinit: #
|
||||
#--------------------------------------------------#
|
||||
AGENTS.md
|
||||
|
||||
# Python
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
@@ -1,145 +1,128 @@
|
||||
# djinit
|
||||
|
||||
<div align="center">
|
||||
|
||||
> PyPI didn't allow the original name, so you'll find it as **djinitx** on PyPI
|
||||
|
||||
<div align="center">
|
||||
|
||||
<img src="https://img.shields.io/pypi/v/djinitx?color=blue&label=PyPI&logo=pypi&logoColor=white" alt="PyPI">
|
||||
<img src="https://img.shields.io/badge/Django-4.2%20%7C%205.1%20%7C%205.2-0C4B33?logo=django&logoColor=white" alt="Django">
|
||||
<img src="https://img.shields.io/badge/Django-5.1%20%7C%205.2%20%7C%206.0-0C4B33?logo=django&logoColor=white" alt="Django">
|
||||
<img src="https://img.shields.io/badge/Python-3.13%2B-3776AB?logo=python&logoColor=white" alt="Python">
|
||||
<a href="https://github.com/S4NKALP/djinit/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License"></a>
|
||||
|
||||
</div>
|
||||
|
||||
**djinit** helps you set up a production‑ready Django project in minutes. No more copy‑pasting settings or manually wiring up apps—just answer a few questions and get a modern Django project with REST API, authentication, documentation, and deployment configs ready to go.
|
||||
|
||||
## Why djinit?
|
||||
|
||||
Starting a Django project usually means spending hours setting up the same things: splitting settings for dev/prod, configuring DRF, adding JWT auth, setting up CORS, preparing for deployment. djinit does all of this for you with sensible defaults and lets you choose the project structure that fits your needs.
|
||||
|
||||
## Installation
|
||||
|
||||
**Recommended** (using pipx):
|
||||
A CLI that scaffolds production-ready Django projects with DRF, JWT auth, API docs, and deployment configs - all through an interactive wizard. No more copy-pasting settings or wiring up apps by hand.
|
||||
|
||||
```bash
|
||||
pipx install djinitx
|
||||
djinit setup
|
||||
```
|
||||
|
||||
Or with pip:
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Recommended - isolated environment
|
||||
pipx install djinitx
|
||||
|
||||
# Or with uv
|
||||
uv tool install djinitx
|
||||
|
||||
# Or with pip
|
||||
pip install djinitx
|
||||
```
|
||||
|
||||
Or with uv:
|
||||
Requires Python 3.13+.
|
||||
|
||||
```bash
|
||||
uv tool install djinitx
|
||||
```
|
||||
## Usage
|
||||
|
||||
**Requirements**: Python 3.13+
|
||||
|
||||
## Getting Started
|
||||
### Create a project
|
||||
|
||||
```bash
|
||||
djinit setup
|
||||
```
|
||||
|
||||
or the shorter alias:
|
||||
|
||||
```bash
|
||||
# or
|
||||
dj setup
|
||||
```
|
||||
|
||||
The wizard will ask you a few questions:
|
||||
The wizard walks you through these choices:
|
||||
|
||||
1. **What structure do you want?**
|
||||
- **Standard** – Classic Django layout with split settings
|
||||
- **Predefined** – Organized with `apps/` and `api/` folders (great for larger projects)
|
||||
- **Unified** – Everything under `core/` and `apps/` (clean and minimal)
|
||||
- **Single Folder** – All apps in one configurable folder (simple and flat)
|
||||
1. **Project structure** - Standard, Predefined (`apps/` + `api/`), Unified (`core/` + `apps/`), or Single Folder
|
||||
2. **Project name and directory**
|
||||
3. **Database** - PostgreSQL or MySQL; `DATABASE_URL` or individual env vars
|
||||
4. **Frontend tools** - Tailwind CSS, HTMX, and/or Vite (optional)
|
||||
5. **Django apps** - Name them; optionally nest under an `apps/` package
|
||||
6. **CI/CD** - GitHub Actions, GitLab CI, both, or none
|
||||
|
||||
2. **Project Setup** – Destination directory (use `.` for current) and project name.
|
||||
When you're done, you have a fully configured Django project ready to run.
|
||||
|
||||
3. **Database Configuration** – Choose `DATABASE_URL` (recommended) or individual variables; pick PostgreSQL or MySQL.
|
||||
|
||||
4. **Django Apps** *(Standard structure only)* – Whether to create an `apps/` folder and which apps to scaffold.
|
||||
|
||||
5. **CI/CD Pipeline** – GitHub Actions, GitLab CI, both, or skip it.
|
||||
|
||||
That’s it—your project will be ready with everything configured.
|
||||
|
||||
## What You Get
|
||||
|
||||
- **Split settings** for development and production
|
||||
- **Django REST Framework** with JWT authentication
|
||||
- **API documentation** (Swagger UI at `/docs/`)
|
||||
- **CORS** configured for local development
|
||||
- **WhiteNoise** for static files
|
||||
- **PostgreSQL** support (SQLite for dev)
|
||||
- **Modern admin** interface (`django-jazzmin`)
|
||||
- **Deployment ready** with `Procfile` and `runtime.txt`
|
||||
- **Development tools** (`Justfile` with common commands)
|
||||
- **Environment template** (`.env.sample`)
|
||||
- **Git ready** (`.gitignore` included)
|
||||
|
||||
## Commands
|
||||
|
||||
### Create a Project
|
||||
|
||||
```bash
|
||||
djinit setup
|
||||
```
|
||||
|
||||
### Add Apps to an Existing Project
|
||||
### Add apps later
|
||||
|
||||
```bash
|
||||
djinit app users products orders
|
||||
```
|
||||
|
||||
Creates the apps, adds them to `INSTALLED_APPS`, and wires up URLs.
|
||||
Creates the apps, registers them in `INSTALLED_APPS`, and wires up URL includes.
|
||||
|
||||
### Generate Secret Keys
|
||||
### Generate secret keys
|
||||
|
||||
```bash
|
||||
djinit secret
|
||||
djinit secret --count 5 --length 64
|
||||
```
|
||||
|
||||
Use `--count 5` to generate five keys or `--length 64` to change the length.
|
||||
## What You Get
|
||||
|
||||
Every project ships with:
|
||||
|
||||
- **Split settings** - `base.py`, `development.py`, `production.py` with sensible defaults
|
||||
- **Django REST Framework** - With JWT authentication (access + refresh tokens)
|
||||
- **API documentation** - Swagger UI at `/docs/`, ReDoc at `/schema/`
|
||||
- **CORS** - Configured for local dev, locked down for production
|
||||
- **WhiteNoise** - Static file serving, production-ready
|
||||
- **django-jazzmin** - Modern admin interface
|
||||
- **Environment management** - `.env.sample` with `django-environ`
|
||||
- **Deployment configs** - `Dockerfile`, `Procfile`, `runtime.txt`
|
||||
- **CI/CD pipelines** - GitHub Actions and/or GitLab CI
|
||||
- **Task runner** - `Justfile` with common commands
|
||||
- **`.gitignore`** - Python + Node defaults
|
||||
|
||||
Optional add-ons when selected:
|
||||
|
||||
| Feature | Package | What's configured |
|
||||
|---|---|---|
|
||||
| Tailwind CSS | django-tailwind-cli | Settings, DaisyUI theme, Node install in Docker |
|
||||
| HTMX | django-htmx | Installed app + HtmxMiddleware |
|
||||
| Vite | django-vite | `vite.config.ts`, dev/prod mode toggles, CI build step |
|
||||
|
||||
### Development workflow
|
||||
|
||||
```bash
|
||||
just dev # Start dev server
|
||||
just migrate # Run migrations
|
||||
just test # Run tests
|
||||
just lint # Lint with ruff
|
||||
just format # Format with ruff
|
||||
just vite # Start Vite dev server (if selected)
|
||||
just dev-full # Django + Vite together (if selected)
|
||||
```
|
||||
|
||||
No `just` installed? These map directly to Django management commands.
|
||||
|
||||
## Project Structures
|
||||
|
||||
### Standard Structure
|
||||
### Standard
|
||||
|
||||
```
|
||||
myproject/
|
||||
├── manage.py
|
||||
├── myproject/ # Config module
|
||||
│ ├── settings/ # Split settings
|
||||
│ ├── settings/ # base.py, development.py, production.py
|
||||
│ ├── urls.py
|
||||
│ └── wsgi.py
|
||||
└── apps/ # Your apps (optional)
|
||||
└── users/
|
||||
```
|
||||
|
||||
### Single Folder Layout
|
||||
|
||||
```
|
||||
myproject/
|
||||
├── manage.py
|
||||
├── project/ # Configurable folder name
|
||||
│ ├── settings/
|
||||
│ ├── urls.py
|
||||
│ ├── models/ # All models here
|
||||
│ ├── api/ # API views & serializers
|
||||
│ │ └── your_model_name/
|
||||
│ │ ├── views.py
|
||||
│ │ ├── serializers.py
|
||||
│ │ └── urls.py
|
||||
│ └── wsgi.py
|
||||
```
|
||||
|
||||
### Predefined Structure
|
||||
### Predefined
|
||||
|
||||
```
|
||||
myproject/
|
||||
@@ -154,7 +137,7 @@ myproject/
|
||||
└── v1/
|
||||
```
|
||||
|
||||
### Unified Structure
|
||||
### Unified
|
||||
|
||||
```
|
||||
myproject/
|
||||
@@ -168,107 +151,69 @@ myproject/
|
||||
├── serializers/
|
||||
├── views/
|
||||
├── urls/
|
||||
└── api/ # API routes
|
||||
└── api/
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
### Single Folder
|
||||
|
||||
Your project ships with a `Justfile` for common tasks:
|
||||
|
||||
```bash
|
||||
just dev # Start dev server
|
||||
just migrate # Run migrations
|
||||
just makemigrations # Create migrations
|
||||
just shell # Django shell
|
||||
just test # Run tests
|
||||
just format # Format code
|
||||
just lint # Lint code
|
||||
```
|
||||
|
||||
If you don’t have `just` installed, these are just shortcuts for the equivalent Django commands.
|
||||
|
||||
## What's Included
|
||||
|
||||
### Packages
|
||||
|
||||
- **Django** – Web framework
|
||||
- **Django REST Framework** – API toolkit
|
||||
- **djangorestframework‑simplejwt** – JWT authentication
|
||||
- **drf‑spectacular** – OpenAPI/Swagger docs
|
||||
- **django‑cors‑headers** – CORS handling
|
||||
- **django‑jazzmin** – Modern admin UI
|
||||
- **whitenoise** – Static file serving
|
||||
- **psycopg2‑binary** – PostgreSQL driver
|
||||
- **gunicorn** – Production WSGI server
|
||||
- **python‑dotenv** – `.env` handling
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Endpoint | Description |
|
||||
|-------------------|---------------------------------|
|
||||
| `/admin/` | Django admin |
|
||||
| `/token/` | Obtain JWT token |
|
||||
| `/token/refresh/` | Refresh JWT token |
|
||||
| `/docs/` | Swagger UI (dev only) |
|
||||
| `/schema/` | OpenAPI schema (dev only) |
|
||||
|
||||
### Settings Overview
|
||||
|
||||
- **Development** – SQLite, `DEBUG=True`, console email backend, permissive CORS.
|
||||
- **Production** – PostgreSQL, hardened security settings, SMTP email backend, strict CORS.
|
||||
myproject/
|
||||
├── manage.py
|
||||
├── project/ # Configurable name
|
||||
│ ├── settings/
|
||||
│ ├── urls.py
|
||||
│ ├── models/
|
||||
│ ├── api/
|
||||
│ └── wsgi.py
|
||||
```
|
||||
|
||||
## Environment Setup
|
||||
|
||||
Copy the sample file and fill in your values:
|
||||
|
||||
```bash
|
||||
cp .env.sample .env
|
||||
```
|
||||
|
||||
```dotenv
|
||||
SECRET_KEY=your-secret-key-here # Generate with: djinit secret
|
||||
DATABASE_URL=postgres://user:pass@host:5432/db
|
||||
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
|
||||
```
|
||||
SQLite works out of the box for development. Swap in your production database when you're ready.
|
||||
|
||||
SQLite works out of the box for development—no extra DB setup required.
|
||||
## Packages
|
||||
|
||||
## Roadmap
|
||||
| Package | Role |
|
||||
|---|---|
|
||||
| Django | Web framework |
|
||||
| django-environ | Environment variable management |
|
||||
| django-jazzmin | Admin UI |
|
||||
| djangorestframework | REST API toolkit |
|
||||
| djangorestframework-simplejwt | JWT authentication |
|
||||
| drf-yasg | Swagger/OpenAPI docs |
|
||||
| django-cors-headers | CORS handling |
|
||||
| whitenoise | Static file serving |
|
||||
| gunicorn | Production WSGI server |
|
||||
| psycopg[binary] | PostgreSQL driver |
|
||||
| django-tailwind-cli | Tailwind CSS _(optional)_ |
|
||||
| django-htmx | HTMX _(optional)_ |
|
||||
| django-vite | Vite bundler _(optional)_ |
|
||||
|
||||
The following items are tracked in **TODO.md** and represent the near‑future direction of djinit.
|
||||
## API Endpoints
|
||||
|
||||
### Planned Features
|
||||
- **Docker Support** – Auto‑generate a `Dockerfile` for containerized deployments.
|
||||
- **Frontend Integration** – Scaffold React, Vue, or HTMX alongside the Django backend.
|
||||
- **Celery Integration** – Simplify background task setup with Celery.
|
||||
- **More Packages** – Add optional integrations for popular Django packages.
|
||||
|
||||
### Enhancements
|
||||
- **Add more project structure templates** – Expand the set of ready‑made layouts.
|
||||
- **Add testing framework setup** – Provide pytest and coverage configuration out of the box.
|
||||
- **Fix bugs** – Ongoing maintenance and bug resolution.
|
||||
|
||||
### Completed
|
||||
- **Interactive configuration wizard** – Streamlined project creation experience.
|
||||
- **Improved structure detection** – Smarter detection of existing Django layouts.
|
||||
|
||||
> Contributions that address any of the above items are highly welcome!
|
||||
| Endpoint | Description |
|
||||
|---|---|
|
||||
| `/admin/` | Django admin |
|
||||
| `/token/` | Obtain JWT pair |
|
||||
| `/token/refresh/` | Refresh access token |
|
||||
| `/docs/` | Swagger UI |
|
||||
| `/schema/` | ReDoc |
|
||||
|
||||
## Contributing
|
||||
|
||||
Found a bug or have an idea? Open an issue or submit a pull request. Contributions are always welcome!
|
||||
Contributions are welcome. Open an issue or submit a PR.
|
||||
|
||||
1. Fork the repository.
|
||||
2. Create a feature branch (`git checkout -b feature/awesome-feature`).
|
||||
3. Make your changes and ensure tests pass (`just test`).
|
||||
4. Submit a pull request with a clear description of the change.
|
||||
1. Fork the repo
|
||||
2. Create a feature branch (`git checkout -b feature/whatever`)
|
||||
3. Make your changes and run `just test`
|
||||
4. Submit a pull request
|
||||
|
||||
Please follow the existing code style (ruff + black) and include tests for new functionality.
|
||||
Match the existing code style - ruff linting and formatting are enforced.
|
||||
|
||||
## License
|
||||
|
||||
MIT © Sankalp Tharu
|
||||
|
||||
---
|
||||
|
||||
*© 2024 Sankalp Tharu. All rights reserved.*
|
||||
MIT © [Sankalp Tharu](https://sankalptharu.com.np)
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
## Features
|
||||
|
||||
- [ ] Docker Support: Auto generating `Dockerfile` so you can containerize easily
|
||||
- [ ] Frontend Integration: Options to scaffold React, Vue, or HTMX right alongside Django
|
||||
- [x] Docker Support: Auto generating `Dockerfile` so you can containerize easily
|
||||
- [x] Frontend Integration: Options to scaffold React, Vue, or HTMX right alongside Django
|
||||
- [ ] Celery: Making background tasks easier to set up
|
||||
- [ ] More Packages: Integrating other popular packages to help you build feature rich apps effortlessly
|
||||
|
||||
@@ -11,6 +11,5 @@
|
||||
|
||||
- [ ] Add more project structure templates
|
||||
- [x] Add interactive configuration wizard
|
||||
- [ ] Add testing framework setup (pytest, coverage)
|
||||
- [x] Add testing framework setup (pytest, coverage)
|
||||
- [x] Improve structure detection
|
||||
- [ ] Fix bugs
|
||||
+1
-1
@@ -7,7 +7,7 @@ packages = ["src/djinit"]
|
||||
|
||||
[project]
|
||||
name = "djinitx"
|
||||
version = "0.3.4"
|
||||
version = "0.3.7"
|
||||
description = "A CLI tool to setup Django project"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
questionary==2.1.1
|
||||
rich==15.0.0
|
||||
# dev
|
||||
ruff==0.15.11
|
||||
pre-commit==4.5.1
|
||||
ruff==0.16.6
|
||||
pre-commit==4.6.2
|
||||
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name="djinitx",
|
||||
version="0.3.4",
|
||||
version="0.3.7",
|
||||
description="A CLI tool to setup Django projects",
|
||||
long_description=open("README.md").read(),
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
@@ -17,6 +17,7 @@ class ProjectMetadata:
|
||||
database_type: str = "postgresql"
|
||||
use_tailwind: bool = False
|
||||
use_htmx: bool = False
|
||||
use_vite: bool = False
|
||||
predefined_structure: bool = False
|
||||
unified_structure: bool = False
|
||||
single_structure: bool = False
|
||||
@@ -33,6 +34,7 @@ class ProjectMetadata:
|
||||
"database_type": self.database_type,
|
||||
"use_tailwind": self.use_tailwind,
|
||||
"use_htmx": self.use_htmx,
|
||||
"use_vite": self.use_vite,
|
||||
"predefined_structure": self.predefined_structure,
|
||||
"unified_structure": self.unified_structure,
|
||||
"single_structure": self.single_structure,
|
||||
|
||||
@@ -77,6 +77,7 @@ class FileCreator(BaseService):
|
||||
|
||||
secret_key = generate_secret_key()
|
||||
dev_context = {"secret_key": secret_key}
|
||||
dev_context["use_vite"] = self.metadata.get("use_vite", False)
|
||||
|
||||
# Prepare context for base.py with full AppConfig paths
|
||||
base_settings_context = base_context.copy()
|
||||
@@ -88,9 +89,10 @@ class FileCreator(BaseService):
|
||||
for name in base_settings_context["app_names"]
|
||||
]
|
||||
|
||||
# Add Tailwind and HTMX to context
|
||||
# Add Tailwind, HTMX, and Vite to context
|
||||
base_settings_context["use_tailwind"] = self.metadata.get("use_tailwind", False)
|
||||
base_settings_context["use_htmx"] = self.metadata.get("use_htmx", False)
|
||||
base_settings_context["use_vite"] = self.metadata.get("use_vite", False)
|
||||
|
||||
for filename, context in [
|
||||
("base.py", base_settings_context),
|
||||
@@ -115,7 +117,13 @@ class FileCreator(BaseService):
|
||||
) -> None:
|
||||
"""Helper to create urls.py, wsgi.py, and asgi.py."""
|
||||
|
||||
urls_context = {"url_type": "project", "api_module": api_module, "include_api": True}
|
||||
urls_context = {
|
||||
"url_type": "project",
|
||||
"project_name": self.project_name,
|
||||
"api_module": api_module,
|
||||
"include_api": True,
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
}
|
||||
if comment_out_api:
|
||||
urls_context["comment_out_api_url"] = True
|
||||
|
||||
@@ -131,8 +139,46 @@ class FileCreator(BaseService):
|
||||
filepath, template, context, f"Created {prefix}/{filename}", should_format=True
|
||||
)
|
||||
|
||||
def create_dockerfile(self) -> None:
|
||||
context = {
|
||||
"module_name": self.module_name,
|
||||
"database_type": self.metadata.get("database_type", "postgresql"),
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
"use_vite": self.metadata.get("use_vite", False),
|
||||
}
|
||||
self._render_and_create_file(
|
||||
"Dockerfile",
|
||||
"project/Dockerfile-tpl",
|
||||
context,
|
||||
"Created Dockerfile for containerization",
|
||||
)
|
||||
|
||||
def create_dockerignore(self) -> None:
|
||||
self._render_and_create_file(
|
||||
".dockerignore",
|
||||
"project/dockerignore-tpl",
|
||||
{},
|
||||
"Created .dockerignore file",
|
||||
)
|
||||
|
||||
def create_vite_config(self) -> None:
|
||||
context = {
|
||||
"module_name": self.module_name,
|
||||
"project_name": self.project_name,
|
||||
}
|
||||
self._render_and_create_file(
|
||||
"vite.config.ts",
|
||||
"project/vite.config.ts-tpl",
|
||||
context,
|
||||
"Created vite.config.ts for Vite frontend bundling",
|
||||
)
|
||||
|
||||
def create_gitignore(self) -> None:
|
||||
self._render_and_create_file(".gitignore", "project/gitignore-tpl", {}, "Created .gitignore file")
|
||||
context = {
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
"use_vite": self.metadata.get("use_vite", False),
|
||||
}
|
||||
self._render_and_create_file(".gitignore", "project/gitignore-tpl", context, "Created .gitignore file")
|
||||
|
||||
def create_requirements(self) -> None:
|
||||
context = {
|
||||
@@ -140,6 +186,7 @@ class FileCreator(BaseService):
|
||||
"database_type": self.metadata.get("database_type", "postgresql"),
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
"use_htmx": self.metadata.get("use_htmx", False),
|
||||
"use_vite": self.metadata.get("use_vite", False),
|
||||
}
|
||||
self._render_and_create_file(
|
||||
"requirements.txt",
|
||||
@@ -181,6 +228,9 @@ class FileCreator(BaseService):
|
||||
"module_name": self.module_name,
|
||||
"database_type": metadata.get("database_type", "postgresql"),
|
||||
"use_database_url": metadata.get("use_database_url", True),
|
||||
"use_tailwind": metadata.get("use_tailwind", False),
|
||||
"use_htmx": metadata.get("use_htmx", False),
|
||||
"use_vite": metadata.get("use_vite", False),
|
||||
}
|
||||
self._render_and_create_file(
|
||||
"pyproject.toml",
|
||||
@@ -198,6 +248,7 @@ class FileCreator(BaseService):
|
||||
"app_names": effective_app_modules,
|
||||
"include_apps": True,
|
||||
"include_api": True,
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
}
|
||||
self._render_and_create_file(
|
||||
"urls.py",
|
||||
@@ -209,7 +260,11 @@ class FileCreator(BaseService):
|
||||
)
|
||||
|
||||
def create_justfile(self) -> None:
|
||||
context = {"project_name": self.project_name, "module_name": self.module_name}
|
||||
context = {
|
||||
"project_name": self.project_name,
|
||||
"module_name": self.module_name,
|
||||
"use_vite": self.metadata.get("use_vite", False),
|
||||
}
|
||||
self._render_and_create_file(
|
||||
"justfile",
|
||||
"project/justfile-tpl",
|
||||
@@ -255,7 +310,13 @@ class FileCreator(BaseService):
|
||||
CommonUtils.create_file_from_template(
|
||||
project_urls_path,
|
||||
"config/urls.py-tpl",
|
||||
{"url_type": "project", "api_module": "api", "include_api": True},
|
||||
{
|
||||
"url_type": "project",
|
||||
"project_name": self.project_name,
|
||||
"api_module": "api",
|
||||
"include_api": True,
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
},
|
||||
"Updated project urls.py to include API routes",
|
||||
should_format=True,
|
||||
)
|
||||
@@ -376,7 +437,15 @@ class FileCreator(BaseService):
|
||||
def create_github_actions(self) -> None:
|
||||
github_dir = os.path.join(self.project_root, ".github", "workflows")
|
||||
os.makedirs(github_dir, exist_ok=True)
|
||||
context = {"project_name": self.project_name, "module_name": self.module_name}
|
||||
context = {
|
||||
"project_name": self.project_name,
|
||||
"module_name": self.module_name,
|
||||
"database_type": self.metadata.get("database_type", "postgresql"),
|
||||
"use_database_url": self.metadata.get("use_database_url", True),
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
"use_htmx": self.metadata.get("use_htmx", False),
|
||||
"use_vite": self.metadata.get("use_vite", False),
|
||||
}
|
||||
workflow_file = os.path.join(github_dir, "ci.yml")
|
||||
self._render_and_create_file(
|
||||
workflow_file,
|
||||
@@ -387,7 +456,15 @@ class FileCreator(BaseService):
|
||||
)
|
||||
|
||||
def create_gitlab_ci(self) -> None:
|
||||
context = {"project_name": self.project_name, "module_name": self.module_name}
|
||||
context = {
|
||||
"project_name": self.project_name,
|
||||
"module_name": self.module_name,
|
||||
"database_type": self.metadata.get("database_type", "postgresql"),
|
||||
"use_database_url": self.metadata.get("use_database_url", True),
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
"use_htmx": self.metadata.get("use_htmx", False),
|
||||
"use_vite": self.metadata.get("use_vite", False),
|
||||
}
|
||||
self._render_and_create_file(
|
||||
".gitlab-ci.yml",
|
||||
"project/ci/gitlab_ci-tpl",
|
||||
@@ -421,6 +498,7 @@ class FileCreator(BaseService):
|
||||
"database_type": self.metadata.get("database_type", "postgresql"),
|
||||
"use_tailwind": self.metadata.get("use_tailwind", False),
|
||||
"use_htmx": self.metadata.get("use_htmx", False),
|
||||
"use_vite": self.metadata.get("use_vite", False),
|
||||
},
|
||||
"cicd": {
|
||||
"github": self.metadata.get("use_github_actions", False),
|
||||
|
||||
@@ -104,12 +104,17 @@ class SetupCreator:
|
||||
self.file_creator.create_readme,
|
||||
self.file_creator.create_env_file,
|
||||
self.file_creator.create_djinit_config,
|
||||
self.file_creator.create_dockerfile,
|
||||
self.file_creator.create_dockerignore,
|
||||
lambda: self.file_creator.create_pyproject(self.metadata),
|
||||
]
|
||||
|
||||
for step_func in utility_steps:
|
||||
step_func()
|
||||
|
||||
if self.metadata.get("use_vite", False):
|
||||
self.file_creator.create_vite_config()
|
||||
|
||||
UIFormatter.print_success("Created all utility files successfully!")
|
||||
|
||||
def _create_cicd_pipelines(self) -> None:
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import environ
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
env_path = Path(__file__).resolve().parent.parent / ".env"
|
||||
env = environ.Env()
|
||||
environ.Env.read_env()
|
||||
environ.Env.read_env(env_path)
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", env("DJANGO_SETTINGS_MODULE", default="[[ project_name ]].settings.production"))
|
||||
application = get_asgi_application()
|
||||
@@ -9,14 +9,15 @@ from pathlib import Path
|
||||
|
||||
import environ
|
||||
|
||||
env_path = Path(__file__).resolve().parent.parent.parent / ".env"
|
||||
env = environ.Env()
|
||||
environ.Env.read_env()
|
||||
environ.Env.read_env(env_path)
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
||||
# See https://docs.djangoproject.com/en/6.1/howto/deployment/checklist/
|
||||
|
||||
|
||||
# Application definition
|
||||
@@ -25,7 +26,7 @@ THIRD_PARTY_APPS = [
|
||||
"rest_framework_simplejwt",
|
||||
"rest_framework_simplejwt.token_blacklist",
|
||||
"corsheaders",
|
||||
"drf_spectacular",
|
||||
"drf_yasg",
|
||||
"django_tailwind_cli", # @IF use_tailwind
|
||||
"django_htmx", # @IF use_htmx
|
||||
]
|
||||
@@ -82,7 +83,7 @@ WSGI_APPLICATION = "[[ project_name ]].wsgi.application"
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
||||
# https://docs.djangoproject.com/en/6.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
@@ -101,7 +102,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.2/topics/i18n/
|
||||
# https://docs.djangoproject.com/en/6.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
TIME_ZONE = "UTC"
|
||||
@@ -138,21 +139,11 @@ REST_FRAMEWORK = {
|
||||
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
||||
"rest_framework.authentication.SessionAuthentication",
|
||||
),
|
||||
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
||||
"DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema",
|
||||
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
|
||||
"PAGE_SIZE": 20,
|
||||
}
|
||||
|
||||
# DRF Spectacular settings
|
||||
SPECTACULAR_SETTINGS = {
|
||||
"TITLE": "[[ project_name ]] API",
|
||||
"DESCRIPTION": "API documentation for [[ project_name ]]",
|
||||
"VERSION": "1.0.0",
|
||||
"SERVE_INCLUDE_SCHEMA": False,
|
||||
"COMPONENT_SPLIT_REQUEST": True,
|
||||
"SCHEMA_PATH_PREFIX": "/api/",
|
||||
}
|
||||
|
||||
# JWT Settings
|
||||
SIMPLE_JWT = {
|
||||
"ACCESS_TOKEN_LIFETIME": timedelta(days=1),
|
||||
@@ -185,7 +176,6 @@ CORS_ALLOW_HEADERS = [
|
||||
|
||||
CORS_PREFLIGHT_MAX_AGE = 86400 # 24 hours
|
||||
# Security Settings
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
X_FRAME_OPTIONS = "DENY"
|
||||
|
||||
@@ -225,3 +215,13 @@ TAILWIND_CLI_CONFIG = {
|
||||
},
|
||||
}
|
||||
# @ENDIF
|
||||
|
||||
# @IF use_vite
|
||||
# Vite Settings
|
||||
DJANGO_VITE = {
|
||||
"default": {
|
||||
"manifest_path": BASE_DIR / "static" / "dist" / "manifest.json",
|
||||
"static_url_prefix": "dist",
|
||||
}
|
||||
}
|
||||
# @ENDIF
|
||||
@@ -1,6 +1,6 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
from .base import *
|
||||
from .base import * # noqa: F401, F403
|
||||
|
||||
SECRET_KEY = "[[ secret_key ]]"
|
||||
|
||||
@@ -10,7 +10,7 @@ DEBUG = True
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
||||
# https://docs.djangoproject.com/en/6.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
@@ -30,8 +30,12 @@ DATABASES = {
|
||||
}
|
||||
}
|
||||
|
||||
# Email backend for development
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
# Email settings for development
|
||||
MAILERS = {
|
||||
"default": {
|
||||
"BACKEND": "django.core.mail.backends.console.EmailBackend",
|
||||
},
|
||||
}
|
||||
|
||||
# CORS settings for development
|
||||
# Allow all origins in development (convenient but not secure)
|
||||
@@ -40,3 +44,8 @@ CORS_ALLOW_CREDENTIALS = True
|
||||
|
||||
# Disable HTTPS redirect in development
|
||||
SECURE_SSL_REDIRECT = False
|
||||
|
||||
# @IF use_vite
|
||||
# Vite Settings
|
||||
DJANGO_VITE["default"]["dev_mode"] = True
|
||||
# @ENDIF
|
||||
@@ -1,6 +1,6 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
from .base import *
|
||||
from .base import * # noqa: F401, F403
|
||||
# @IF use_database_url
|
||||
import dj_database_url
|
||||
# @ENDIF
|
||||
@@ -43,14 +43,20 @@ CORS_ALLOW_CREDENTIALS = True
|
||||
CORS_ALLOW_PRIVATE_NETWORK = False
|
||||
|
||||
# Email settings
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_HOST = env('EMAIL_HOST', default="smtp.gmail.com")
|
||||
EMAIL_PORT = env.int('EMAIL_PORT', default=587)
|
||||
EMAIL_USE_TLS = env.bool('EMAIL_USE_TLS', default=True)
|
||||
EMAIL_HOST_USER = env('EMAIL_HOST_USER', default="")
|
||||
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD', default="")
|
||||
DEFAULT_FROM_EMAIL = env('EMAIL_HOST_USER', default="")
|
||||
SERVER_EMAIL = env('EMAIL_HOST_USER', default="")
|
||||
MAILERS = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.mail.backends.smtp.EmailBackend',
|
||||
'OPTIONS': {
|
||||
'host': env('EMAIL_HOST', default='smtp.gmail.com'),
|
||||
'port': env.int('EMAIL_PORT', default=587),
|
||||
'use_tls': env.bool('EMAIL_USE_TLS', default=True),
|
||||
'username': env('EMAIL_HOST_USER', default=''),
|
||||
'password': env('EMAIL_HOST_PASSWORD', default=''),
|
||||
},
|
||||
},
|
||||
}
|
||||
DEFAULT_FROM_EMAIL = env('EMAIL_HOST_USER', default='')
|
||||
SERVER_EMAIL = env('EMAIL_HOST_USER', default='')
|
||||
|
||||
# Security settings for production
|
||||
SECURE_SSL_REDIRECT = True
|
||||
@@ -63,12 +69,14 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SESSION_COOKIE_SECURE = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
|
||||
# Static files
|
||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||
|
||||
# @IF use_tailwind
|
||||
## Tailwind
|
||||
TAILWIND_CLI_VERSION = "4.1.3" # Pin version
|
||||
TAILWIND_CLI_AUTOMATIC_DOWNLOAD = False # Use pre-installed CLI
|
||||
TAILWIND_CLI_DIST_CSS = "static/css/tailwind.min.css"
|
||||
# @ENDIF
|
||||
|
||||
# @IF use_vite
|
||||
# Vite Settings
|
||||
DJANGO_VITE["default"]["dev_mode"] = False
|
||||
# @ENDIF
|
||||
@@ -2,16 +2,15 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.urls import path, re_path
|
||||
from django.conf import settings
|
||||
from django.views.static import serve
|
||||
# @IF include_api or include_apps
|
||||
from django.urls import include
|
||||
# @ENDIF
|
||||
# @IF include_api
|
||||
from django.conf import settings
|
||||
from drf_spectacular.views import (
|
||||
SpectacularAPIView,
|
||||
SpectacularSwaggerView,
|
||||
)
|
||||
from drf_yasg.views import get_schema_view
|
||||
from drf_yasg import openapi
|
||||
from rest_framework_simplejwt.views import (
|
||||
TokenObtainPairView,
|
||||
TokenRefreshView,
|
||||
@@ -44,11 +43,34 @@ urlpatterns = [
|
||||
|
||||
# Don't show schema in production
|
||||
if settings.DEBUG:
|
||||
schema_view = get_schema_view(
|
||||
openapi.Info(
|
||||
title="[[ project_name ]] API",
|
||||
default_version="v1",
|
||||
),
|
||||
public=True,
|
||||
)
|
||||
urlpatterns += [
|
||||
path("schema/", SpectacularAPIView.as_view(), name="schema"),
|
||||
path("docs/", SpectacularSwaggerView.as_view(url_name="schema")),
|
||||
path("docs/", schema_view.with_ui("swagger", cache_timeout=0)),
|
||||
path("schema/", schema_view.with_ui("redoc", cache_timeout=0)),
|
||||
]
|
||||
# @ENDIF
|
||||
# @IF use_tailwind
|
||||
|
||||
# django-browser-reload for tailwind live reload (dev only)
|
||||
if settings.DEBUG:
|
||||
urlpatterns += [
|
||||
path("__reload__/", include("django_browser_reload.urls")),
|
||||
]
|
||||
# @ENDIF
|
||||
|
||||
urlpatterns += [
|
||||
re_path(
|
||||
r"^media/(?P<path>.*)$",
|
||||
serve,
|
||||
{"document_root": settings.MEDIA_ROOT},
|
||||
),
|
||||
]
|
||||
# @ENDIF
|
||||
# @IF url_type == 'api_root'
|
||||
from django.urls import include, path
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import environ
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
env_path = Path(__file__).resolve().parent.parent / ".env"
|
||||
env = environ.Env()
|
||||
environ.Env.read_env()
|
||||
environ.Env.read_env(env_path)
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", env("DJANGO_SETTINGS_MODULE", default="[[ project_name ]].settings.production"))
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
FROM python:3.13-slim
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PIP_NO_CACHE_DIR=1
|
||||
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# @IF database_type == 'mysql'
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
default-libmysqlclient-dev \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
# @ELSE
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
# @ENDIF
|
||||
|
||||
# @IF use_tailwind or use_vite
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
ENV NODE_VERSION=20
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
# @ENDIF
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# @IF use_tailwind
|
||||
RUN python manage.py tailwind install
|
||||
# @ENDIF
|
||||
|
||||
# @IF use_vite
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
RUN npm run build
|
||||
# @ENDIF
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN python manage.py collectstatic --noinput || true
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["gunicorn", "[[ module_name ]].wsgi:application", "--bind", "0.0.0.0:8000"]
|
||||
@@ -11,22 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.12", "3.13"]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: test_db
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
python-version: ["3.13"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -39,71 +24,54 @@ jobs:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v4
|
||||
with:
|
||||
version: "latest"
|
||||
enable-cache: true
|
||||
cache-suffix: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync
|
||||
|
||||
# @IF use_vite
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Build frontend
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
# @ENDIF
|
||||
|
||||
- name: Run linting
|
||||
run: uv run ruff check .
|
||||
|
||||
- name: Format check
|
||||
run: uv run ruff format --check .
|
||||
|
||||
- name: Set up PostgreSQL
|
||||
env:
|
||||
DB_NAME: test_db
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_HOST: localhost
|
||||
DB_PORT: 5432
|
||||
run: |
|
||||
echo "PostgreSQL is ready"
|
||||
|
||||
- name: Run Django tests
|
||||
- name: Run Django checks
|
||||
env:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
DB_NAME: test_db
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_HOST: localhost
|
||||
DB_PORT: 5432
|
||||
run: |
|
||||
uv run python manage.py check
|
||||
|
||||
- name: Run tests
|
||||
env:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
run: |
|
||||
uv run python manage.py test
|
||||
|
||||
- name: Check migrations
|
||||
env:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
DB_NAME: test_db
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: postgres
|
||||
DB_HOST: localhost
|
||||
DB_PORT: 5432
|
||||
run: |
|
||||
uv run python manage.py makemigrations --check --dry-run
|
||||
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v4
|
||||
with:
|
||||
version: "latest"
|
||||
|
||||
- name: Install dependencies
|
||||
run: uv sync
|
||||
|
||||
- name: Run security check
|
||||
run: uv run python manage.py check --deploy
|
||||
- name: Security check
|
||||
env:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
run: |
|
||||
uv run python manage.py check --deploy
|
||||
@@ -1,133 +1,76 @@
|
||||
# GitLab CI/CD configuration for [[ project_name ]]
|
||||
|
||||
stages:
|
||||
- test
|
||||
- lint
|
||||
- test
|
||||
- security
|
||||
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
||||
UV_CACHE_DIR: "$CI_PROJECT_DIR/.cache/uv"
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .cache/pip
|
||||
- .cache/uv
|
||||
- .venv/
|
||||
|
||||
# @IF database_type == 'mysql'
|
||||
services:
|
||||
- mysql:8.0
|
||||
|
||||
variables:
|
||||
MYSQL_DATABASE: test_db
|
||||
MYSQL_ROOT_PASSWORD: test_password
|
||||
# @ELSE
|
||||
services:
|
||||
- postgres:15
|
||||
|
||||
variables:
|
||||
POSTGRES_DB: test_db
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/test_db"
|
||||
|
||||
# Use Docker-in-Docker
|
||||
services:
|
||||
- postgres:15
|
||||
# @ENDIF
|
||||
|
||||
before_script:
|
||||
- python --version
|
||||
- pip install uv
|
||||
- uv sync
|
||||
# @IF use_vite
|
||||
- npm ci
|
||||
- npm run build
|
||||
# @ENDIF
|
||||
|
||||
# Lint stage
|
||||
lint:
|
||||
stage: lint
|
||||
image: python:3.13
|
||||
script:
|
||||
- uv run ruff check .
|
||||
- uv run ruff format --check .
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
- if: $CI_COMMIT_BRANCH == 'develop'
|
||||
|
||||
# Test stage
|
||||
test:
|
||||
stage: test
|
||||
image: python:3.13
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
- if: $CI_COMMIT_BRANCH == 'develop'
|
||||
|
||||
# Multiple Python versions
|
||||
test:python310:
|
||||
stage: test
|
||||
image: python:3.10
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
test:python311:
|
||||
stage: test
|
||||
image: python:3.11
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
test:python312:
|
||||
stage: test
|
||||
image: python:3.12
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
test:python313:
|
||||
stage: test
|
||||
image: python:3.13
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
ALLOWED_HOSTS: localhost
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check
|
||||
- uv run python manage.py migrate
|
||||
- uv run python manage.py test
|
||||
only:
|
||||
- branches
|
||||
- merge_requests
|
||||
|
||||
# Security check
|
||||
security:
|
||||
stage: security
|
||||
image: python:3.13
|
||||
variables:
|
||||
DJANGO_SETTINGS_MODULE: [[ module_name ]].settings.development
|
||||
SECRET_KEY: test-secret-key-for-ci
|
||||
script:
|
||||
- pip install uv
|
||||
- uv sync
|
||||
- uv run python manage.py check --deploy
|
||||
- uv run python manage.py validate_templates
|
||||
only:
|
||||
- main
|
||||
- develop
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
- if: $CI_COMMIT_BRANCH == 'develop'
|
||||
@@ -0,0 +1,26 @@
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
*.so
|
||||
.venv
|
||||
venv
|
||||
ENV
|
||||
.env
|
||||
.git
|
||||
.gitignore
|
||||
*.md
|
||||
README*
|
||||
LICENSE
|
||||
.coverage
|
||||
htmlcov
|
||||
.pytest_cache
|
||||
.mypy_cache
|
||||
.ruff_cache
|
||||
.DS_Store
|
||||
node_modules
|
||||
*.log
|
||||
db.sqlite3
|
||||
media
|
||||
staticfiles
|
||||
@@ -20,7 +20,7 @@ DB_PORT=
|
||||
# @ENDIF
|
||||
|
||||
# Frontend URL
|
||||
FRONTEND_URL=https://frontend.your-domain.com,www.frontend.your-domain.com
|
||||
FRONTEND_URL=https://frontend.your-domain.com,https://www.frontend.your-domain.com
|
||||
|
||||
# Email settings
|
||||
EMAIL_HOST=
|
||||
|
||||
@@ -71,7 +71,9 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Node modules (if using frontend tools)
|
||||
# @IF use_tailwind or use_vite
|
||||
node_modules/
|
||||
# @ENDIF
|
||||
|
||||
# Secret keys
|
||||
secret.txt
|
||||
|
||||
@@ -14,6 +14,17 @@ dev:
|
||||
dev-server HOST="0.0.0.0" PORT="8000":
|
||||
uv run python manage.py runserver {{ HOST }}:{{ PORT }}
|
||||
|
||||
# @IF use_vite
|
||||
# Start Vite dev server
|
||||
vite:
|
||||
npm run dev
|
||||
|
||||
# Start both Django and Vite dev servers
|
||||
dev-full:
|
||||
@just dev &
|
||||
@just vite
|
||||
# @ENDIF
|
||||
|
||||
# Create migrations for all apps
|
||||
makemigrations:
|
||||
uv run python manage.py makemigrations
|
||||
|
||||
@@ -3,15 +3,28 @@
|
||||
"""generated with djinit"""
|
||||
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import environ
|
||||
|
||||
# Load environment variables from .env
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
env = environ.Env()
|
||||
env.read_env(BASE_DIR / ".env")
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", os.getenv("DJANGO_SETTINGS_MODULE", "[[ project_name ]].settings.production"))
|
||||
os.environ.setdefault(
|
||||
"DJANGO_SETTINGS_MODULE",
|
||||
env(
|
||||
"DJANGO_SETTINGS_MODULE",
|
||||
default="[[ project_name ]].settings.production",
|
||||
),
|
||||
)
|
||||
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
@@ -21,12 +34,9 @@ def main():
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
env = environ.Env()
|
||||
# Read .env file, if it exists
|
||||
environ.Env.read_env()
|
||||
|
||||
main()
|
||||
@@ -1,17 +1,17 @@
|
||||
[project]
|
||||
name = "[[ package_name ]]"
|
||||
name = "[[ project_name ]]"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
license = { text = "MIT" }
|
||||
dependencies = [
|
||||
"django",
|
||||
"django>=6.1,<7.0",
|
||||
"django-environ",
|
||||
"django-jazzmin",
|
||||
"djangorestframework",
|
||||
"djangorestframework-simplejwt",
|
||||
"drf-spectacular",
|
||||
"drf-yasg",
|
||||
"django-cors-headers",
|
||||
"whitenoise",
|
||||
# @IF database_type == 'mysql'
|
||||
@@ -20,12 +20,28 @@ dependencies = [
|
||||
# @IF database_type == 'postgresql'
|
||||
"psycopg[binary]",
|
||||
# @ENDIF
|
||||
# @IF use_tailwind
|
||||
"django-tailwind-cli",
|
||||
# @ENDIF
|
||||
# @IF use_htmx
|
||||
"django-htmx",
|
||||
# @ENDIF
|
||||
# @IF use_vite
|
||||
"django-vite",
|
||||
# @ENDIF
|
||||
# @IF use_database_url
|
||||
"dj-database-url",
|
||||
# @ENDIF
|
||||
"gunicorn",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ruff>=0.16.6",
|
||||
"pytest>=9.1.1",
|
||||
"pytest-django>=4.14.0",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
target-version = "py313"
|
||||
@@ -46,4 +62,7 @@ ignore = [
|
||||
]
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
known-first-party = ["[[ module_name ]]"]
|
||||
known-first-party = ["[[ project_name ]]"]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"[[ project_name ]]/settings/*.py" = ["F403", "F405"]
|
||||
@@ -7,11 +7,12 @@ django-environ
|
||||
django-jazzmin
|
||||
djangorestframework
|
||||
djangorestframework_simplejwt
|
||||
drf-spectacular
|
||||
drf-yasg
|
||||
django-cors-headers
|
||||
whitenoise
|
||||
django-tailwind-cli # @IF use_tailwind
|
||||
django-htmx # @IF use_htmx
|
||||
django-vite # @IF use_vite
|
||||
# @IF database_type == 'mysql'
|
||||
mysqlclient
|
||||
# @ELSE
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { defineConfig } from "vite"
|
||||
import react from "@vitejs/plugin-react-swc"
|
||||
import tailwindcss from "@tailwindcss/vite"
|
||||
import path from "path"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }) => ({
|
||||
plugins: [react(), tailwindcss()],
|
||||
base: mode === "production" ? "/static/" : "/",
|
||||
build: {
|
||||
outDir: "../[[ module_name ]]/static/dist",
|
||||
emptyOutDir: true,
|
||||
manifest: "manifest.json",
|
||||
rollupOptions: {
|
||||
input: "src/main.tsx",
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
host: "127.0.0.1",
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/media": {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
dedupe: [
|
||||
"react",
|
||||
"react-dom",
|
||||
"react/jsx-runtime",
|
||||
"react/jsx-dev-runtime",
|
||||
"@tanstack/react-query",
|
||||
],
|
||||
},
|
||||
}))
|
||||
+12
-1
@@ -25,6 +25,7 @@ class StructureOptions(TypedDict):
|
||||
use_database_url: bool
|
||||
use_tailwind: bool
|
||||
use_htmx: bool
|
||||
use_vite: bool
|
||||
use_github: bool
|
||||
use_gitlab: bool
|
||||
|
||||
@@ -179,6 +180,9 @@ class InputCollector:
|
||||
def get_htmx_choice(self) -> bool:
|
||||
return UIFormatter.confirm("Include HTMX? (via django-htmx)", default=True)
|
||||
|
||||
def get_vite_choice(self) -> bool:
|
||||
return UIFormatter.confirm("Include Vite? (via django-vite for frontend bundling)", default=False)
|
||||
|
||||
def _get_structure_metadata(self, options: StructureOptions) -> Tuple[str, str, list[str], dict]:
|
||||
"""Helper method to generate metadata dictionary."""
|
||||
project_dir = options["project_dir"]
|
||||
@@ -203,6 +207,7 @@ class InputCollector:
|
||||
database_type=options["database_type"],
|
||||
use_tailwind=options.get("use_tailwind", False),
|
||||
use_htmx=options.get("use_htmx", False),
|
||||
use_vite=options.get("use_vite", False),
|
||||
predefined_structure=options["predefined"],
|
||||
unified_structure=options["unified"],
|
||||
single_structure=options["single"],
|
||||
@@ -260,9 +265,10 @@ def get_user_input() -> Tuple[str, str, str, list, dict]:
|
||||
database_type = collector.get_database_type_choice()
|
||||
use_database_url = collector.get_database_config_choice()
|
||||
|
||||
# Step 3: Tailwind and HTMX
|
||||
# Step 3: Tailwind, HTMX, and Vite
|
||||
use_tailwind = collector.get_tailwind_choice()
|
||||
use_htmx = collector.get_htmx_choice()
|
||||
use_vite = collector.get_vite_choice()
|
||||
|
||||
# Step 3: Django Apps (Standard only)
|
||||
nested = False
|
||||
@@ -289,6 +295,7 @@ def get_user_input() -> Tuple[str, str, str, list, dict]:
|
||||
database_type=database_type,
|
||||
use_tailwind=use_tailwind,
|
||||
use_htmx=use_htmx,
|
||||
use_vite=use_vite,
|
||||
)
|
||||
return project_dir, project_name, app_names[0], app_names, metadata.to_dict()
|
||||
else:
|
||||
@@ -302,6 +309,7 @@ def get_user_input() -> Tuple[str, str, str, list, dict]:
|
||||
use_database_url=use_database_url,
|
||||
use_tailwind=use_tailwind,
|
||||
use_htmx=use_htmx,
|
||||
use_vite=use_vite,
|
||||
use_github=use_github,
|
||||
use_gitlab=use_gitlab,
|
||||
)
|
||||
@@ -340,6 +348,9 @@ def confirm_setup(project_dir: str, project_name: str, app_names: list, metadata
|
||||
use_htmx = "Yes" if metadata.get("use_htmx", False) else "No"
|
||||
console.print(f"[{UIColors.HIGHLIGHT}]HTMX:[/{UIColors.HIGHLIGHT}] {use_htmx}")
|
||||
|
||||
use_vite = "Yes" if metadata.get("use_vite", False) else "No"
|
||||
console.print(f"[{UIColors.HIGHLIGHT}]Vite:[/{UIColors.HIGHLIGHT}] {use_vite}")
|
||||
|
||||
console.print()
|
||||
UIFormatter.print_separator()
|
||||
console.print()
|
||||
|
||||
@@ -46,9 +46,10 @@ class DjangoHelper:
|
||||
"database_type": metadata.get("database_type", "postgresql"),
|
||||
"use_tailwind": metadata.get("use_tailwind", False),
|
||||
"use_htmx": metadata.get("use_htmx", False),
|
||||
"use_vite": metadata.get("use_vite", False),
|
||||
}
|
||||
|
||||
dev_context = {"secret_key": secret_key}
|
||||
dev_context = {"secret_key": secret_key, "use_vite": metadata.get("use_vite", False)}
|
||||
|
||||
# Production context also needs database info
|
||||
prod_context = base_context.copy()
|
||||
@@ -64,6 +65,7 @@ class DjangoHelper:
|
||||
"url_type": "project",
|
||||
"project_name": project_name,
|
||||
"django_version": DjangoHelper.DJANGO_VERSION,
|
||||
"use_tailwind": metadata.get("use_tailwind", False),
|
||||
}
|
||||
project_files = [
|
||||
("urls.py", "config/urls.py-tpl", urls_context),
|
||||
|
||||
Reference in new issue
Block a user