Skip to main content

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.

PyPI version Python versions License: MIT

Requirements

  • Python ≥ 3.10
  • Django ≥ 5.0 — uses db_default
  • PostgreSQL — soft deletion and the updated_at refresh 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_at trigger) live in a migration generated by makeguitarmigrations. Until you run that command and migrate, .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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_guitars-0.1.0.tar.gz (22.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_guitars-0.1.0-py3-none-any.whl (17.0 kB view details)

Uploaded Python 3

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

Hashes for django_guitars-0.1.0.tar.gz
Algorithm Hash digest
SHA256 380bc374e1cda8c0a69a17e5080027cb022d113ce2da500895d9f2ab60560e45
MD5 55a15412443e8f1b703c8f44a1208772
BLAKE2b-256 11405b0a8215f6aa76d338aa0b8da365305a8efced93dbc9b4a1f415c5d67e86

See more details on using hashes here.

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

Hashes for django_guitars-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1cca317da3de2a62e3fb5bfef8409fd266b742287d8cf30c0bc32054d869e403
MD5 fc72149b523d9809e1320a3989a0e101
BLAKE2b-256 b0b7255578362da51affe369ae882a60981f0baf4676d46698a522c88b4a6822

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page