A reusable Django app for cataloging guitars.
Project description
django-guitars
Django Reinhardt was one of the greatest guitarists who ever lived — so django-guitars hands the other Django, the web framework, a better set of guitars.
A small, focused collection of reusable Django utilities: opinionated base models, PostgreSQL-enforced soft deletion, and the helpers that make them work. Use only the pieces you need.
Requirements
- Python ≥ 3.10
- Django ≥ 5.0 — uses
db_default - PostgreSQL — soft deletion and the
updated_atrefresh are enforced by PostgreSQL rules and triggers
Installation
pip install django-guitars
Add the app to your settings:
INSTALLED_APPS = [
# ...
"guitars",
]
What's inside
Base models
| Base | What you get |
|---|---|
SetarModel |
_created_at / _updated_at (DB-default NOW(); _updated_at kept current by a PostgreSQL statement trigger, so it's accurate even for bulk/raw updates), .update() / .aupdate(), cached-property invalidation on refresh_from_db(), and app_label() / model_name() / class_name() helpers |
GuitarModel |
Everything in SetarModel plus soft deletion (it is SetarModel + SoftDeletableModel) |
SoftDeletableModel |
Soft deletion on its own |
from django.db import models
from guitars.models import GuitarModel
class Article(GuitarModel):
title = models.CharField(max_length=200)
The .update() helper sets attributes and saves in one call:
article.update(title="New title") # set fields + save (only changed fields)
article.update(title="x", _save=False) # change in memory only, no DB write
await article.aupdate(title="async") # async variant
Soft deletion
For models inheriting SoftDeletableModel (or GuitarModel), .delete()
becomes a soft delete: the row stays and _deleted_at is set. Because this
is enforced by a PostgreSQL rule, it holds even for queryset bulk deletes and
raw SQL. Three managers expose the data:
Article.objects.all() # live rows only (the default manager)
Article._archives.all() # soft-deleted rows only
Article._all_objects.all() # everything
article.delete() # soft delete — sets _deleted_at
article.is_deleted # True
article.is_alive # False
# Permanently remove rows (and CASCADE-related rows), bypassing the rule:
Article._all_objects.filter(...).hard_delete()
Soft-deleting a row also soft-deletes rows related by on_delete=CASCADE.
⚠️ Required setup. The soft-delete rule (and the
updated_attrigger) live in a migration generated bymakeguitarmigrations. Until you run that command andmigrate,.delete()permanently deletes the row — the protection isn't active yet. Re-run it whenever you add or change a model that uses these bases.
makeguitarmigrations
makemigrations does not create the triggers and rules — they live in
separate migrations generated by this command, and this step is required for
soft deletion and the updated_at trigger to work. After your usual
makemigrations, run:
python manage.py makeguitarmigrations
It scans your first-party apps for models with _updated_at / _deleted_at
and writes the matching trigger/rule migrations. Tell it which apps are yours:
# settings.py
LOCAL_APPS = ["blog", "shop"] # apps the command scans
# Optional: which app hosts the shared trigger-function migration.
# Defaults to LOCAL_APPS[0].
# TRIGGER_FUNCTION_APP = "blog"
Use --check in CI to fail when advanced migrations are missing:
python manage.py makeguitarmigrations --check
DisableSignals
A context manager that temporarily disconnects Django signals — handy for bulk imports or silent saves:
from django.db.models.signals import post_save
from guitars.signals import DisableSignals
with DisableSignals(): # all default signals
instance.save() # nothing fires
with DisableSignals(signals=[post_save]): # only the listed signals
instance.save()
Development
Requires uv and Docker (for PostgreSQL).
uv sync # install dependencies + the package (editable)
docker compose up -d # start PostgreSQL (skip if you already run one on :5432)
uv run pytest # run the test suite
uv run pytest --cov=guitars --cov-report=term-missing
The test suite defines concrete models in tests/testapp (the shipped package
is abstract-only) and runs against a real PostgreSQL database so the rules and
triggers are actually exercised.
License
MIT © 2026 Behnam RK
Why "guitars"?
Django Reinhardt was a legendary jazz guitarist. This package gives the other
Django — the web framework — some better guitars: a grab-bag of utilities that
make everyday Django a little nicer. The base models keep the theme going with
SetarModel and GuitarModel.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_guitars-0.1.0.tar.gz.
File metadata
- Download URL: django_guitars-0.1.0.tar.gz
- Upload date:
- Size: 22.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
380bc374e1cda8c0a69a17e5080027cb022d113ce2da500895d9f2ab60560e45
|
|
| MD5 |
55a15412443e8f1b703c8f44a1208772
|
|
| BLAKE2b-256 |
11405b0a8215f6aa76d338aa0b8da365305a8efced93dbc9b4a1f415c5d67e86
|
File details
Details for the file django_guitars-0.1.0-py3-none-any.whl.
File metadata
- Download URL: django_guitars-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.18 {"installer":{"name":"uv","version":"0.11.18","subcommand":["publish"]},"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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cca317da3de2a62e3fb5bfef8409fd266b742287d8cf30c0bc32054d869e403
|
|
| MD5 |
fc72149b523d9809e1320a3989a0e101
|
|
| BLAKE2b-256 |
b0b7255578362da51affe369ae882a60981f0baf4676d46698a522c88b4a6822
|