Skip to main content

zibanu.django

Base framework package of Zibanu Django: ORM base classes, Django REST Framework extensions, and shared utilities that every optional package builds on.

Overview

zibanu.django is the always-installed foundation of the Zibanu Django framework. It has no dependency on any optional package (auth, repository, logging, template, gis, mqtt, state_machine) — each of those depends on this one, never the other way around. Its own runtime dependencies are Django, Django REST Framework, Pillow and drf-extra-fields; SimpleJWT is not imported at module level — ViewSet and ModelViewSet resolve JWTTokenUserAuthentication lazily, per request, only when django.apps.apps.is_installed("zibanu.django.auth") — so the base package installs and runs with no optional package present at all.

It provides four areas:

  • zibanu.django.db — a Model/DatedModel base class pair adding multi-database routing and a recursive bulk-update helper, a Manager honoring that routing, a PhoneField model field, and a custom Oracle backend for reserved-word column names.
  • zibanu.django.rest_framework — a ViewSet/ModelViewSet pair with this project's own authentication/permission defaults and error envelope, a permission_required decorator, custom exceptions, a CurrentUserDefault field compatible with SimpleJWT's TokenUser, and a HybridImageField with size validation.
  • zibanu.django.lib — shared classes (BaseModelController, CodeGenerator, ExtendedJSONEncoder, ModelName, RegEx), the DialCodes enumeration, request- context middleware, and utility functions (mail, files, dates, model introspection, users, signal receivers).
  • zibanu.django.api — three ready-to-mount REST endpoints: country dial codes, Django's configured languages, and available timezones.

Installation

pip install ./installers/zibanu-django/

Add it to INSTALLED_APPS in your project's settings (it is required by every optional Zibanu Django package, so in practice it is always present):

INSTALLED_APPS = [
    # ...
    "zibanu.django",
]

zibanu.django does not ship any model of its own, so no migration is required just for installing it.

Quick Start

Build a model on the project's own base class instead of django.db.models.Model, to get multi-database routing and the recursive set() helper for free:

from zibanu.django.db.models import DatedModel, models


class Article(DatedModel):
    title = models.CharField(max_length=200)
    body = models.TextField()

Expose it through a ModelViewSet, whose actions are reached through your own @action-decorated POST endpoints (see API Reference):

from zibanu.django.rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action


class ArticleViewSet(ModelViewSet):
    model = Article

    @action(detail=False, methods=["post"])
    def list_articles(self, request, *args, **kwargs):
        return self._list(request, *args, **kwargs)

Features

  • Multi-database routing (use_db) built into every model without extra wiring.
  • Recursive bulk-attribute assignment (Model.set()) that also creates, updates or deletes nested one-to-one/one-to-many child objects in one atomic transaction.
  • A ModelViewSet contract that standardizes the error envelope, returns 204 (not 200 with an empty body) for an empty list, and centralizes exception-to- response translation.
  • A SimpleJWT-aware CurrentUserDefault and get_user() helper, so serializer defaults and permission checks work whether request.user is a real model instance or a lightweight TokenUser.
  • FileUtils, backed by Django's storage API, for reading/saving/inspecting files (MIME type, checksum, size) with optional lazy feature loading.
  • Email, built on EmailMultiAlternatives, rendering HTML/text templates and reporting partial-delivery SMTP failures instead of treating them as success.
  • Three ready-made REST endpoints: dial codes, languages, and timezones.
  • A custom Oracle backend that lets a model field be named after a reserved word.
  • The after_ready-deferred signal-wiring pattern (ZbAppConfig), shared by every optional package's own AppConfig, so model/DB access during signal connection never races Django's app-loading sequence.
  • A MinimumLevelRequired(max_level=N) permission, granting access by privilege level (get_user_level(), lower integer = higher privilege) with a superuser bypass and an explicit 403 (code="insufficient_level") instead of a plain False when the level can't be resolved or falls short.
  • No optional-package import at module load time anywhere in the base package: ViewSet/ModelViewSet resolve authentication_classes per request, so zibanu.django starts and serves requests with zero optional packages installed.

Usage

Basic Usage

Read the configured languages and timezones once zibanu.django.urls is mounted under your project (see Configuration):

POST /utils/language/list/
POST /utils/timezone/list/
POST /utils/dial_codes/list/

Use the shared ErrorMessages and APIException when writing your own service code, so errors carry this project's {"errors": [...], "code": ...} envelope:

