Django Model Scanner
A static analysis tool for Django models using pylint and astroid. Scan Django model definitions and export them to structured YAML without importing or executing any Django code.
Why?
Existing Django model analysis tools require executing Django code (django.setup()), which:
- ❌ Is slow (full application initialization)
- ❌ Has side effects (signals, database connections)
- ❌ Requires proper environment setup (settings, database config)
- ❌ Cannot analyze broken or untrusted code safely
This tool uses static AST analysis with astroid to:
- ✅ Scan models without code execution
- ✅ Work without Django runtime or database
- ✅ Handle all import styles and aliases
- ✅ Support abstract inheritance
- ✅ Export to structured YAML
Installation
pip install -e .
Important: Django must be installed for astroid type inference:
# Install with Django
pip install -e ".[examples]"
# Or install Django separately
pip install django>=3.2
Or with development dependencies:
pip install -e ".[dev]"
After installation, the django-model-scanner command will be available in your PATH.
Quick Start
CLI Usage (Recommended)
The simplest way to use the scanner:
# Scan a Django project or app
python -m django_model_scanner -p /path/to/project
# Or use the installed command
django-model-scanner -p /path/to/project
# Specify custom output location
python -m django_model_scanner -p ./myapp -o models.yaml
# Scan specific models file
django-model-scanner -p ./blog/models.py -o blog_models.yaml
This generates a YAML file (default: django_models.yaml) with all discovered models.
Usage with Pylint (Advanced)
For direct pylint integration:
# Scan specific models file
python -m pylint myapp/models.py \
--load-plugins=django_model_scanner.checker \
--disable=all
# Scan all Python files in directory (recursive)
python -m pylint myapp/*.py \
--load-plugins=django_model_scanner.checker \
--disable=all
# Scan entire project
python -m pylint . \
--load-plugins=django_model_scanner.checker \
--disable=all
This generates django_models.yaml with all discovered models.
CLI Reference
Command-line Options
python -m django_model_scanner [OPTIONS]
# or
django-model-scanner [OPTIONS]
Options:
-p, --project PATH(required): Path to Django project, app, or models.py file to scan-o, --output FILE(optional): Output YAML file path (default:django_models.yaml)--version: Show version and exit-h, --help: Show help message and exit
Examples
# Basic usage with default output
django-model-scanner -p /path/to/project
# Custom output location
django-model-scanner -p ./src -o output/models.yaml
# Scan specific app
django-model-scanner -p ./blog -o blog_models.yaml
# Scan single models file
django-model-scanner -p ./myapp/models.py -o myapp.yaml
# Show help
django-model-scanner --help
# Show version
django-model-scanner --version
Advanced Usage (Pylint Integration)
For users who need direct pylint control:
Basic Scan
# Scan a specific models file
python -m pylint myapp/models.py --load-plugins=django_model_scanner.checker --disable=all
# Scan all .py files in a directory
python -m pylint myapp/*.py --load-plugins=django_model_scanner.checker --disable=all
# Scan entire project recursively
python -m pylint . --load-plugins=django_model_scanner.checker --disable=all
Custom Output Path
pylint myapp/ \
--load-plugins=django_model_scanner.checker \
--disable=all \
--django-models-output=output/models.yaml
Verbose Mode
pylint myapp/ \
--load-plugins=django_model_scanner.checker \
--disable=all \
--django-models-verbose=y
Output Format
Example YAML
blog.models.TimestampedModel:
module: blog.models
abstract: true
bases: []
fields:
created_at:
type: DateTimeField
auto_now_add: true
updated_at:
type: DateTimeField
auto_now: true
blog.models.Category:
module: blog.models
abstract: false
bases: []
table: blog_categories
fields:
id:
type: AutoField
primary_key: true
name:
type: CharField
max_length: 100
slug:
type: SlugField
unique: true
blog.models.Post:
module: blog.models
abstract: false
bases:
- blog.models.TimestampedModel
table: blog_post
fields:
created_at:
type: DateTimeField
auto_now_add: true
updated_at:
type: DateTimeField
auto_now: true
title:
type: CharField
max_length: 200
status:
type: CharField
max_length: 20
choices:
- [draft, Draft]
- [published, Published]
- [archived, Archived]
default: draft
author:
type: ForeignKey
on_delete: models.CASCADE
related_name: posts
relationships:
author:
type: ForeignKey
to: auth.models.User
on_delete: models.CASCADE
related_name: posts
category:
type: ForeignKey
to: blog.models.Category
on_delete: models.SET_NULL
related_name: posts
Schema Structure
Each model entry contains:
- module: Python module path
- abstract: Boolean indicating if model is abstract
- bases: List of Django Model base classes (excluding
django.db.models.Model) - table: Database table name (only for concrete models)
- fields: Dictionary of field definitions
- Field name → field properties (type, options)
- Field choices are exported as structured lists
- Defaults, booleans, and numbers are properly typed
- relationships: Dictionary of relationship metadata (ForeignKey, ManyToMany, OneToOne)
- Includes:
to,on_delete,related_name,through, etc.
- Includes:
Features
✅ Model Detection
- Detects Django models via inheritance from
django.db.models.Model - Handles direct and indirect inheritance
- Supports aliased imports (
from django.db.models import Model as DjangoModel) - Works across files and modules
✅ Field Parsing
- Extracts all Django field types (CharField, IntegerField, etc.)
- Captures field options (max_length, null, blank, default, etc.)
- Identifies primary keys
- Handles various import styles
✅ Relationship Resolution
- ForeignKey relationships
- OneToOneField relationships
- ManyToManyField relationships
- Self-referential relationships (
"self") - String model references (
"app.Model","Model") - Cascade behaviors (
on_delete) - Related names and through models
✅ Abstract Inheritance
- Identifies abstract models (
Meta.abstract = True) - Merges fields from abstract parents into concrete children
- Handles multi-level inheritance
- Preserves field order per MRO
✅ YAML Export
- Structured, machine-readable output
- Normalized values (booleans, numbers, strings)
- Preserves definition order
- Separate fields and relationships sections
Example
See the examples/blog/models.py file for a complete example with:
- Abstract base models
- ForeignKey relationships
- ManyToMany relationships
- Self-referential relationships
- OneToOne relationships
- Custom table names
Run the scanner on the example:
# Scan the example models file
pylint examples/blog/models.py \
--load-plugins=django_model_scanner.checker \
--disable=all
# Or use the quickstart script
./quickstart.sh
How It Works
- Pylint Integration: Runs as a pylint checker, leveraging pylint's file traversal
- AST Analysis: Uses astroid to analyze Python AST without executing code
- Model Detection: Identifies Django models via inheritance checking
- Field Extraction: Parses field definitions and options from AST nodes
- Two-Pass Processing:
- Pass 1: Collect all models
- Pass 2: Merge abstract inheritance
- YAML Export: Normalizes values and exports to structured YAML
Architecture
┌─────────────────────┐
│ Pylint Framework │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ DjangoModelChecker │
│ (checker.py) │
└──────────┬──────────┘
│
┌──────┴──────┬──────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌────────┐
│ast_utils│ │ model_ │ │export │
│ .py │ │parser.py│ │ .py │
└─────────┘ └─────────┘ └────────┘
Use Cases
- 📚 Documentation: Auto-generate model reference docs
- 📊 ER Diagrams: Convert to diagram formats (Mermaid, GraphViz)
- 🔍 Schema Analysis: Track model changes over time
- ✅ Migration Validation: Compare models against migrations
- 📈 Metrics: Calculate model complexity, field counts
- 🔗 Relationship Mapping: Visualize model dependencies
Limitations
- Dynamic Fields: Cannot detect programmatically generated fields
- Proxy Models: Not supported in v0.1 (coming in future release)
- Multi-table Inheritance: Not supported in v0.1
- Custom Metaclasses: May not work with heavily customized model metaclasses
- Standard App Structure: Assumes
app.modelsmodule structure
Development
Run Tests
python tests/test_scanner.py
Or with pytest:
pytest tests/
Project Structure
django_model_scanner/
├── __init__.py # Package initialization
├── ast_utils.py # AST helper functions
├── model_parser.py # Model parsing logic
├── export.py # YAML export
└── checker.py # Pylint checker
examples/
└── blog/
└── models.py # Example Django models
tests/
└── test_scanner.py # Unit tests
Configuration
The checker supports these options:
--django-models-output=<path>: Output file path (default:django_models.yaml)--django-models-verbose=<y/n>: Enable verbose output (default:n)
Contributing
Contributions welcome! Please:
- Follow the existing code style
- Add tests for new features
- Update documentation
- Keep changes focused and minimal
License
MIT License - see LICENSE file for details
Related Projects
Roadmap
- Proxy model support
- Multi-table inheritance
- JSON export format
- ER diagram generation
- Migration validation
- Schema diff tool
- Custom field type plugins
Metadata
Release files for django-model-scanner 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| django_model_scanner-0.1.0.tar.gz | 17.2 kB | Without provenance | Details for django_model_scanner-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| django_model_scanner-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for django_model_scanner-0.1.0-py3-none-any.whl |
Total release size: 35.4 kB
Release files / django_model_scanner-0.1.0.tar.gz
| Download URL | django_model_scanner-0.1.0.tar.gz |
|---|---|
| Size | 17.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a601f27f634f28a6297364f38703c3491a6b8c6e8132fd83c843055d2ac815df
|
|
BLAKE2b-256 checksum How to use checksums |
fe94cb6abf60ccc81abfd43eb46ca903b856d96b465c6af6dfa8d6be7b97bc34
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / django_model_scanner-0.1.0-py3-none-any.whl
| Download URL | django_model_scanner-0.1.0-py3-none-any.whl |
|---|---|
| Size | 18.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
868592abf31a99bfbcb4d1892d3e262c99175ab9f993fbf1c50af9beb30144d1
|
|
BLAKE2b-256 checksum How to use checksums |
d57bb6970ab1be7a58ae53dc2fd6bd84cfedc019da46f6877c657fa83c953bbb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for django-model-scanner 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.