Metadata-Version: 2.4
Name: dflango
Version: 0.1.0
Summary: Django-like utilities for Flask applications
Author-email: Martino Scarcia <martino.scarcia@hybrissoftware.it>
License-Expression: MIT
Project-URL: Homepage, https://git.hybrissoftware.it/hybris/dflango
Project-URL: Repository, https://git.hybrissoftware.it/hybris/dflango
Project-URL: Issues, https://git.hybrissoftware.it/hybris/dflango/-/issues
Keywords: flask,django,web,framework,utilities
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
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: Framework :: Flask
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: flask>=2.0.0
Requires-Dist: flask-sqlalchemy>=3.0.0
Requires-Dist: flask-migrate>=4.0.0
Requires-Dist: flask-cors>=5.0.0
Requires-Dist: marshmallow>=3.0.0
Requires-Dist: sqlalchemy>=1.4.0
Requires-Dist: pyjwt>=2.0.0
Requires-Dist: click>=8.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: flake8>=6.0.0; extra == "dev"
Requires-Dist: isort>=5.12.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
Dynamic: license-file

# dflango

[![Pipeline Status](https://git.hybrissoftware.it/hybris/dflango/badges/main/pipeline.svg)](https://git.hybrissoftware.it/hybris/dflango/-/pipelines)
[![PyPI version](https://badge.fury.io/py/dflango.svg)](https://badge.fury.io/py/dflango)
[![Python versions](https://img.shields.io/pypi/pyversions/dflango.svg)](https://pypi.org/project/dflango/)

Django-like utilities for Flask applications. This package provides useful tools and patterns inspired by Django to make Flask development more productive.

## Features

- **ModelSchema**: Marshmallow schemas with integrated SQLAlchemy model validation
- **Custom Fields**: Extended Marshmallow fields for common data types
- **Base Views**: Generic class-based views for list and detail operations
- **Authentication Services**: JWT-based authentication utilities
- **Management Commands**: Flask CLI commands for common tasks like loading fixtures
- **Route Registry**: Centralized route management for better organization
- **Template Generation**: Helpers for generating template application structures and files

## Installation

### From PyPI (when published)

```bash
pip install dflango
```

### From TestPyPI (for testing)

To test the latest version from TestPyPI:

```bash
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ dflango
```

**Note**: The `--extra-index-url` flag is required to install dependencies from the main PyPI repository, as TestPyPI doesn't host all packages.

### From source

```bash
git clone https://git.hybrissoftware.it/hybris/dflango.git
cd dflango
pip install -e .
```

### For development

```bash
pip install -e ".[dev]"
```

## Quick Start

```python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from dflango import DFlango

# Create Flask app
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'

# Initialize SQLAlchemy
db = SQLAlchemy(app)

# Initialize DFlango
dflango = DFlango()
dflango.init_app(app, db)

# Or in one step:
# dflango = DFlango(app, db)
```

### Using ModelSchema

```python
from dflango import ModelSchema
from marshmallow import fields

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

class UserSchema(ModelSchema):
    class Meta:
        model = User
    
    username = fields.Str(required=True)
    email = fields.Email(required=True)

# Validate and save
schema = UserSchema()
schema.validate(request.json)
user = schema.save()
```

### Using Class-Based Views

```python
from dflango.views import DetailView, ListView

class UserDetailView(DetailView):
    model = User
    schema_class = UserSchema

class UserListView(ListView):
    model = User
    schema_class = UserSchema
    
# Register routes
app.add_url_rule('/users/<int:pk>', view_func=UserDetailView.as_view('user_detail'))
app.add_url_rule('/users', view_func=UserListView.as_view('user_list'))
```

### URL Routing with Route Registry

You can organize your routes using the `RouteRegistry` class like in the example below:

```python
# Flask
from flask import Blueprint

# dFlango
from dflango.routes import RouteRegistry

# Views
from .views.users import UserListView

# Create route registry
class UsersRoutes(RouteRegistry):
    blueprint = Blueprint('users', __name__)
    routes = [
        ('/users', UserListView, 'user_list'),
    ]
```

### Using Management Commands

You can launch management commands via Flask CLI. For example:

```bash
flask load-fixtures path/to/fixtures.json
```

```bash
flask start-app myapp
```

### Configuration

You can configure dflango in three ways:

#### 1. Through Flask Config Class (Recommended)

```python
class Config:
    # Flask settings
    SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db'
    SECRET_KEY = 'your-secret-key'
    
    # DFlango settings (prefix with DFLANGO_)
    DFLANGO_DEFAULT_PAGE_SIZE = 50
    DFLANGO_MAX_PAGE_SIZE = 200
    DFLANGO_ENABLE_SOFT_DELETE = True
    DFLANGO_JWT_SECRET_KEY = 'your-jwt-secret'
    DFLANGO_JWT_EXPIRATION_DELTA = 7200  # 2 hours

app.config.from_object(Config)
dflango = DFlango(app, db)
```

#### 2. Direct app.config update

```python
app.config.update(
    DFLANGO_DEFAULT_PAGE_SIZE=20,
    DFLANGO_MAX_PAGE_SIZE=100,
    DFLANGO_ENABLE_SOFT_DELETE=True,
)
dflango = DFlango(app, db)
```

#### 3. Programmatically after initialization

```python
dflango = DFlango(app, db)

# Update specific settings
dflango.update_config(DEFAULT_PAGE_SIZE=30, ENABLE_SOFT_DELETE=False)

# Or reload from app.config
app.config['DFLANGO_DEFAULT_PAGE_SIZE'] = 40
dflango.reload_config()
```

### Available Configuration Options

| Config Key | Default | Description |
|------------|---------|-------------|
| `DFLANGO_JWT_SECRET_KEY` | `None` | Secret key for JWT encoding/decoding |
| `DFLANGO_JWT_ALGORITHM` | `'HS256'` | JWT algorithm |
| `DFLANGO_JWT_EXPIRATION_DELTA` | `3600` | JWT expiration in seconds |
| `DFLANGO_DEFAULT_PAGE_SIZE` | `20` | Default items per page |
| `DFLANGO_MAX_PAGE_SIZE` | `100` | Maximum items per page |
| `DFLANGO_ENABLE_SOFT_DELETE` | `True` | Enable soft delete functionality |
| `DFLANGO_SOFT_DELETE_FIELD` | `'deleted_at'` | Field name for soft delete timestamp |
| `DFLANGO_ENABLE_QUERY_LOGGING` | `False` | Enable query logging |
| `DFLANGO_STRICT_VALIDATION` | `True` | Enable strict validation |
| `DFLANGO_FIXTURES_PATH` | `'fixtures'` | Path to fixtures directory |
| `DFLANGO_CORS_ORIGINS` | `'*'` | CORS allowed origins |
| `DFLANGO_CORS_ALLOW_HEADERS` | `'*'` | CORS allowed headers |
| `DFLANGO_CORS_SUPPORTS_CREDENTIALS` | `False` | CORS support credentials |

## Requirements

- Python >= 3.8
- Flask >= 2.0.0
- Flask-SQLAlchemy >= 3.0.0
- Flask-Migrate >= 4.0.0
- Flask-Cors >= 5.0.0
- Marshmallow >= 3.0.0
- SQLAlchemy >= 1.4.0
- PyJWT >= 2.0.0

## Development

To contribute to dflango:

1. Clone the repository
2. Create a virtual environment: `python -m venv venv`
3. Activate it: `source venv/bin/activate` (Linux/Mac) or `venv\Scripts\activate` (Windows)
4. Install in development mode: `pip install -e ".[dev]"`
5. Install pre-commit hooks: `pre-commit install`

### Code Formatting

The project uses **black**, **isort**, and **flake8** for code formatting and style.

**Format code automatically:**
```bash
# Using the script
./format.sh

# Or manually
black dflango/
isort dflango/
```

**Check formatting (what CI does):**
```bash
black --check dflango/
isort --check-only dflango/
flake8 dflango/
```

**Pre-commit hooks (automatic formatting before commit):**
```bash
# Install once
pre-commit install

# Run manually on all files
pre-commit run --all-files
```

The CI pipeline will **verify** (not fix) that code is properly formatted. If it fails, run `./format.sh` locally and commit the changes.

### Running Tests

```bash
pytest
```

## License

This project is licensed under the MIT License - see the LICENSE file for details.



## Building and Publishing to PyPI

### Automatic Release with GitLab CI/CD (Recommended)

The project uses GitLab CI/CD to automatically publish to PyPI when you create a tag.

**Initial setup** (one-time only):
1. Create an API token on [pypi.org/manage/account/token](https://pypi.org/manage/account/token/)
2. Add the token in GitLab: **Settings** → **CI/CD** → **Variables**
   - Key: `PYPI_TOKEN`
   - Value: Your PyPI token

**To release a new version:**
```bash
# 1. Update version in pyproject.toml and dflango/__init__.py
# 2. Commit and push
git add .
git commit -m "Release v0.1.1"
git push

# 3. Create and push the tag
git tag v0.1.1
git push origin v0.1.1

# 4. The pipeline builds and waits for confirmation on GitLab UI
# 5. Click Play on GitLab → Pipelines to publish
```

📚 **Complete guide**: See [RELEASE.md](RELEASE.md)

---

### Manual Publication (Optional)

If you prefer to publish manually:

### Step 1: Install the necessary tools

```bash
python -m pip install --upgrade build twine
```

### Step 2: Clean and build

```bash
rm -rf dist/ build/ *.egg-info
python -m build
```

### Step 3: Verify and publish

```bash
twine check dist/*
twine upload dist/*
```

Or use the automated script:
```bash
./publish.sh
```