from zibanu.django.rest_framework.exceptions import APIException
from zibanu.django.lib.utils import ErrorMessages

raise APIException(ErrorMessages.NOT_FOUND, code="does_not_exists", status_code=404)

Advanced Usage

Send a templated e-mail, resolving the current request automatically for template context and reporting SMTP partial-delivery failures:

from zibanu.django.lib.utils import Email

email = Email(subject="Welcome", to=["user@example.com"])
email.set_html_template("emails/welcome", context={"user_name": "Jane"})
email.send()

Inspect an uploaded file without loading it eagerly, then persist it through Django's storage API:

from zibanu.django.lib.utils import FileUtils

fu = FileUtils(
    file_dir="uploads", file_name="avatar.png",
    file_handler=uploaded_file, overwrite=True, eager_load=False,
)
fu.save()
print(fu.file_size, fu.mime_type)

Restrict a plain function-based or class-based view action with the project's permission decorator (note it must go through method_decorator on a class-based view — see the decorator's own docstring for why):

from django.utils.decorators import method_decorator
from zibanu.django.rest_framework.decorators import permission_required


class ReportViewSet(ViewSet):
    @method_decorator(permission_required(("is_staff", "reports.view_report")))
    def list(self, request, *args, **kwargs):
        ...

Gate a view by privilege level instead of a fixed permission string — grants access when the resolved level is at or above the threshold (level <= max_level), or when the user is a superuser:

from rest_framework.permissions import IsAuthenticated
from zibanu.django.rest_framework.permissions import MinimumLevelRequired


class InternalReportViewSet(ViewSet):
    permission_classes = [IsAuthenticated, MinimumLevelRequired(max_level=2)]

API Reference

zibanu.django.db.models

  • Model — abstract base adding the use_db routing attribute and set(attributes, force_update=True), a transactional bulk-assign that also walks and persists related one-to-one/one-to-many child objects.
  • DatedModel — extends Model, adding created_at/modified_at (auto_now_add/auto_now).
  • Manager — routes get_queryset() by the model's use_db, and adds get_by_pk(pk), raising Model.DoesNotExist when no row matches.
  • PhoneField — a JSONField storing {"dial_code": ..., "phone_number": ...}, validated against DialCodes.

zibanu.django.rest_framework.viewsets

  • ViewSet — rest_framework.viewsets.ViewSet defaulting to IsAuthenticated. authentication_classes is empty at class-body scope; get_authenticators() resolves it per request, falling back to [JWTTokenUserAuthentication] only when zibanu.django.auth is installed (and appending TokenAuthentication when settings.DEBUG is True) — no optional-package import happens at module level.
  • ModelViewSet — rest_framework.viewsets.ModelViewSet restricted to POST (http_method_names = ["post"]); the public list/retrieve/create/ update/destroy all raise NotImplementedException (405). A concrete subclass exposes its own @action-decorated POST endpoints calling the protected _list/_retrieve/_create/_update/_destroy/_destroy_list helpers, which translate ORM/validation errors into APIException. Same deferred authentication_classes resolution as ViewSet.

zibanu.django.rest_framework.decorators

  • permission_required(permissions, raise_exception=True) — validates Django permission strings (plus the special "is_staff" flag) against the resolved current user; raises PermissionDenied by default.

zibanu.django.rest_framework.exceptions

  • APIException — base exception; builds detail = {"errors": [...], "code": ...} and defaults message/code from the resolved HTTP status.
  • NotAuthenticated, PermissionDenied, NotImplementedException, MultipleLoginError — project-specific subclasses; ValidationError, NotFound, ParseError, etc. are re-exported from DRF unchanged.

zibanu.django.rest_framework.fields

  • CurrentUserDefault — resolves request.user through get_user(), so it works whether request.user is a real model instance or a SimpleJWT TokenUser.
  • HybridImageField — accepts base64 or multipart uploads, validates width/ height against image_width/image_height context keys, and can resize the base64 representation via image_size.

zibanu.django.rest_framework.permissions

  • IsOwnerOrReadOnly — allows safe methods to everyone; write methods only when obj.owner (resolved via get_user()) matches the requesting user.
  • MinimumLevelRequired(max_level) — instantiated per view (not a bare class in permission_classes); grants access to a superuser unconditionally, otherwise when get_user_level() resolves an int at or above the threshold (level <= max_level). Raises zibanu.django.rest_framework.exceptions.PermissionDenied with code="insufficient_level" — never a plain False — when the level can't be resolved or falls short. Implements __call__ returning self so DRF's get_permissions() (which otherwise expects to instantiate each entry itself) accepts an already-built instance.

