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')
Choices
IntegerChoices
Auto-numbered integer choices starting from 1 (configurable via inner Meta.start).
import nx
class Priority(nx.IntegerChoices):
class Meta:
start = 1
LOW = "Low Priority" # 1
MEDIUM = "Medium Priority" # 2
HIGH = "High Priority" # 3
# Explicit values still work
class Status(nx.IntegerChoices):
PENDING = "Pending" # 1
APPROVED = 10 # 10
REJECTED = "Rejected" # 11
# Custom tuples are preserved
class Custom(nx.IntegerChoices):
NORMAL = "Normal" # 1
SPECIAL = (99, "Special") # 99
ZeroBasedChoices
Same as IntegerChoices, but defaults to starting from 0.
class Priority(nx.ZeroBasedChoices):
LOW = "Low" # 0
MEDIUM = "Medium" # 1
HIGH = "High" # 2
TextChoices
Auto-lowercased values for consistent API keys.
class TriggerType(nx.TextChoices):
SALE_AMOUNT = "Sale Amount" # value = "sale_amount"
ORDER_COUNT = "Order Count" # value = "order_count"
# Custom tuples are preserved
class Status(nx.TextChoices):
ACTIVE = "Active" # value = "active"
CUSTOM = ("custom_key", "Custom") # value = "custom_key"
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.4.1
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.4.1.tar.gz | 12.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_nx-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.7 kB
Release files / django_nx-0.4.1.tar.gz
| Download URL | django_nx-0.4.1.tar.gz |
|---|---|
| Size | 12.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f9a96c14927e6e0b7f36f04ab8e276c0722a55f81924ca76740d9510146480ed
|
|
BLAKE2b-256 checksum How to use checksums |
3018510f0aa617d0a156185168d2560c3636c45fb08ae539d0dd1c6be4df84fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","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.4.1-py3-none-any.whl
| Download URL | django_nx-0.4.1-py3-none-any.whl |
|---|---|
| Size | 14.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9c463c3f2826a69f71d29a644986fc5c7e9ad524f0a0014b81e2742c1b885a31
|
|
BLAKE2b-256 checksum How to use checksums |
f4c1f56c04163a934826169941bf900917f1c61d4385da3a4d6ccf9bb7a49e43
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","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}
|