Metadata-Version: 2.5
Name: django-postman-gen
Version: 1.0.0
Summary: Generate and sync a Postman collection from your Django project by pure introspection.
Project-URL: Homepage, https://github.com/Santoshrawal1125/django-postman-generator
Project-URL: Repository, https://github.com/Santoshrawal1125/django-postman-generator
Project-URL: Issues, https://github.com/Santoshrawal1125/django-postman-generator/issues
Author-email: Santosh Rawal <santoshrawal1125@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: api,codegen,collection,django,drf,postman
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Code Generators
Requires-Python: >=3.10
Requires-Dist: django>=4.2
Requires-Dist: djangorestframework>=3.14
Provides-Extra: spectacular
Requires-Dist: drf-spectacular>=0.27; extra == 'spectacular'
Description-Content-Type: text/markdown

# django-postman-gen

Generate — and keep in sync — a Postman collection straight from your Django
project. No AI, no annotations required: it walks the URL resolver, reads your
serializers and permissions, and writes a Postman v2.1 collection you can
commit next to your code.

```
python manage.py postman_sync
```

## Why

Hand-maintaining a Postman collection means every new endpoint is a chore:
create the request, type the URL, write an example body, remember the auth.
This app automates all of it and — unlike one-shot exporters — **syncs**
instead of overwriting: your descriptions, saved examples and scripts survive
regeneration.

## What it covers

- **Every view type** — `ModelViewSet` (routers), `APIView` subclasses and
  `@api_view` functions, discovered through the URL resolver, not the router.
- **Request bodies** via a fallback chain, no code changes needed:
  1. `@extend_schema(request=...)` (drf-spectacular, optional)
  2. `serializer_class` / `get_serializer_class()`
  3. AST scan of the handler for `SomeSerializer(data=request.data)` —
     covers fat APIViews with inline serializers
  4. otherwise an empty body flagged `TODO`
- **Realistic example JSON** from serializer fields — real-looking values
  chosen by field name (`email`, `phone`, `price`, `first_name`), with types,
  `choices` and `max_length` respected. File fields get multipart formdata.
- **Requests chained into a runnable flow** (see below).
- **Auth wired up**: collection-level Bearer `{{access_token}}`, `noauth` on
  `AllowAny` endpoints, and a test script injected into your login request
  that saves the token from the response — log in once, everything works.
- **Path params** become Postman variables with sensible example values;
  list endpoints get (disabled) `search` / `ordering` / filter / pagination
  query params from your view configuration.
- **Numbered folders** (`01. Auth`, `02. Shop`, ...) in run order, so the
  sidebar reads like the flow instead of an alphabetical pile.

## A collection you can actually run

The generated collection is a working flow, not a folder of disconnected
placeholders. Hit **Run collection** and it goes signup → login → create →
read → update → delete, carrying real data through:

- **Shared credentials.** Fields like `email`, `username`, `password` and
  `phone` become collection variables, so the values you sign up with are the
  values you log in with. Change `{{user_email}}` once and the whole chain
  follows.
- **Captured ids.** A resource with a create endpoint saves the new id from
  the response into `{{product_id}}`, and its detail requests use that as the
  path parameter — so `GET /products/:pk/` hits the object you just created.
  Foreign keys in other bodies (`product_id`) reuse the same variable.
- **Captured tokens.** Your login request saves the token into
  `{{access_token}}`, which every authenticated request inherits.
- **Order that follows the data.** Auth first, then resources sorted so
  whatever captures `{{product_id}}` runs before whatever needs it — even
  across folders — and delete comes last so a full run doesn't destroy the
  object halfway through.

Every variable is seeded with a realistic default, so any single request still
works on its own without running the chain first. Set
`DATA["UNIQUE_SIGNUP"] = True` to have signup randomize its email/username per
run, so you can re-run it without tripping uniqueness constraints.

## Sync, not overwrite

`postman_sync` performs a three-way merge using a lock file
(`collection.lock.json`, commit it) that fingerprints what the generator
last wrote:

- fields you never touched are updated when the code changes
- fields you edited by hand are kept (a conflict is reported if the code
  changed too; `--force-managed` overwrites)
- requests you added manually in Postman are never touched, and neither are
  your saved response examples, extra headers or your own test scripts
- endpoints deleted from the code are flagged `[GONE]`, not deleted
- a request you dragged into your own folder stays there; one still sitting
  where the generator put it gets re-filed when the folder strategy changes
- URL renames are detected through the view identity, so your edits follow
  the request to its new path

Output is deterministic — running the command twice produces byte-identical
files, so collection diffs are reviewable in PRs.

## Install

```
pip install django-postman-gen            # or: uv add django-postman-gen
pip install django-postman-gen[spectacular]   # with @extend_schema support
```

```python
INSTALLED_APPS = [
    ...
    "postman_gen",
]
```

## Configure

Everything is optional; these are the defaults:

```python
POSTMAN_GEN = {
    "COLLECTION_NAME": None,               # defaults to the project name
    "OUTPUT_PATH": "postman/collection.json",
    "ENVIRONMENT_PATH": None,              # optional Postman environment, created once
    "BASE_URL": "http://localhost:8000",
    "FOLDER_STRATEGY": "app_label",        # or "url_segment"
    "NUMBER_FOLDERS": True,                # "01. Auth", "02. Shop", ...
    "AUTH_FOLDER": "Auth",                 # None keeps login/signup in their app folder
    "INCLUDE": [],                         # path regexes; empty = everything
    "EXCLUDE": [],
    "INCLUDE_PLAIN_VIEWS": False,          # non-DRF views
    "REMOVED_PREFIX": "[GONE] ",
    "AUTH": {
        "TOKEN_VARIABLE": "access_token",
        "LOGIN_PATHS": ["/api/auth/login/", "/api/auth/refresh/"],
        "SIGNUP_PATHS": [],                # auto-detected from "signup"/"register"
        "TOKEN_JSON_PATH": "access",       # dotted path into the login response
    },
    "DATA": {
        "LINK_REQUESTS": True,             # share values across requests
        "IDENTITY_FIELDS": ["email", "username", "password", "phone"],
        "ID_JSON_PATH": "id",              # where a create response carries the id
        "UNIQUE_SIGNUP": False,            # randomize signup email/username per run
        "VALUES": {},                      # field name -> your own example value
    },
}
```

Use `DATA["VALUES"]` when the built-in guess isn't right for your domain:

```python
"VALUES": {"email": "qa@mycompany.com", "price": "1499.00", "city": "Mumbai"},
```

## Use

```
python manage.py postman_sync              # generate / sync
python manage.py postman_sync --dry-run    # show what would change
python manage.py postman_sync --check      # CI: exit non-zero when out of sync
python manage.py postman_sync --force-managed   # overwrite manual edits
```

Import `postman/collection.json` into Postman. Re-import after each sync, or
point Postman at the file in your repo.

## Requirements

Python ≥ 3.10, Django ≥ 4.2, Django REST Framework ≥ 3.14.
drf-spectacular is optional.

## License

MIT