zibanu.django.lib.classes

  • BaseModelController — wraps load/save access to a model instance and its related child objects, driven by a Meta inner class validated through ModelControllerMetaClass.
  • CodeGenerator — generates numeric/alphanumeric/secure codes and UUIDs for verification/short-lived-code use cases.
  • ExtendedJSONEncoder — a DjangoJSONEncoder covering FieldFile, plain dates, model instances, and any duck-typed geojson-carrying geometry object.
  • ModelName — a str subclass parsing an app_label.ModelName identifier.
  • RegEx — a regular-expression pattern builder plus character-class constants (LETTERS, DIGITS, PHONE_NUMBER, TAX_ID, MAC_ADDRESS, ...).

zibanu.django.lib.enums

  • DialCodes — a TextChoices enumeration of supported country dial codes, with dial_codes_list() and sorted(alphabetic=True) helpers.

zibanu.django.lib.utils

  • Email — EmailMultiAlternatives subclass rendering HTML/text templates and reporting partial-delivery SMTP failures via PartialSendError.
  • FileUtils — file operations backed by Django's storage API (metadata, checksum, save/read/delete), with optional lazy feature loading.
  • ErrorMessages — reusable, translatable error-message constants.
  • change_timezone(date, tz=None) / add_timezone(date, tz=None) — relabel or apply a timezone to a datetime value.
  • get_user(user) / get_user_object(user) — resolve a real Django user (the zibanu.django.auth proxy when installed) from a SimpleJWT TokenUser.
  • get_user_level(user) -> int | None — resolves user's privilege level, always reading it fresh from the database (never a cached JWT claim): tries get_user(user)'s own level attribute first, then falls back to a direct query against zibanu.django.auth.models.User when that package is installed and the attribute was absent (covers the TokenAuthentication debug path). Never raises — any resolution failure returns None.
  • get_model_list() / get_model_choices() / get_model_fields() — introspect installed Django apps for concrete models.
  • get_ip_address(request) / get_http_origin(request, md5=False) — HTTP request helpers.
  • get_receiver_id(**kwargs) / get_sender_name(sender) — signal-receiver helpers.
  • object_to_list(obj) / import_class(dotted_name) — flatten a nested object into human-readable strings; import a symbol by dotted path.

zibanu.django.api.services

  • DialCodesViewSet, LanguageViewSet, TimeZoneViewSet — POST endpoints listing dial codes, Django's configured languages, and available timezones, mounted by zibanu.django.urls.

zibanu.django.app_config

  • ZbAppConfig — base AppConfig deferring signal wiring to an after_ready background thread, so model/DB access never races Django's app-loading sequence. Subclassed by every optional package's own AppConfig, never instantiated directly (default = False).

Configuration

Wire the base package's pieces into your Django settings:

MIDDLEWARE = [
    # ...
    "zibanu.django.lib.middleware.RequestContextMiddleware",
]

REST_FRAMEWORK = {
    "EXCEPTION_HANDLER": "zibanu.django.rest_framework.utils.exception_handler",
    # ...
}

Mount the base package's own REST endpoints:

urlpatterns = [
    path("utils/", include("zibanu.django.urls")),
]

Relevant settings:

Setting Purpose
ZB_MAIL_DEFAULT_FROM Default From address for Email. Falls back to DEFAULT_FROM_EMAIL when unset (ZbDjango.ready() logs when it is).
ZB_MAIL_REPLY_TO Default Reply-To address for Email, when set.

Testing

python manage.py test zibanu.django.tests

This package's own tests live under zibanu/django/tests/, including the shared test infrastructure (test_base.py's BaseTestCase, fixtures, fixture_path()) that the optional packages' own test suites import from.

Requirements

  • Python 3.9+
  • Django >= 3.2
  • djangorestframework
  • djangorestframework-simplejwt
  • Pillow
  • drf-extra-fields >= 3.7

djangorestframework-simplejwt is pinned in the installer's own pyproject.toml, but the base package never imports it at module level — ViewSet/ModelViewSet import it lazily, per request, only once zibanu.django.auth is installed (see Features). A deployment running zibanu.django with no optional package can omit it in practice, even though the installer still declares it.

