django-nx
Lightweight Django field utilities and extensions that reduce boilerplate and enforce sensible defaults.
Installation
pip install django-nx
Requires Python >= 3.9, Django >= 3.2, and Django REST Framework >= 3.12.4.
Quick Start
from nx import nx
class Product(nx.Model):
name = nx.CharField('Name') # Default max_length=128
price = nx.MoneyField('Price')
status = nx.IntChoiceField('Status', choices=ProductStatus)
tags = nx.ArrayField('Tags')
metadata = nx.ObjectField('Metadata')
Table of Contents
Model Fields
All fields automatically use verbose_name as help_text when help_text is not explicitly provided.
Character Fields
| Field | Default | Description |
|---|---|---|
CharField |
default="", blank=True, max_length=128 |
String field with empty string default |
TextField |
default="", blank=True |
Long text field with empty string default |
TextChoiceField |
max_length=64, defaults to first choice |
CharField backed by choices enum |
name = nx.CharField('Name', max_length=255)
description = nx.TextField('Description')
priority = nx.TextChoiceField('Priority', choices=PriorityLevel)
Numeric Fields
| Field | Default | Description |
|---|---|---|
IntegerField |
— | Standard integer with auto help_text |
MoneyField |
max_digits=18, decimal_places=2, default=Decimal('0') |
Decimal field for monetary values |
IntChoiceField |
default=first_choice |
SmallIntegerField backed by choices enum |
quantity = nx.IntegerField('Quantity')
price = nx.MoneyField('Price') # DECIMAL(18,2)
discount = nx.MoneyField('Discount', max_digits=5, decimal_places=4)
status = nx.IntChoiceField('Status', choices=OrderStatus)
Boolean & Temporal Fields
| Field | Default | Description |
|---|---|---|
BooleanField |
default=False, blank=True |
Boolean flag |
DateField |
null=True, blank=True |
Date picker |
DateTimeField |
null=True, blank=True |
DateTime picker |
is_active = nx.BooleanField('Is Active')
published_at = nx.DateTimeField('Published At')
birth_date = nx.DateField('Birth Date')
Relationship Fields
| Field | Default | Description |
|---|---|---|
ForeignKey |
null=True, blank=True, on_delete=CASCADE |
Standard FK with nullable defaults |
OneToOne |
null=True, blank=True, on_delete=CASCADE |
One-to-one with nullable defaults |
ManyToMany |
blank=True |
Many-to-many relation |
ShadowForeignKey |
db_constraint=False |
FK without database constraint |
ShadowOneToOne |
db_constraint=False |
One-to-one without DB constraint |
ShadowManyToMany |
db_constraint=False |
Many-to-many without DB constraint |
user = nx.ForeignKey('auth.User', 'User')
profile = nx.OneToOne('accounts.Profile', 'Profile')
tags = nx.ManyToMany('products.Tag', 'Tags')
# Soft / logical foreign key (no DB-level constraint)
legacy_id = nx.ShadowForeignKey('legacy.Model', 'Legacy Ref')
JSON & UUID Fields
| Field | Default | Description |
|---|---|---|
ObjectField |
default=dict, blank=True |
JSONField defaulting to {} |
ArrayField |
default=list, blank=True |
JSONField defaulting to [] |
ShortUUIDField |
max_length=22, auto-generated |
URL-safe concise UUID |
config = nx.ObjectField('Config')
items = nx.ArrayField('Items')
code = nx.ShortUUIDField('Code')
Base Model
nx.Model is an abstract base model that provides:
created_at– auto_now_add timestampupdated_at– auto_now timestampis_deleted– soft-delete flag (defaultFalse)- Auto-generated
db_table–{app_label}_{snake_case_model_name}viahumps.decamelize
import nx
class Product(nx.Model):
name = nx.CharField('Name', max_length=255)
class Meta:
app_label = 'shop'
# db_table = 'shop_product' # auto-generated if omitted
Explicitly set
Meta.db_tableto skip auto-naming.
QuerySet
nx.QuerySet adds soft-delete aware query methods. It reads deleted_field from Model.Meta (defaults to is_deleted).
from nx.models.querysets import QuerySet
class ProductQuerySet(QuerySet):
pass
class Product(nx.Model):
...
class Meta:
deleted_field = 'is_deleted'
Product.objects.valid() # is_deleted=False / 0
Product.objects.invalid() # is_deleted=True / 1
DRF Serializers
| Class | Description |
|---|---|
nx.drf.MoneyField |
DecimalField(max_digits=18, decimal_places=2) |
nx.drf.QuantityField |
IntegerField(min_value=0) |
nx.drf.MethodField |
Alias for SerializerMethodField |
nx.drf.AutoInstanceLookupMixin |
Mixin that auto-looks up instance by id on save |
from rest_framework import serializers
import nx
class ProductSerializer(nx.drf.AutoInstanceLookupMixin, serializers.ModelSerializer):
price = nx.drf.MoneyField()
stock = nx.drf.QuantityField()
category_name = nx.drf.MethodField()
class Meta:
model = Product
fields = ['id', 'price', 'stock', 'category_name']
def get_category_name(self, obj):
return obj.category.name if obj.category else None
DRF Views
ListMetadataMixin
Inject a top-level meta object (or any custom root key) into list responses.
from rest_framework import viewsets
import nx
class ProductViewSet(nx.drf.ListMetadataMixin, viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
list_metadata_root = "meta" # optional; omit to merge at top-level
def get_list_metadata(self, request, queryset, response):
return {
"total": queryset.count(),
"timestamp": timezone.now().isoformat(),
}
Response shape with list_metadata_root = "meta":
{
"count": 100,
"results": [...],
"meta": {
"total": 100,
"timestamp": "2024-01-15T09:30:00Z"
}
}
Response shape without list_metadata_root:
{
"count": 100,
"results": [...],
"total": 100,
"timestamp": "2024-01-15T09:30:00Z"
}
Utilities
get_stat_datetime_range
Returns today, week, month, and year datetime ranges respecting USE_TZ.
from nx.utils import get_stat_datetime_range
ranges = get_stat_datetime_range()
# ranges.today -> (2024-01-15 00:00:00, 2024-01-15 23:59:59.999999)
# ranges.week -> (Mon 00:00:00, Sun 23:59:59.999999)
# ranges.month -> (1st 00:00:00, last_day 23:59:59.999999)
# ranges.year -> (Jan 1 00:00:00, Dec 31 23:59:59.999999)
Development
# Install dependencies
uv sync
# Run tests
pytest
# Lint
ruff check .
# Build
uv build
License
MIT License — see LICENSE for details.
Release files for django-nx 0.5.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 | |
|---|---|---|---|
| django_nx-0.5.0.tar.gz | 11.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_nx-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.3 kB
Release files / django_nx-0.5.0.tar.gz
| Download URL | django_nx-0.5.0.tar.gz |
|---|---|
| Size | 11.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b36322ff2de4f39bdb87f4ee9ac861ff54165ac331045c98f5f8fb392a587934
|
|
BLAKE2b-256 checksum How to use checksums |
078827bb18ddef302343a6e148b71127efb223377abae60f37b039f6c22429cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / django_nx-0.5.0-py3-none-any.whl
| Download URL | django_nx-0.5.0-py3-none-any.whl |
|---|---|
| Size | 12.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
87aeaea0f583c9569b228ae82436ae920abf1a66c3b22348f916ee181a27d160
|
|
BLAKE2b-256 checksum How to use checksums |
fd166491710fa860aef2ec85ab233ba97730711cc216964ec7529416f92b1c6b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|