Utility Modules¶
This page lists utility modules that live directly in the
ferrosoft package. They support non-functional requirements such
as configuration. For most purposes, prefer modules from
ferrobase.apps, where new modules should also be created.
Ferrosoft.config module¶
YAML-based Django settings loader with environment merging and variable substitution.
This module is the core of Ferrosoft’s configuration system. Rather than keeping settings in a Python file, settings are stored in one or more YAML files whose structure mirrors Django’s settings namespace. The loading pipeline has three stages:
Merge files — one or more YAML files are read and merged with the
|operator (right-hand file wins on key collision).Select environment — the merged YAML is expected to have a
"default"top-level key and one key per environment (e.g."development","production"). The active environment’s block is merged on top of"default".Substitute variables — string values that start with
"@ "are treated asstr.format()templates and rendered with two namespaces:env—os.environ, so"@ {env[SECRET_KEY]}"reads theSECRET_KEYenvironment variable.settings— the fully merged (but not yet substituted) settings dict, so"@ {settings[BASE_DIR]}/media"derives a path from another setting.
If a referenced key is missing, a
MissingOptionsentinel is stored instead of raising immediately. The error is deferred until the setting value is actually used (e.g. converted tostr), so that optional settings that are never accessed do not cause startup failures.
Environment variables¶
FERROSOFT_ENVSelects the environment block merged on top of
default. Defaults to"development".FERROSOFT_CONFIGColon-separated list of YAML file paths to load and merge. Defaults to
"settings.yml".
Typical usage¶
In a Django settings module:
from ferrosoft.config import load_django
load_django(globals())
This writes every resolved key from the YAML configuration directly into the calling module’s global namespace, making them available to Django as settings.
- ferrosoft.config.CONFIG_FILE_DEFAULT = 'settings.yml'¶
Default config file path used when
CONFIG_FILE_ENVis not set.
- ferrosoft.config.CONFIG_FILE_ENV = 'FERROSOFT_CONFIG'¶
Name of the environment variable that lists YAML config file paths.
- exception ferrosoft.config.ConfigError[source]¶
Bases:
RuntimeErrorRaised for unrecoverable configuration problems.
Examples include a YAML file that does not contain a mapping, or a required Django setting that is absent from the resolved configuration.
- ferrosoft.config.DEFAULT_ENV_NAME = 'default'¶
Top-level YAML key whose block is used as the base for all environments.
- ferrosoft.config.ENV_DEFAULT = 'development'¶
Default value for
ENV_ENVwhen the variable is not set.
- ferrosoft.config.ENV_ENV = 'FERROSOFT_ENV'¶
Name of the environment variable that selects the active config environment.
- class ferrosoft.config.MissingOption(option_keys, originator)[source]¶
Bases:
objectDeferred error sentinel for unresolvable variable references.
When a
"@ {env[VAR]}"or"@ {settings[KEY]}"substitution fails because the referenced name is missing, aMissingOptioninstance is stored in its place rather than raising immediately. TheConfigErroris only raised when the sentinel’s__str__method is called — i.e. when the setting value is actually used as a string.This design allows the full configuration to be loaded without aborting on optional or environment-specific settings that happen not to be set in the current environment.
- Parameters:
option_keys (
Iterable[str]) – The sequence of YAML key names that form the path to the setting that could not be resolved (used in the error message).originator (
Exception) – TheKeyErrorraised duringstr.format()that triggered the deferred error.
- ferrosoft.config.load_django(settings_module)[source]¶
Populate a Django settings module from the resolved YAML configuration.
Reads all YAML configuration files, selects and merges the active environment block, performs variable substitution, and writes every resulting key directly into
settings_module.Intended to be called from a Django settings file with
globals()as the argument:from ferrosoft.config import load_django load_django(globals())
- ferrosoft.config.merge_config_environment(unmerged_config)[source]¶
Select and merge the active environment block from the raw config dict.
Looks for a
"default"top-level key and merges the block identified by theFERROSOFT_ENVenvironment variable on top of it (right-wins). IfFERROSOFT_ENVis not set, falls back to"development".- Parameters:
unmerged_config (
dict) – The raw configuration dictionary as returned byread_config_files_merged(), containing one key per environment plus"default".- Return type:
- Returns:
A flat settings dictionary with environment-specific overrides applied. If
"default"is absent the entireunmerged_configis returned as-is with a warning. If the active environment key is absent, the"default"block is returned with a warning.
- ferrosoft.config.read_config_file(file_name)[source]¶
Read and parse a single YAML configuration file.
- Parameters:
file_name (
str) – Path to the YAML file to read.- Return type:
- Returns:
The parsed YAML content as a Python dictionary, or an empty dict if the file does not exist.
- Raises:
ConfigError – If the file exists but its content is not a YAML mapping.
- ferrosoft.config.read_config_files()[source]¶
Execute the full configuration pipeline and return resolved settings.
Combines
read_config_files_merged(),merge_config_environment(), andsubstitute()into a single call.- Return type:
- Returns:
A flat dictionary of Django settings with all variable references resolved. Values that could not be resolved are represented by
MissingOptionsentinels.
- ferrosoft.config.read_config_files_merged()[source]¶
Read and merge all YAML configuration files listed in
FERROSOFT_CONFIG.Splits the
FERROSOFT_CONFIGenvironment variable on":"and loads each path in order. Files are merged with the|operator so that keys from later files override keys from earlier files. The resulting dict still contains the top-level environment keys ("default","development", etc.) — environment selection happens inmerge_config_environment().- Return type:
- Returns:
A merged dictionary of all YAML files, keyed by environment name at the top level.
- Raises:
ConfigError – If any loaded file’s top-level value is not a mapping.
- ferrosoft.config.require_settings(*keys)[source]¶
Fetch named settings from the active Django settings module.
Validates that each requested key is present and returns all values in a single dictionary. Intended for use by services that need a small, named subset of Django settings without importing the full settings module.
- Parameters:
*keys (
str) – One or more Django setting names to retrieve.- Return type:
- Returns:
A dictionary mapping each requested key to its current value.
- Raises:
ConfigError – If any of the requested keys is absent from Django settings.
Example
from ferrosoft.config import require_settings cfg = require_settings("VICTORIA_BASE_URL", "MEDIA_ROOT") base_url = cfg["VICTORIA_BASE_URL"]
- ferrosoft.config.substitute(settings, root_settings, key_trail=None)[source]¶
Recursively resolve variable references in a settings dictionary.
Iterates over every key in
settingsand callssubstitute_value()on each value. Nested dicts are recursed; thekey_trailis extended at each level so that error messages can report the full dotted path to the problematic setting.- Parameters:
settings (
dict) – The dictionary of settings to process.root_settings (
dict) – The top-level (unsubstituted) settings dictionary, passed down unchanged so that{settings[KEY]}references can look up any top-level key.key_trail (
list[str] |None) – Accumulated list of key names representing the path from the root to the currentsettingsdict. Used inMissingOptionerror messages. PassNone(or omit) at the top-level call.
- Return type:
- Returns:
A new dictionary with the same keys as
settingsand all"@ ..."string values replaced by their resolved counterparts.
- ferrosoft.config.substitute_value(value, root_settings, key_trail)[source]¶
Resolve a single configuration value, handling the substitution DSL.
String values that begin with
"@ "are treated asstr.format()templates rendered with two keyword namespaces:env—os.environsettings— the top-levelroot_settingsdictionary
Examples of valid substitution strings:
"@ {env[DATABASE_URL]}" "@ {settings[BASE_DIR]}/media" "@ {env[SECRET_KEY]}"
Dictionaries are recursed via
substitute(). List elements are substituted individually. All other types (int,bool,None, etc.) are returned unchanged.If a
{env[...]}or{settings[...]}reference cannot be resolved because the key is absent, aMissingOptionsentinel is returned instead of raising immediately.- Parameters:
value – The raw configuration value to process.
root_settings (
dict) – The top-level settings dictionary used to resolve{settings[KEY]}references.key_trail (
list[str]) – Path of keys from the root to the current value, used to construct informativeMissingOptionerror messages.
- Returns:
The resolved value, a
MissingOptionsentinel, or the original value unchanged if no substitution applies.
ferrosoft.decimal module¶
ferrosoft.importutils module¶
Restricted dynamic import utility for loading objects from first-party app modules.
This module provides a single entry-point, import_object(), for
dynamically importing Python objects (functions, classes, etc.) by their
fully-qualified name. It enforces a namespace restriction so that only objects
whose module path starts with "ferrosoft.apps." can be loaded, preventing
an attacker-controlled or misconfigured string from importing arbitrary Python
code.
Typical use case: the task queue and the global updater system store
callable references as dotted strings in configuration or the database.
import_object() resolves those strings at runtime without exposing a
general-purpose code-execution surface.
- exception ferrosoft.importutils.Error[source]¶
Bases:
ExceptionBase exception class for import utility errors.
- exception ferrosoft.importutils.ForbiddenModule[source]¶
Bases:
ErrorRaised when the requested module path fails the namespace check.
This is a security guard: only modules whose fully-qualified name starts with
_REQUIRED_MODULE("ferrosoft.apps.") may be imported dynamically. Any attempt to import from the standard library, third-party packages, or other internal namespaces raises this exception.- Parameters:
qualified_name – The fully-qualified object name that was rejected.
- ferrosoft.importutils.import_object(qualified_name)[source]¶
Import and return a named object from a first-party application module.
Splits
qualified_nameon the last"."to obtain the module path and the attribute name, imports the module viaimportlib.import_module(), and returns the named attribute.The namespace guard enforces that
qualified_namestarts with"ferrosoft.apps."before any import is attempted, limiting the attack surface of dynamic import to first-party application code only.- Parameters:
qualified_name (
str) – Fully-qualified name of the object to import, e.g."ferrosoft.apps.emiflow.services.catalog.build_catalog". Must start with"ferrosoft.apps.".- Returns:
The imported object, or
Noneif the module does not expose an attribute with the given name.- Raises:
ForbiddenModule – If
qualified_namedoes not start with"ferrosoft.apps.".
Example
from ferrosoft.importutils import import_object updater_fn = import_object( "ferrosoft.apps.ferrobase.services.update.run_global_update" ) if updater_fn is not None: updater_fn()
ferrosoft.strparse module¶
Tolerant string-to-type parsers for processing external and user-supplied data.
All parsers in this module are intentionally lenient:
The
parse_*_noexceptfunctions returnNoneinstead of raising on invalid input, making them safe to use with data from CSV files, query parameters, or any other source where the value may be absent or malformed.parse_boolean()treats every unrecognised string asTrue, mirroring the JavaScript truthiness model and the convention used by HTML checkboxes (where a submitted field value always means “checked”).
The _UNTRUTHY_VALUES list deliberately covers representations from
multiple languages and data formats (Python’s "none", JavaScript’s
"null" and "undefined", SQL’s implicit empty string, numeric zeros)
so that the same parser can be used across heterogeneous data sources.
- ferrosoft.strparse.get_untruthy_values()[source]¶
Return the list of string values that
parse_boolean()treats asFalse.Callers can use this to display or validate the set of accepted falsy representations without hard-coding the list themselves.
- Return type:
- Returns:
A list of lowercase strings considered untruthy by
parse_boolean().
- ferrosoft.strparse.parse_boolean(value)[source]¶
Parse a string as a boolean, treating all unrecognised values as
True.Converts
valueto lowercase and checks it against_UNTRUTHY_VALUES. Any string not in that list — including"yes","on","1","true", or any arbitrary non-empty string — is consideredTrue.This permissive convention is intentional: it matches the behaviour of HTML form checkboxes (where the presence of any submitted value means “enabled”) and JavaScript’s general truthiness rules.
- Parameters:
value (
str) – The raw string to interpret as a boolean.- Return type:
- Returns:
Falseifvalue.lower()is in_UNTRUTHY_VALUES,Trueotherwise.
Example
parse_boolean("true") # True parse_boolean("yes") # True parse_boolean("1") # True parse_boolean("false") # False parse_boolean("0") # False parse_boolean("none") # False parse_boolean("") # False
- ferrosoft.strparse.parse_decimal_noexcept(value)[source]¶
Parse a string as a
Decimal, returningNoneon failure.
- ferrosoft.strparse.parse_int_noexcept(value)[source]¶
Parse a string as an integer, returning
Noneon failure or absent input.
ferrosoft.addresses module¶
Warning
Prefer ferrosoft.apps.ferromaps package over this module! It is only kept around for compatibility, though these days it should be dead code.
Address data types and geocoding/routing services.
Deprecated since version This: module is superseded by
ferrosoft.apps.ferromaps.services.addresses, which should be used
for all new code. The replacement offers several improvements:
Addressis a mutable@dataclassinstead of an immutableNamedTuple.Coordinates and distances use
Decimalinstead offloat, avoiding floating-point rounding in emissions calculations.Geocoding and routing are split into separate abstract base classes (
GeocoderandDistanceRouter) that can be composed viaSwitchingAddressRouter.Optional Django-cache layer via
CachingAddressRouter.Database-backed fuzzy geocoding via
FuzzyGeocoder.The active router implementation is resolved dynamically from the
FERROBASE_ADDRESS_ROUTER_CLASSsetting instead of being hardwired to Geoapify.AddressRouterGeoapifyfalls back to city-level geocoding when a full-address lookup returns no results, and raises typed exceptions (CoordinatesNotFound,RoutingException) instead of bareRuntimeError.
- class ferrosoft.addresses.Address(first_name: str, last_name: str, salutation: str, company: str, additional_lines: str, street: str, house_no: str, city: str, postal_code: str, country: str)[source]¶
Bases:
NamedTupleStructured postal address as an immutable value object.
Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.Addressinstead. The replacement is a mutable@dataclassand is accepted directly by the new service’sgeocodemethod.All fields default to an empty string when constructed via
from_dict().- format_lines(join=None)[source]¶
Format the structured address into display lines or a single string.
Omits empty fields. The output order is: salutation / first name / last name, company, additional lines (split on
"\n"), street + house number, postal code + city, country.
- classmethod from_dict(data)[source]¶
Construct an
Addressfrom a dictionary, defaulting missing keys to"".
- geocode()[source]¶
Geocode this address using the default
AddressRouterService.Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.Address.geocode()on the replacementAddressclass instead.- Return type:
- Returns:
The
Locationresolved byAddressRouterGeoapify.
- class ferrosoft.addresses.AddressRouterFake(distances)[source]¶
Bases:
AddressRouterServiceIn-memory stub implementation for use in tests.
Geocoding always returns a fixed dummy location. Distances are consumed from an iterator supplied at construction time, returning
Noneonce the iterator is exhausted.Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.AddressRouterFakeinstead.- Parameters:
distances (
List[Tuple[int,str]]) – Sequence of(value, unit)tuples to return from successivedistance()calls.
- class ferrosoft.addresses.AddressRouterGeoapify(config)[source]¶
Bases:
AddressRouterServiceGeocoding and routing backed by the Geoapify REST API.
Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.AddressRouterGeoapifyinstead. The replacement validates HTTP response status codes, falls back to city-level geocoding on missing results, usesDecimalfor distances, and raises typed exceptions.Configuration is read from
settings.FERROBASE_MAP_CONFIGviaConfig.- class Config(base_url: <module 'string' from '/home/ap/.local/share/uv/python/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/string/__init__.py'>, api_key: <module 'string' from '/home/ap/.local/share/uv/python/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/string/__init__.py'>)[source]¶
Bases:
NamedTupleGeoapify connection configuration.
- Parameters:
base_url (<module ‘string’ from ‘/home/ap/.local/share/uv/python/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/string/__init__.py’>) – Base URL of the Geoapify API (e.g.
"https://api.geoapify.com").api_key (<module ‘string’ from ‘/home/ap/.local/share/uv/python/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/string/__init__.py’>) – Geoapify API key.
- api_key: <module 'string' from '/home/ap/.local/share/uv/python/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/string/__init__.py'>¶
Alias for field number 1
- base_url: <module 'string' from '/home/ap/.local/share/uv/python/cpython-3.14.5-linux-x86_64-gnu/lib/python3.14/string/__init__.py'>¶
Alias for field number 0
- distance(route)[source]¶
Calculate the road distance of a route via the Geoapify routing API.
Uses
heavy_truckrouting mode. ReturnsNoneif the API response contains no feature results.
- geocode(address)[source]¶
Resolve a free-form address string via the Geoapify geocoding API.
- Parameters:
address (
str) – Free-form address string to geocode.- Return type:
- Returns:
A
Locationbuilt from the first Geoapify result.- Raises:
requests.HTTPError – On a non-2xx HTTP response.
RuntimeError – If the API response is malformed or returns no results.
- class ferrosoft.addresses.AddressRouterService[source]¶
Bases:
ABCAbstract geocoding and routing service.
Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.AddressRouterServiceinstead, which separates geocoding and routing into distinct abstract base classes and supports pluggable implementations viaFERROBASE_ADDRESS_ROUTER_CLASS.The
default()class method always returns anAddressRouterGeoapifyinstance.- classmethod default()[source]¶
Return the default service instance (always
AddressRouterGeoapify).Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.AddressRouterService.get_instance()instead, which selects the implementation from Django settings.
- class ferrosoft.addresses.Coordinates(lat: float, lon: float)[source]¶
Bases:
NamedTupleGeographic coordinates as a (latitude, longitude) pair using
float.Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.Coordinatesinstead, which stores values asDecimalto preserve precision in downstream calculations.
- class ferrosoft.addresses.Distance(value: float, unit: str)[source]¶
Bases:
NamedTupleA measured distance with a unit string.
Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.Distanceinstead, which stores the value asDecimal.
- class ferrosoft.addresses.Location(coords: Coordinates, name: str, address: Address)[source]¶
Bases:
NamedTupleA resolved geographic location combining coordinates, a display name, and an address.
Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.Locationinstead.- coords: Coordinates¶
Alias for field number 0
- class ferrosoft.addresses.Route(legs: List[RouteLeg])[source]¶
Bases:
NamedTupleAn ordered sequence of route legs.
Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.Routeinstead.
- class ferrosoft.addresses.RouteLeg(start: Coordinates, destination: Coordinates)[source]¶
Bases:
NamedTupleA single leg of a route, defined by start and destination coordinates.
Deprecated since version Use:
ferrosoft.apps.ferromaps.services.addresses.RouteLeginstead.- destination: Coordinates¶
Alias for field number 1
- start: Coordinates¶
Alias for field number 0
ferrosoft.report module¶
PDF report generation via the WeasyPrint microservice.
This module provides a two-stage pipeline for producing reports:
Template rendering — a Django template is rendered to HTML with a standard context that includes the current UTC timestamp, a title, and an embeddability flag.
PDF conversion — the rendered HTML is POSTed to a running WeasyPrint HTTP microservice, which returns the PDF bytes.
The WeasyPrint service is configured via the WEASYPRINT_URL Django setting
and is typically started alongside the application in the development
environment (make local). In production it is declared as a dependency in
compose-production.yml.
Callers can bypass PDF generation entirely by passing as_html=True (to get
rendered HTML bytes back) or as_text=True (to get the raw HTML string
before encoding), which is useful for preview modes or debugging.
Example:
from ferrosoft.report import generate_report
pdf_bytes = generate_report(
"emiflow/report.html",
context={"shipment": shipment},
title="Emissions Report",
)
# HTML preview (embeddable in an iframe)
html_bytes = generate_report(
"emiflow/report.html",
context={"shipment": shipment},
title="Emissions Report",
as_html=True,
)
- exception ferrosoft.report.ReportError[source]¶
Bases:
RuntimeErrorRaised when the WeasyPrint service returns a non-200 HTTP response.
- Parameters:
message – Human-readable description including the HTTP status code.
- ferrosoft.report.generate_report(template, context=None, title=None, as_html=None, as_text=None)[source]¶
Render a Django template and optionally convert it to PDF.
The template is rendered with a base context merged with any caller-supplied
context. The base context always includes:timestamp— current UTC datetime fromnow_utc().title— passed through from thetitleargument.report_embeddable— set to the value ofas_htmlso templates can adjust their output (e.g. omit<html>/<body>tags when embedded in an iframe).
- Parameters:
template (
str|Template) – Either a template name string (resolved viadjango.template.loader.get_template()) or an already-loadedTemplateinstance.context (
dict|None) – Additional template variables merged on top of the base context. Caller-supplied keys override the base context keys.title (
str|None) – Document title injected into the template astitle.as_html (
bool|None) – IfTrue, return the rendered HTML encoded asbyteswithout sending it to WeasyPrint. Thereport_embeddabletemplate variable is also set toTrue.as_text (
bool|None) – IfTrue, return the rendered HTML as a plainstrwithout encoding or PDF conversion. Takes precedence overas_htmlfor the encoding step.
- Return type:
- Returns:
PDF bytes when neither
as_htmlnoras_textis set. Encoded HTML bytes whenas_html=True. Raw HTML string whenas_text=True.
- ferrosoft.report.generate_report_pdf(html_content)[source]¶
Convert an HTML document to PDF by calling the WeasyPrint microservice.
The HTML is POSTed as
text/htmlto the URL configured insettings.WEASYPRINT_URL. A SHA-256 digest of the content is sent as theX-Trace-Idheader so that WeasyPrint logs can be correlated with application logs.- Parameters:
html_content (
bytes) – The HTML document as UTF-8 encoded bytes.- Return type:
- Returns:
The PDF document as raw bytes returned by WeasyPrint.
- Raises:
ValueError – If
WEASYPRINT_URLis not present in Django settings.ReportError – If WeasyPrint returns any HTTP status other than 200.
ferrosoft.request module¶
Query string parsing with PHP/Rails/jQuery bracket-notation support.
Deprecated since version 2.0.0: Use of this module is strongly discouraged. New code should use
Django’s built-in form machinery (Form,
ModelForm) together with standard
QueryDict access patterns. Django forms provide
validation, error reporting, rendering, and CSRF protection that this
module does not.
This module was written to handle query strings that use the bracket-notation
convention popularised by PHP, Ruby on Rails, and jQuery’s $.param():
foo=bar→{"foo": "bar"}(scalar)items[]=1&items[]=2→{"items": ["1", "2"]}(list)addr[city]=Berlin&addr[zip]=10115→{"addr": {"city": "Berlin", "zip": "10115"}}(dict)
It also provides is_htmx_request() for detecting
HTMX driven requests, and sanitize_uuid() /
sanitize_decimal() converters used by the Form class.
The Form class mimics the django.forms.Form API at a surface
level (is_valid() / cleaned_data) but lacks Django’s validation
pipeline, localisation support, and widget rendering.
Migration guide¶
Replace Form subclasses with Form subclasses
and restructure_query() calls with direct request.GET/request.POST
access or a DRF serializer. Replace is_htmx_request() with
django_htmx or an inline header check.
- class ferrosoft.request.DecimalField(**kwargs)[source]¶
Bases:
FormFieldA
FormFieldthat sanitizes values withsanitize_decimal().Deprecated since version Use:
DecimalFieldinstead.
- class ferrosoft.request.Form(query)[source]¶
Bases:
objectMinimal Django-form-like class for parsing bracket-notation query strings.
Deprecated since version Use:
Forminstead.At construction time, all
FormFieldclass attributes are discovered by reflection. Theirsanitizercallables are collected into a map that is passed torestructure_query()whenis_valid()is called.- Parameters:
query (
QueryDict) – TheQueryDictto parse (typicallyrequest.GETorrequest.POST).
- is_valid()[source]¶
Parse and sanitize the query string.
Populates
cleaned_dataon success.- Return type:
- Returns:
Trueifrestructure_query()succeeds;Falseif it raisesValueError(malformed key or type conflict).
- class ferrosoft.request.FormField(**kwargs)[source]¶
Bases:
objectDescriptor that declares a field on a
Formsubclass.Stores arbitrary keyword arguments and exposes them via
__getitem__. The special key"sanitizer"is allowed to be absent (returnsNone).Deprecated since version Use:
Fieldand its subclasses instead.- Parameters:
**kwargs – Field configuration, typically
sanitizer.
- class ferrosoft.request.IntegerField(**kwargs)[source]¶
Bases:
DecimalFieldA
DecimalFieldalias for integer inputs.Deprecated since version Use:
IntegerFieldinstead.
- class ferrosoft.request.QueryFieldType(*values)[source]¶
Bases:
EnumClassifies a bracket-notation query string key by its expected value shape.
Determines how values are aggregated when
restructure_query()processes aQueryDict.- SCALAR¶
A plain key with no brackets. Only a single value is stored. Even if the key contains brackets that make it look subscriptable the field type is still
SCALARwhen no brackets are present.
- LIST¶
A key ending in
[]. All values for this key are collected into a list.
- DICT¶
A key with a non-empty subscript, e.g.
addr[city]. Values are stored in a dict keyed by the subscript string.
- DICT = 3¶
- LIST = 2¶
- SCALAR = 1¶
- class ferrosoft.request.UUIDField(**kwargs)[source]¶
Bases:
FormFieldA
FormFieldthat sanitizes values withsanitize_uuid().Deprecated since version Use:
UUIDFieldinstead.- Parameters:
**kwargs – Passed through to
FormField.
- ferrosoft.request.is_htmx_request(request)[source]¶
Return
Trueif the request was issued by the HTMX JavaScript library.HTMX sets the
HX-Request: trueheader on every request it makes. This can be used to return partial HTML fragments instead of full pages.- Parameters:
request (
HttpRequest) – The incoming Django HTTP request.- Return type:
- Returns:
Trueif theHX-Requestheader is present and equals"true";Falseotherwise.
- ferrosoft.request.restructure_query(query, sanitizers=None)[source]¶
Convert a
QueryDictto a nested Python structure.Follows the PHP / Ruby on Rails / jQuery
$.param()bracket-notation convention (lightly):Plain keys (no brackets) are stored as scalars.
Keys ending with
[]are collected into a list.Keys with a named subscript (e.g.
addr[city]) are stored in a dict.
If a sanitizer is provided for a root key, it is applied to every value for that key before storage.
- Parameters:
- Return type:
- Returns:
A
dictwith scalar, list, or dict values depending on the key notation used.- Raises:
ValueError – If a key does not match the expected pattern, if a list field carries more than one value while typed as scalar, or if the same root key is used with conflicting types (e.g. first as a scalar then as a dict).
Example:
# Query string: foo=bar&items[]=1&items[]=2&addr[city]=Berlin result = restructure_query(request.GET) # {"foo": "bar", "items": ["1", "2"], "addr": {"city": "Berlin"}}
- ferrosoft.request.sanitize_decimal(input)[source]¶
Parse a
Decimalfrom a string, treating empty input as zero.