Also required at runtime by specific modules, though not declared in the package's own pyproject.toml: filetype (FileUtils's MIME-type detection) and timezone-utils (TimeZoneViewSet's timezone choices).

License

GPL-3.0-or-later. See the package's own installers/zibanu-django/LICENSE.

Changelog

All notable changes to zibanu.django (the base package) are documented here, one section per principal version (x.y.z). Pre-releases of the same version (x.y.z-dev.N, -alpha.N, -beta.N) fold into that version's section rather than getting a heading of their own.

Scope: this file covers zibanu/django/api, db, lib, rest_framework, locale, static, and the package root (__init__.py, apps.py, urls.py) — i.e. the code that ships as the base package. Changes confined to an optional package's own directory (auth/, repository/, logging/, template/, gis/, mqtt/, state_machine/) are out of scope and live in that package's own CHANGELOG.md.

[3.0.0] — unreleased

Added

  • zibanu.django.app_config.ZbAppConfig — shared AppConfig base class centralizing the ready()/after_ready() deferred-signal-wiring pattern (event-wait with a bounded polling fallback); adopted by zibanu.django.repository and available for the other optional packages to share the same guard instead of each reimplementing it.
  • zibanu.django.lib.middleware.RequestContextMiddleware — publishes the active request on a contextvars.ContextVar so get_request_from_stack() resolves it in O(1); the original call-stack walk remains as the fallback when the middleware isn't installed in a consuming project.
  • Email (lib/utils/mail.py) now tolerates a partial SMTP send: a new PartialSendError exception and dedicated logging/signal path report exactly which recipients were refused, distinct from a full send failure.
  • get_user_level() (lib/utils/user_utils.py) — a new public helper resolving a user's privilege level: it reads level duck-typed off the translated user and, when that is absent, falls back to a direct database lookup by primary key against the zibanu.django.auth User model, only if that optional package is installed. It never raises and never caches — any resolution failure (missing row, zibanu.django.auth not installed, a database error) is reported as None rather than an exception or a stale value. (ZBDJ-759)
  • MinimumLevelRequired (rest_framework/permissions/) — a new view-level permission class, instantiated per view with a max_level threshold (permission_classes = [IsAuthenticated, MinimumLevelRequired(max_level=N)]). It grants access through get_user_level() (with an is_superuser bypass that never calls it) and denies explicitly with PermissionDenied(code="insufficient_level") rather than a plain False; it also implements __call__ (returning itself) so DRF's get_permissions() accepts the pre-instantiated form shown above without raising TypeError before has_permission is ever reached. (ZBDJ-768, ZBDJ-772)

Changed

  • Breaking: PermissionDenied now answers HTTP 403 (Forbidden) instead of 401 (Unauthorized), matching the docstrings and OpenAPI spec that already documented 403 for permission_required rejections.
  • APIException's default error code is now derived from the response's status code through an internal status→code mapping when no explicit code is supplied, instead of a single fixed default — an uncontrolled 500 now answers a consistent code regardless of call site.
  • CurrentUserDefault.get_default() now resolves the current user from request.user (it was previously handing the whole request object to the user-resolution helper) and raises instead of silently returning None when the serializer context carries no request.
  • IsOwnerOrReadOnly now compares the object owner's primary key against the resolved local user instead of request.user directly — under this package's JWT authentication, request.user is a SimpleJWT TokenUser, which never compares equal to a real user model instance.
  • ViewSet/ModelViewSet build their authenticator list per request, in get_authenticators(), instead of freezing it as a class attribute evaluated once when the module is first imported — settings.DEBUG is now honored live instead of at process start.
  • ModelViewSet.create/.update now call the serializer's save() instead of calling create()/update() directly, so any pre/post-save hook defined on the serializer actually runs.
  • ModelViewSet.get_queryset raises ImproperlyConfigured when a subclass doesn't define model, instead of failing later with an unrelated AttributeError.
  • Model.save()/.delete() now honor the model's use_db routing attribute; save() no longer forwards a stray positional *args into Django's own save().
  • Model.set(): one-to-many child reconciliation now deletes rows absent from the payload with a single exclude(...).delete() instead of one .delete() per row; child-identity comparisons use .pk instead of .id, so it now works for a child model without Django's default id primary key; calling set() with no attributes at all now raises immediately instead of failing further down with an unrelated error.
  • Manager.get_by_pk() checks existence with .exists() instead of materializing the full matching queryset.
  • PhoneField no longer caches a row's dial_code/phone_number on the shared field instance (a Field is one object shared across every model instance, request and thread); validate() now validates a merged copy instead of mutating the caller's dictionary; the field gained deconstruct() so a non-default dial_code round-trips through migrations.
  • CodeGenerator.get_alpha_numeric_code()/get_secure_code() now guarantee one character from each declared character class via a fixed-alphabet draw-and-shuffle, replacing an implementation whose effective alphabet grew from its own partially-generated output as it ran — so the advertised guarantee didn't actually hold.
  • ExtendedJSONEncoder: a nested model value is now returned as a dict for the outer encoder to recurse into, instead of being pre-serialized to a JSON string that ended up double-encoded inside the payload; any FieldFile (not just FileField/ ImageFieldFile) is now recognized; a datetime formatting bug that used %M (minutes) instead of %m (month) is corrected.
  • object_to_list() no longer raises TypeError on a numeric error-detail value (it tested emptiness with len()), and no longer silently drops a non-string, non-proxy scalar value — a redundant isinstance(value, object) check had made an entire branch permanently unreachable. get_sender_name() (lib/utils/receivers_utils.py) carried the identical always-true isinstance(value, object) mistake, also fixed.
  • ModelControllerMetaClass raises ImproperlyConfigured instead of relying on assert (stripped when Python runs with -O) for its configuration checks, requires a Meta class to be defined at all, and now copies the inherited Meta before mutating it, so a default it sets for one controller no longer leaks onto sibling subclasses sharing the same base Meta.
  • BaseModelController now actually assigns the related-object list it builds while loading kwargs onto the instance — previously computed and discarded, making that whole walk dead work; it copies the incoming kwargs dict instead of mutating the caller's own, and narrows its blanket except Exception to ValidationError/ ValueError, so a genuine programming error now propagates instead of being reported to the client as invalid input.
  • HybridImageField: builds its guessed MIME type from file.name instead of file.path, which raises on any non-filesystem storage backend; width/height limits supplied via serializer context are now honored independently of each other instead of requiring both keys present at once to apply either.
  • get_user() (lib/utils/user_utils.py) now imports rest_framework_simplejwt lazily inside the function body, so importing this helper no longer requires SimpleJWT to be installed when the caller never needed the TokenUser translation.
  • MultipleLoginError now carries a default detail message ("A session is already open for this user.") instead of None.
  • DialCodesViewSet.list no longer inverts its sorted request flag; LanguageViewSet.list returns a {code, name} list built from settings.LANGUAGES instead of an always-empty placeholder; TimeZoneViewSet.list's "no results" check now tests the actual timezone list instead of the always-non-empty wrapper dict around it.
  • ZbDjango.ready() no longer writes computed defaults for ZB_MAIL_DEFAULT_FROM/ ZB_MAIL_REPLY_TO back onto the settings object; it now only logs when ZB_MAIL_DEFAULT_FROM is left unconfigured.
  • Email's from_email/reply_to settings are now read with getattr(...) instead of direct attribute access, so sending no longer raises when ZB_MAIL_DEFAULT_FROM/ ZB_MAIL_REPLY_TO aren't configured; when no ZB_MAIL_REPLY_TO is set, no Reply-To header is built at all (previously it went out as the literal string "None").
  • urls.py: route names are now dial_codes_list/timezone_list/language_list (were free-text, human-readable strings), and the module declares app_name = "zb_django" for namespaced URL reversal.
  • ModelViewSet/ViewSet.authentication_classes now default to an empty list instead of hardcoding [JWTTokenUserAuthentication]; the JWT authenticator is resolved lazily by get_authenticators(), which imports rest_framework_simplejwt only when zibanu.django.auth is installed. lib/__init__.py now imports .enums before .classes, breaking an import cycle (.classes → zibanu.django.db.models.Model → PhoneField → back to .enums.DialCodes) that only surfaced once nothing else had already forced this package to fully initialize first. Net effect: the base package now starts correctly with no optional package installed, where it previously required zibanu.django.auth/rest_framework_simplejwt to be present at import time regardless of whether a given deployment used JWT auth at all. (ZBDJ-752)

Fixed

  • get_request_from_stack() (used by audit logging on every model save/delete) now walks the call stack with sys._getframe() and stops at the first get_response frame, instead of building a full inspect.stack() list — which resolves source location for every frame — on every call; it also reads the request via f_locals.get() so a frame without that local no longer raises KeyError. It now also checks the RequestContextMiddleware context var first (see Added, above).
  • ExtendedJSONEncoder.default() now serializes any value exposing a geojson attribute (duck-typed) instead of checking isinstance(obj, GEOSGeometry), so the base package keeps no import-time dependency on django.contrib.gis (GDAL/GEOS) — installations without zibanu.django.gis are unaffected either way.
  • FileUtils.file_path now goes through FileSystemStorage.path() (which composes via safe_join), so a traversing file name raises SuspiciousFileOperation instead of resolving to a path outside the storage root.
  • FileUtils.delete() now returns False, not True, when there was nothing to delete — the previous return value contradicted its own documented contract.
  • FileUtils.save() adopts the name the storage backend actually wrote (which can differ from the requested name once the backend de-duplicates it), so a later read/delete targets the file that was actually written.
  • FileUtils's extension/MIME detection no longer rewrites a file name to a bare trailing dot when detection produces no extension, and no longer raises on a handler with no name attribute.
  • add_timezone() (lib/utils/date_time.py) now calls django.utils.timezone.make_aware() instead of a tz.make_aware() method that ZoneInfo doesn't provide.
  • ModelName.app_label/.model_name now raise a clear ValueError for a malformed identifier instead of an IndexError from the unguarded split.
  • RegEx's custom-alphabet pattern builder no longer contains an always-taken dead branch that, in one code path, left a custom pattern unbuilt.

Note: in this range, Email (lib/utils/mail.py) went through an intermediate revision that resolved the active request lazily at render time instead of capturing it in __init__ (closing a locmem-backend pickling failure on the stored request). A later, larger rewrite of the same file — adding the partial-send handling described above — reintroduced the eager self.__request = get_request_from_stack() capture in __init__, which is the form this range ends on.

Note: a 2.1.3-dev.0 bump (no functional change — an __init__.py docstring tweak) briefly preceded the jump straight to 3.0.0-dev.0; 2.1.3 was never a real development line.

[2.1.2] — 2026-08-13

Fixed

  • Fixed a temporary-file leak in FileUtils.__load_features: the file_content code path created NamedTemporaryFile(delete=False), leaving orphaned files under /tmp. It now uses delete=True (every consumer reads from the already-open stream, never the path), flushes the file after writing, and computes file_size from len(file_content) — also fixing a 0-byte size that was previously reported from the unflushed buffer.

Changed

  • __build__ is now tracked as its own field in django/__init__.py, independent of __version__.

[2.1.1] — 2026-05-15

Added

  • The zb_context_data context processor gained full_uri, full_static_uri, full_media_uri, site_name, site_logo, site_favicon, site_description and site_version; Email (lib/utils/mail.py) now reads the current request off the stack to build them. (The new_user, send_code and request_password mail templates were updated accordingly — see the auth package's own changelog.)

Changed

  • Removed the force_extension flag from FileUtils.

[2.1.0] — 2026-03-07

Added

  • ModelViewSet can now accept a pre-built queryset via **kwargs, bypassing its own get_queryset() — added to let services (e.g. a role-aware menu listing) run a custom filter instead of the default one.
  • lib.utils.model_utils.get_model_choices() — a companion to get_model_list() that returns a model-name choices tuple, with an optional empty entry, for admin/form selects.
  • RegEx (lib/classes/regex.py) extended with more building blocks for composing regular expressions.
  • A shared Django-admin static asset: a "tabular inline scroll" widget (static/zb_django/{css,js}/zb_tabular_inline_scroll.{css,js}) for inline admin forms.

Fixed

  • Corrected the 404 error code for a missing object from the misspelled doest_not_exists to does_not_exists.
  • FileUtils received extensive stabilization over this cycle — file-content handling, extension detection (full_path_filename, and full_file_name renamed to full_filename), and metadata handling — across roughly fifteen follow-up commits; net effect is a substantially more robust file_utils.py, as consumed by the repository package's admin view and controller.

Note: during this cycle __init__.py briefly wandered to 2.0.7-dev.* before returning to 2.1.0-dev.* — an artifact of the alpha/beta/develop branches being merged back and forth. No 2.0.7 was ever released; the base-package changes committed under that stray version tag are folded into this 2.1.0 section, since they shipped as part of the same continuous development.

[2.0.6] — 2025-08-23

Fixed

  • Fixed an error relocating a file in FileUtils. (ZBDJ-275)

[2.0.5] — 2025-08-19

Fixed

  • Fixed file deletion in FileUtils. (ZBDJ-266)
  • Fixed a bug that allowed reassigning a PDF to an incompatible category. (ZBDJ-257)

[2.0.4] — 2025-06-19

Added

  • FileUtils gained a "create version file" capability. (ZBDJ-251)

[2.0.3] — 2025-06-16

Fixed

  • Corrected the authentication_classes definition in model_viewset.py and viewset.py.
  • Fixed a bug that generated an incorrect file URL when built from the file's creation date.

[2.0.2] — 2025-04-05

Added

  • FileUtils can now extract a file's MIME type and, for supported categories, embedded metadata (Office: XLSX/DOCX/PPTX; OpenDocument: ODT/ODS/ODP; images: PNG/JPG/TIFF; PDF) via the new FileMetadata class. (ZBDJ-238)

[2.0.1] — 2025-04-01

Fixed

  • Fixed file-type validation in FileUtils that raised an error while editing a file's admin record without attaching a new file. (ZBDJ-229/230)
  • Fixed the file-hash generation error in file_utils.py.

[2.0.0] — 2025-03-14

Changed

  • Model.set() (base model mixin) now handles a nested one-to-one child that already has an in-memory instance but no id: it assigns the parent relation and updates it in place instead of only handling the "no instance yet" case. save() also stopped forwarding a stray *args to Django's own save().

Fixed

  • ExtendedJSONEncoder fixed to correctly serialize nested models.
  • ModelViewSet._update fixed to pass context to the serializer, which was previously omitted and broke context-dependent validation on update.

Note: a 1.5.2-dev.1 hotfix (cut 2025-02-06, the day 1.5.1 shipped, containing the FileClass/Model.set() cleanup above) was superseded by this 2.0.0 line before 1.5.2 was ever published as a stable version — 1.5.2 never shipped; its one commit's changes are folded into this section instead.

[1.5.1] — 2025-02-06

Added

  • New exceptions ProtectedError and RestrictedError (with Spanish translations), mirroring Django's own integrity-error classes; added handling for UniqueConstraint violations and for NotAuthenticated.

Changed

  • Password / secure-code validation (lib/classes/code_generator.py) reworked over several follow-up commits: punctuation handling corrected, minimum-length enforcement wired to app/settings configuration, and validation kept consistent between "secure" and "non-secure" password modes. (ZBDJ-154, ZBDJ-156)
  • get_request_from_stack() now returns None instead of raising when no request is on the stack; the logging package's receivers were updated to match.
  • The MAC_ADDRESS regex was tightened to accept only : as a byte separator.

Fixed

  • Corrected the .po/.mo translation build, which was silently failing to regenerate django.mo.
  • Fixed a recurring bug (three follow-up attempts before it stuck) where a dictionary-valued error message wasn't translated when converting an object to a list.

[1.5.0] — 2024-11-04

Added

  • BaseModelController / ModelController metaclass (lib/classes/base_model_controller.py, lib/metaclasses/model_controller.py) — a reusable base for building model controllers.
  • Model.set() extended to create nested one-to-one children, to save newly created rows for nested one-to-many children, and to support nested ("Creación Anidada") and mass-delete operations. (ZBDJ-146, ZBDJ-147, ZBDJ-151, ZBDJ-152)
  • Model.set() gained a force_update keyword (default True, preserving prior behavior) so a caller can let it insert a new row instead of always forcing an update.

Changed

  • import_class() (lib/utils/generic_utils.py) is no longer limited to locating serializers — it can import any class by dotted path.

Deprecated

  • zibanu.django.utils — its contents moved to zibanu.django.lib.utils. The old import path is kept only as a backward-compatible shim; per the commit that made the move, it "will disappear in a future version."

Fixed

  • model_viewset.py now forwards message_dict to APIException on update failures, instead of losing field-level validation detail.
  • Model.set() fixed to no longer error when a related child object was created in an inconsistent state, and to support one-to-one parent-table lookups.
  • Several fixes to the RegEx class powering pattern validation.

[1.4.2] — 2024-08-21

Added

  • ModelSerializer/ModelViewSet: the create-vs-update context is now surfaced to the serializer (a create key in context, and an _adding property derived from it), so a serializer can tell which operation is in progress.

[1.4.1] — 2024-08-09

Added

  • RegEx (lib/classes/regex.py) — a new class assembling common regular-expression patterns. (ZBDJ-141)

Changed

  • Secure-code/password generation (lib/classes/code_generator.py) now limits which symbols are used and validates generated codes/passwords against that set. (ZBDJ-140)

Fixed

  • Exceptions in model_viewset.py now surface via the detail parameter instead of error, aligning with the intended error-capture configuration. (ZBDJ-138)
  • Two follow-up fixes to RegEx (an enum value and a validation bug) after its initial introduction.

[1.4.0] — 2024-05-10

This is the first version tracked by the package's own __version__ file, added on 2024-05-03. The git tags 1.1.0 (2023-03-17) and 1.1.1 (2023-03-31), present earlier in this repository, predate that tracking and don't correspond to any recorded __version__ value — see their own sections below. Between 1.1.1 and this release (2023-04 through 2024-05, about 13 months) the base package's own surface (api/db/lib/rest_framework) has no version marker of its own; that work is presented here by necessity, anchored on the next real marker this history provides (the first __version__ commit).

Added

  • permission_required decorator (rest_framework/decorators.py) — validates a permission without raising, returning a boolean/response instead.
  • zibanu.django.api: public endpoints listing available timezones and languages (api/services/timezone.py, language.py), registered in urls.py.
  • HybridImageField (rest_framework/fields/hybrid_image.py) — image upload validation with size/format checks, introduced and iterated on repeatedly.
  • PhoneField (db/models/fields/phone_field.py) plus a dial-code enum and the api/services/dial_codes.py endpoint, for phone-number fields with country dial-code lookup/validation.
  • PermissionDenied exception (mirroring DRF's own hierarchy) and a MultipleLoginError exception.
  • ModelViewSet.perform_authentication override translating SimpleJWT's InvalidToken into the package's own APIException.

Changed

  • Consolidated the APIViewSet/ModelViewSet implementation: unified the detail payload shape and status codes across api_exception.py, simplified APIViewSet, and moved exception_handler.py into utils.py.
  • The custom Oracle database backend (db/backends/oracle) was hardened for reserved-word field handling.

Fixed

  • Several regressions in model_viewset.py: order_by handling (twice), partial (PATCH) updates rejecting valid data, and to_representation returning the wrong parameter shape.
  • Multiple translation/localization fixes across the .po/.mo catalogs.

[1.1.1] — 2023-03-31

Added

  • CurrentUserDefault field (rest_framework/fields) — a "current user" default compatible with token-based authentication.

Changed

  • ModelViewSet now logs through the package logger.

[1.1.0] — 2023-03-17

Added

  • Initial scaffold of the base package: Model/DatedModel/Manager (db/models), the custom Oracle database backend for reserved-word field names (db/backends/oracle), and the initial rest_framework/apps.py skeleton.
  • Email utility (utils/mail.py) and mail-send audit-logging signals.

Fixed

  • Corrected the sender class name and completed the Spanish translation catalog.

Metadata

Release files for zibanu-django 3.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zibanu-django 3.0.0
File Size Uploaded
zibanu_django-3.0.0.tar.gz 117.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for zibanu-django 3.0.0
File Interpreter ABI Platform
zibanu_django-3.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 240.1 kB

Release files / zibanu_django-3.0.0.tar.gz

Download URL zibanu_django-3.0.0.tar.gz
Size 117.6 kB
Tags Source
SHA-256 checksum
How to use checksums
d2ec45c2e4a499016eafc984146cfaf2edade65ac75b41a66758017ed6c99e5e
BLAKE2b-256 checksum
How to use checksums
96cf7d9ec03aa9044594f4d5b496b1c1c0685c61479b3720887d211a2fa7ddc9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release files / zibanu_django-3.0.0-py3-none-any.whl

Download URL zibanu_django-3.0.0-py3-none-any.whl
Size 122.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ca039f18e93f9f5544035c5ed94582766de93e0a59c6415cc5e0671bedd2d16b
BLAKE2b-256 checksum
How to use checksums
c3e4b64cbba15d67dcd279c1db5527904d3a0958ca9dad006057210b2f21df14
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release history Release notifications | RSS feed

This release

3.0.0 This release

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.6

2 release files

2.0.5

2 release files

2.0.4

2 release files

2.0.3

1 release file

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.2

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.5

2 release files

1.3.4

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page