ferrosoft.apps.ferrobase package¶
Ferrobase — the load-bearing foundation of the Ferrosoft platform.
Ferrobase is the framework on which every other Ferrosoft application is built. It owns the multi-tenant request pipeline, the identity and access model, the subscription and feature-gating system, the file substrate, the shared reference data, the dependency-injection container, the real-time frontend update channel, and the menu / settings extension points. Other apps register against ferrobase; ferrobase imports none of them. If a Ferrosoft application is a storey of the building, ferrobase is the slab and the steel.
Domain overview¶
The ferrobase domain centres on a small set of long-lived entities that every other app links against:
Organisations and tenants. An
Organizationis a customer; it owns one or moreTenantworkspaces. Each tenant is fully isolated — its own PostgreSQL role, its own database, its own configuration — and is selected per request by the multi-tenant middleware (see below).Identity and access.
Userextends Django’sAbstractUserand is backed by ZITADEL for authentication.APITokencovers machine access. One-time flows like password reset and account activation are issued as access-key documents (PasswordResetRequest,AccountActivationRequest) built onAccessKeyMixin.Subscriptions and feature gating. A
SubscriptionPlanpairs an organisation with the set ofAppFeatures it has licensed;FeatureAssignmentbinds those features to individual tenants. Platform apps consult the assignment before exposing menu entries or routes, so the same codebase can serve customers on different tiers without branching.Files. A
StoredFilelives inside aDirectorywith optionalFileAccessGrantrows scoping visibility per user or organisation; content is delegated to a pluggable storage backend so file data never sits in the application database.Shared reference data.
Country,Language,TimeZone,Material,BusinessPartner,CompanySite,Salutation, and theIDSequence/IdSequenceConfigpair back the drop-downs, look-ups, and human-readable identifiers that every other app reuses.
Multi-tenancy¶
Tenancy is ferrobase’s defining cross-cutting concern. The active
Tenant for a request is stored in a
ContextVar defined in
context, set by
TenantMiddleware
on the way in and read by both synchronous views and asynchronous
Channels consumers without any explicit argument passing. ORM queries
are dispatched by
TenantRouter to
the matching database alias; provisioning — PostgreSQL role and
database creation, migration application, fixture loading — lives in
tenant_mgm. The result is
that downstream apps write ordinary Django code and the tenant
boundary is enforced underneath them.
Cross-cutting infrastructure¶
A handful of platform utilities turn the domain models into a running system, and stay open as extension points for other apps:
The DI container in
containers(built withdependency_injector) is initialised inFerrobaseConfig.ready, wired into the cheapyq task queue and selected services, and parameterised through theFERROBASE_CONTAINERsettings block.The shared HTTPX client is managed by
contextas an ASGI lifespan manager so every outbound HTTP call — most prominently the ZITADEL adapter — reuses a single connection pool.The frontend update channel.
FrontendUIUpdaterserialisesElement/UpdateSpectrees and broadcasts them over a Django Channels group, so server-side state changes repaint UI fragments live without a full page reload.Generic CRUD.
genericdefines the platform’s gold-standard class-based views —CreateView,UpdateView,DeleteView,ListView,FilterView,TableView— paired withmodel_crud_routesto mint URL patterns from them. Other apps build their CRUD on top, not on raw Django.Menu registration.
SettingsMenuRegistrycollectsMenuItemcontributions from every installed app’sAppConfigso the settings navigation is composed at startup rather than hard-coded.
Package layout¶
models— ORM models grouped by concern:base(UUID model and the mixin familyAccessKey,Address,Coordinates,ReadOnly),identity(organisations, tenants, users, API tokens),subscription(plans, features, assignments),file(stored files, access grants),shared(countries, languages, materials, partners, sites, ID sequences, menu items),registry(lookup-table back ends), plusdecimal,fields,units, andbuiltin.services— business logic: the ZITADEL IAM adapter, tenant provisioning, user management, onboarding, scientific unit conversion, the cheapyq task-queue glue, image CAPTCHA, password-reset email dispatch, Server-Sent Events client, static-file serving, cleanup tasks, and platform updaters.views— the generic CRUD framework plus per-domain views for users, organisations, tenants, settings, groups, materials, partners, files, API tokens, and the dashboard index.middleware— request-pipeline middleware:tenant(per-request routing plusTenantRouter),cache,language,asgi, andappmeta.tenant— the active-tenantContextVar, tenant-scoped query helpers, and the forms / tables that surface tenants in the UI.api— base DRF infrastructure (exception handlers, router, base serializers, base viewsets) that the app-level REST APIs build on.auth— role definitions used by the permission system.forms— base form mixins (copy,fields,model_forms).session— password-reset session forms and templated email dispatch.tables— reusable django-tables2 columns and base tables with platform rendering for currency, units, dates, arrays, inline edit, and summing footers.templatetags— template helpers spanning Bootstrap 5 widgets, datetime / decimal formatting, IAM checks, menu rendering, and the AJAX / HTMX loading-indicator glue.management— Django management commands, includingferrobase_update, the umbrella updater other apps hook into.
Top-level modules cover cross-cutting concerns:
apps— DjangoAppConfigthat builds the DI container, registers the HTTPX lifespan manager, and exposes theAppMeta,MenuItem, andSettingsMenuRegistryextension types other apps use.containers—dependency_injectorContainerwiring.consumers— Django Channels WebSocket consumers used by the frontend update channel.context— ASGI lifespan context manager for the shared HTTPX client.context_processors— Django template context processors.encodings— character-encoding detection and the catalogue of supported text encodings.filter— base django-filterFilterSetdefinitions reused across apps.frontend— primitives for pushing real-time UI updates over Channels.templating— utilities for rendering Django templates from raw strings.urls— root URL configuration for the ferrobase application.
External integrations¶
Ferrobase carries the platform’s interfaces to the outside world so downstream apps don’t have to:
ZITADEL. The IAM provider behind every authenticated request.
zitadelwraps the ZITADEL IAM v2 REST API for user, organisation, and project management; end users authenticate over ZITADEL’s OIDC flows and are mirrored into ferrobase’sUserrecords.PostgreSQL multi-tenancy. Tenant provisioning in
tenant_mgmcreates one PostgreSQL role and database per tenant and runs migrations and fixtures against it;TenantRouterdispatches each ORM query to the right alias at runtime.Django Channels over Redis / Valkey. The channel layer carries the frontend update broadcasts and any other WebSocket traffic served by
consumers.HTTPX. All outbound HTTP — ZITADEL, file storage backends, third-party APIs called from downstream apps — flows through the shared connection pool managed by the lifespan context manager in
context.
Subpackages¶
- ferrosoft.apps.ferrobase.api package
- ferrosoft.apps.ferrobase.auth package
- ferrosoft.apps.ferrobase.forms package
- Submodules
- ferrosoft.apps.ferrobase.forms.copy module
- ferrosoft.apps.ferrobase.forms.fields module
- ferrosoft.apps.ferrobase.forms.model_forms module
- ferrosoft.apps.ferrobase.management package
- Subpackages
- ferrosoft.apps.ferrobase.management.commands package
- Submodules
- ferrosoft.apps.ferrobase.management.commands.addtenant module
- ferrosoft.apps.ferrobase.management.commands.deltenant module
- ferrosoft.apps.ferrobase.management.commands.dumpdata_tenant module
- ferrosoft.apps.ferrobase.management.commands.ferrobase_cleanup module
- ferrosoft.apps.ferrobase.management.commands.ferrobase_update module
- ferrosoft.apps.ferrobase.management.commands.listtenants module
- ferrosoft.apps.ferrobase.management.commands.loaddata_tenant module
- ferrosoft.apps.ferrobase.management.commands.migratetenants module
- ferrosoft.apps.ferrobase.management.commands package
- Submodules
- ferrosoft.apps.ferrobase.management.tenantcommand module
- Subpackages
- ferrosoft.apps.ferrobase.middleware package
- Submodules
- ferrosoft.apps.ferrobase.middleware.appmeta module
- ferrosoft.apps.ferrobase.middleware.asgi module
- ferrosoft.apps.ferrobase.middleware.cache module
- ferrosoft.apps.ferrobase.middleware.language module
- ferrosoft.apps.ferrobase.middleware.tenant module
- ferrosoft.apps.ferrobase.models package
- Submodules
- ferrosoft.apps.ferrobase.models.base module
AccessKeyMixinAddressMixinAddressMixin.MetaAddressMixin.additional_linesAddressMixin.addressAddressMixin.address_lines()AddressMixin.cityAddressMixin.companyAddressMixin.countryAddressMixin.country_idAddressMixin.first_nameAddressMixin.house_noAddressMixin.idAddressMixin.last_nameAddressMixin.postal_codeAddressMixin.salutationAddressMixin.salutation_idAddressMixin.street
CommonLimitCoordinatesMixinISOCatalogEntryISOCatalogEntryManagerIntegerChoicesMixinObjectLookupCachePrefetchedFieldMissingReadOnlyMixinSelectOptionsMixinUUIDModelprefetched_field()
- ferrosoft.apps.ferrobase.models.builtin module
- ferrosoft.apps.ferrobase.models.decimal module
- ferrosoft.apps.ferrobase.models.fields module
- ferrosoft.apps.ferrobase.models.file module
AccessModeAccessSubjectDirectoryFileAccessGrantFileInfoStoredFileStoredFile.DoesNotExistStoredFile.MultipleObjectsReturnedStoredFile.access_grantsStoredFile.contentStoredFile.creation_timeStoredFile.delete()StoredFile.directoryStoredFile.directory_idStoredFile.expire_onStoredFile.get_absolute_url()StoredFile.get_authenticated_file()StoredFile.get_next_by_creation_time()StoredFile.get_previous_by_creation_time()StoredFile.guess_media_type()StoredFile.idStoredFile.media_typeStoredFile.nameStoredFile.objectsStoredFile.ownerStoredFile.owner_idStoredFile.transport_chains
StoredFileManager
- ferrosoft.apps.ferrobase.models.identity module
APITokenAPIToken.DoesNotExistAPIToken.MultipleObjectsReturnedAPIToken.SettingsAPIToken.TOKEN_ID_CLAIMAPIToken.acreate()APIToken.averify()APIToken.create()APIToken.expirationAPIToken.get_next_by_expiration()APIToken.get_previous_by_expiration()APIToken.get_status_display()APIToken.idAPIToken.is_valid()APIToken.nameAPIToken.objectsAPIToken.statusAPIToken.tenantAPIToken.tenant_idAPIToken.userAPIToken.user_id
APITokenPrefetchedManagerAPITokenStatusAccountActivationRequestAccountActivationRequest.DoesNotExistAccountActivationRequest.MultipleObjectsReturnedAccountActivationRequest.access_keyAccountActivationRequest.access_key_expiryAccountActivationRequest.create()AccountActivationRequest.get_absolute_url()AccountActivationRequest.get_next_by_access_key_expiry()AccountActivationRequest.get_previous_by_access_key_expiry()AccountActivationRequest.idAccountActivationRequest.objectsAccountActivationRequest.tenantAccountActivationRequest.tenant_id
OrganizationOrganization.DoesNotExistOrganization.MultipleObjectsReturnedOrganization.contact_emailOrganization.contact_first_nameOrganization.contact_last_nameOrganization.get_absolute_url()Organization.iam_referenceOrganization.idOrganization.nameOrganization.objectsOrganization.save()Organization.subscriptionOrganization.subscription_idOrganization.tenantsOrganization.users
OrganizationManagerPasswordResetRequestPasswordResetRequest.DoesNotExistPasswordResetRequest.MultipleObjectsReturnedPasswordResetRequest.access_keyPasswordResetRequest.access_key_expiryPasswordResetRequest.create()PasswordResetRequest.get_next_by_access_key_expiry()PasswordResetRequest.get_previous_by_access_key_expiry()PasswordResetRequest.idPasswordResetRequest.objectsPasswordResetRequest.userPasswordResetRequest.user_id
TenantTenant.DoesNotExistTenant.MultipleObjectsReturnedTenant.activate_connection()Tenant.activeTenant.afind()Tenant.all_ordered()Tenant.database_hostnameTenant.database_nameTenant.database_passwordTenant.database_portTenant.database_usernameTenant.django_connectionTenant.expiry_dateTenant.find()Tenant.get_absolute_url()Tenant.idTenant.lookup()Tenant.nameTenant.objectsTenant.organizationTenant.organization_id
TenantManagerUserUser.DoesNotExistUser.MINIMUM_PASSWORD_LENGTHUser.MultipleObjectsReturnedUser.PasswordInsufficientSizeUser.PasswordsDoNotMatchUser.REQUIRED_FIELDSUser.all_ordered()User.api_tokensUser.assign_roles()User.color_themeUser.date_joinedUser.emailUser.emaildevice_setUser.first_nameUser.format_email()User.get_absolute_url()User.get_assigned_feature()User.get_by_username()User.get_color_theme_display()User.get_next_by_date_joined()User.get_previous_by_date_joined()User.groupsUser.idUser.is_activeUser.is_staffUser.is_superuserUser.languageUser.language_idUser.last_loginUser.last_nameUser.local_user()User.logentry_setUser.objectsUser.organizationUser.organization_idUser.passwordUser.passwordresetrequest_setUser.phonedevice_setUser.set_password_validated()User.set_tenants()User.staticdevice_setUser.tenant_allowed()User.tenant_options()User.timezoneUser.timezone_idUser.totpdevice_setUser.user_permissionsUser.username
UserManager
- ferrosoft.apps.ferrobase.models.registry module
- ferrosoft.apps.ferrobase.models.shared module
AccountAccount.nameAccount.internalAccount.addressAccount.latitudeAccount.longitudeAccount.DoesNotExistAccount.MultipleObjectsReturnedAccount.addressAccount.externals()Account.idAccount.internalAccount.internals()Account.latitudeAccount.longitudeAccount.nameAccount.objectsAccount.validate()
BusinessPartnerBusinessPartner.DoesNotExistBusinessPartner.MultipleObjectsReturnedBusinessPartner.additional_linesBusinessPartner.cityBusinessPartner.companyBusinessPartner.countryBusinessPartner.country_idBusinessPartner.first_nameBusinessPartner.format_for_map()BusinessPartner.get_absolute_url()BusinessPartner.get_by_name()BusinessPartner.get_ordered()BusinessPartner.house_noBusinessPartner.idBusinessPartner.last_nameBusinessPartner.latitudeBusinessPartner.longitudeBusinessPartner.nameBusinessPartner.objectsBusinessPartner.postal_codeBusinessPartner.salutationBusinessPartner.salutation_idBusinessPartner.save()BusinessPartner.street
ColorThemeColorThemeDiscoveryCompanySiteCompanySite.DoesNotExistCompanySite.MultipleObjectsReturnedCompanySite.additional_linesCompanySite.allowed_usersCompanySite.cityCompanySite.codeCompanySite.companyCompanySite.countryCompanySite.country_idCompanySite.first_nameCompanySite.get_absolute_url()CompanySite.house_noCompanySite.idCompanySite.is_user_allowed()CompanySite.last_nameCompanySite.nameCompanySite.objectsCompanySite.options_for_user()CompanySite.postal_codeCompanySite.salutationCompanySite.salutation_idCompanySite.set_locations_for_user()CompanySite.street
CountryIDSequenceIdSequenceConfigLanguageLanguageManagerLocalUserLockableModelMixinMaterialMaterialManagerMenuItemModelIsLockedSalutationTimeZoneTimeZoneManager
- ferrosoft.apps.ferrobase.models.subscription module
AppFeatureAppFeatureManagerFeatureAssignmentFeatureAssignment.DoesNotExistFeatureAssignment.MultipleObjectsReturnedFeatureAssignment.featureFeatureAssignment.feature_idFeatureAssignment.get_absolute_url()FeatureAssignment.idFeatureAssignment.limitFeatureAssignment.objectsFeatureAssignment.planFeatureAssignment.plan_idFeatureAssignment.with_plan()
FeatureAssignmentModelSubscriptionPlanSubscriptionPlan.DoesNotExistSubscriptionPlan.MultipleObjectsReturnedSubscriptionPlan.assigned_featuresSubscriptionPlan.enable_all_featuresSubscriptionPlan.featuresSubscriptionPlan.get_absolute_url()SubscriptionPlan.get_assigned_feature()SubscriptionPlan.idSubscriptionPlan.max_tonnageSubscriptionPlan.nameSubscriptionPlan.objects
SubscriptionPlanManager
- ferrosoft.apps.ferrobase.models.units module
- ferrosoft.apps.ferrobase.services package
- Submodules
- ferrosoft.apps.ferrobase.services.captcha module
- ferrosoft.apps.ferrobase.services.cleanup module
CleanupServiceCleanupService.DELETE_EXAMINATION_REQUEST_DAYSCleanupService.DELETE_TENANT_DAYSCleanupService.cleanup_master_database()CleanupService.cleanup_tenant_database()CleanupService.deactivate_expired_tenants()CleanupService.delete_empty_orgs()CleanupService.delete_expired_files()CleanupService.delete_inactive_tenants()CleanupService.delete_old_examination_requests()
- ferrosoft.apps.ferrobase.services.conversion module
- ferrosoft.apps.ferrobase.services.help module
- ferrosoft.apps.ferrobase.services.idgenerator module
- ferrosoft.apps.ferrobase.services.onboarding module
- ferrosoft.apps.ferrobase.services.sse module
- ferrosoft.apps.ferrobase.services.static_files module
- ferrosoft.apps.ferrobase.services.taskqueue module
- ferrosoft.apps.ferrobase.services.tenant_mgm module
- ferrosoft.apps.ferrobase.services.update module
- ferrosoft.apps.ferrobase.services.user_mgm module
- ferrosoft.apps.ferrobase.services.zitadel module
- ferrosoft.apps.ferrobase.session package
- ferrosoft.apps.ferrobase.tables package
- Submodules
- ferrosoft.apps.ferrobase.tables.columns module
- ferrosoft.apps.ferrobase.tables.crud module
- ferrosoft.apps.ferrobase.tables.models module
- ferrosoft.apps.ferrobase.tables.table module
- ferrosoft.apps.ferrobase.templatetags package
- Submodules
- ferrosoft.apps.ferrobase.templatetags.ajax module
- ferrosoft.apps.ferrobase.templatetags.bootstrap5 module
- ferrosoft.apps.ferrobase.templatetags.datetime module
- ferrosoft.apps.ferrobase.templatetags.decimal module
- ferrosoft.apps.ferrobase.templatetags.ferrobase module
- ferrosoft.apps.ferrobase.templatetags.ferrobase_files module
- ferrosoft.apps.ferrobase.templatetags.ferrobase_menu module
- ferrosoft.apps.ferrobase.templatetags.foundation module
- ferrosoft.apps.ferrobase.templatetags.iam module
- ferrosoft.apps.ferrobase.templatetags.labels module
- ferrosoft.apps.ferrobase.templatetags.tables module
- ferrosoft.apps.ferrobase.tenant package
- ferrosoft.apps.ferrobase.views package
- Submodules
- ferrosoft.apps.ferrobase.views.api_token module
APITokenCreateViewAPITokenReadViewAPITokenReadView.activity_appAPITokenReadView.activity_titleAPITokenReadView.create_viewAPITokenReadView.filterset_classAPITokenReadView.get_queryset()APITokenReadView.modelAPITokenReadView.orderingAPITokenReadView.permission_requiredAPITokenReadView.pluralAPITokenReadView.singularAPITokenReadView.table_class
- ferrosoft.apps.ferrobase.views.base module
- ferrosoft.apps.ferrobase.views.company_site module
CompanySiteCreateViewCompanySiteDeleteViewCompanySiteReadViewCompanySiteUpdateViewCompanySiteUpdateView.activity_appCompanySiteUpdateView.activity_titleCompanySiteUpdateView.cancel_viewCompanySiteUpdateView.create_viewCompanySiteUpdateView.delete_viewCompanySiteUpdateView.form_classCompanySiteUpdateView.form_templateCompanySiteUpdateView.modelCompanySiteUpdateView.permission_requiredCompanySiteUpdateView.pluralCompanySiteUpdateView.read_viewCompanySiteUpdateView.singular
- ferrosoft.apps.ferrobase.views.files module
DirectoryListingViewDirectoryListingView.activity_appDirectoryListingView.activity_titleDirectoryListingView.create_viewDirectoryListingView.filterset_classDirectoryListingView.get_queryset()DirectoryListingView.help_topicDirectoryListingView.modelDirectoryListingView.orderingDirectoryListingView.permission_requiredDirectoryListingView.pluralDirectoryListingView.singularDirectoryListingView.table_class
DirectoryRootViewFileUpdateViewFileUpdateView.activity_appFileUpdateView.activity_titleFileUpdateView.cancel_viewFileUpdateView.create_viewFileUpdateView.delete_viewFileUpdateView.fieldsFileUpdateView.get_context_data()FileUpdateView.help_topicFileUpdateView.modelFileUpdateView.permission_requiredFileUpdateView.pluralFileUpdateView.read_viewFileUpdateView.singularFileUpdateView.template_name
- ferrosoft.apps.ferrobase.views.generic module
ClassAttributesCopyViewCopyViewMetaCreateViewCreateViewMetaDeleteViewDeleteViewMetaDependentCreateViewDetailViewDetailViewMetaFilterViewFilterViewMetaListViewListViewMetaModelContextMixinModelContextMixin.cancel_viewModelContextMixin.copy_viewModelContextMixin.create_viewModelContextMixin.delete_viewModelContextMixin.enable_filter_displayModelContextMixin.form_templateModelContextMixin.get_context_data()ModelContextMixin.is_allowed()ModelContextMixin.list_item_templateModelContextMixin.multipart_formModelContextMixin.pluralModelContextMixin.read_viewModelContextMixin.related_linksModelContextMixin.related_links_defaultModelContextMixin.singular
PAGE_SIZEPlatformActivityMixinTableViewTableViewMetaTemplateViewUpdateViewUpdateViewMetamodel_crud_routes()
- ferrosoft.apps.ferrobase.views.group module
GroupCreateViewGroupDeleteViewGroupListViewGroupPermissionsViewGroupPermissionsView.activity_appGroupPermissionsView.activity_titleGroupPermissionsView.cancel_viewGroupPermissionsView.create_viewGroupPermissionsView.delete_viewGroupPermissionsView.form_classGroupPermissionsView.get_success_url()GroupPermissionsView.modelGroupPermissionsView.permission_requiredGroupPermissionsView.pluralGroupPermissionsView.read_viewGroupPermissionsView.singular
GroupUpdateViewGroupUpdateView.activity_appGroupUpdateView.activity_titleGroupUpdateView.cancel_viewGroupUpdateView.create_viewGroupUpdateView.delete_viewGroupUpdateView.fieldsGroupUpdateView.get_success_url()GroupUpdateView.modelGroupUpdateView.permission_requiredGroupUpdateView.pluralGroupUpdateView.read_viewGroupUpdateView.singularGroupUpdateView.template_name
- ferrosoft.apps.ferrobase.views.index module
- ferrosoft.apps.ferrobase.views.material module
MaterialCopyViewMaterialCreateViewMaterialDeleteViewMaterialListViewMaterialUpdateViewMaterialUpdateView.activity_appMaterialUpdateView.activity_titleMaterialUpdateView.cancel_viewMaterialUpdateView.copy_viewMaterialUpdateView.create_viewMaterialUpdateView.delete_viewMaterialUpdateView.fieldsMaterialUpdateView.help_topicMaterialUpdateView.modelMaterialUpdateView.permission_requiredMaterialUpdateView.pluralMaterialUpdateView.read_viewMaterialUpdateView.singular
- ferrosoft.apps.ferrobase.views.media module
- ferrosoft.apps.ferrobase.views.onboarding module
- ferrosoft.apps.ferrobase.views.organization module
FeatureAssignmentCreateViewFeatureAssignmentCreateView.activity_appFeatureAssignmentCreateView.activity_titleFeatureAssignmentCreateView.cancel_viewFeatureAssignmentCreateView.form_classFeatureAssignmentCreateView.get_form_kwargs()FeatureAssignmentCreateView.modelFeatureAssignmentCreateView.parent_modelFeatureAssignmentCreateView.permission_required
FeatureAssignmentDeleteViewFeatureAssignmentUpdateViewFeatureAssignmentUpdateView.activity_appFeatureAssignmentUpdateView.activity_titleFeatureAssignmentUpdateView.cancel_viewFeatureAssignmentUpdateView.create_viewFeatureAssignmentUpdateView.delete_viewFeatureAssignmentUpdateView.form_classFeatureAssignmentUpdateView.get_success_url()FeatureAssignmentUpdateView.modelFeatureAssignmentUpdateView.permission_requiredFeatureAssignmentUpdateView.pluralFeatureAssignmentUpdateView.read_viewFeatureAssignmentUpdateView.singular
FeatureLimiterFeatureRequiredMixinOrganizationCreateViewOrganizationDeleteViewOrganizationTableViewOrganizationTableView.activity_appOrganizationTableView.activity_titleOrganizationTableView.create_viewOrganizationTableView.filterset_classOrganizationTableView.modelOrganizationTableView.orderingOrganizationTableView.permission_requiredOrganizationTableView.pluralOrganizationTableView.singularOrganizationTableView.table_class
OrganizationUpdateViewOrganizationUpdateView.activity_appOrganizationUpdateView.activity_titleOrganizationUpdateView.cancel_viewOrganizationUpdateView.create_viewOrganizationUpdateView.delete_viewOrganizationUpdateView.form_classOrganizationUpdateView.get_context_data()OrganizationUpdateView.modelOrganizationUpdateView.permission_requiredOrganizationUpdateView.pluralOrganizationUpdateView.read_viewOrganizationUpdateView.related_links()OrganizationUpdateView.singularOrganizationUpdateView.template_name
SubscriptionPlanCopyViewSubscriptionPlanCreateViewSubscriptionPlanDeleteViewSubscriptionPlanTableViewSubscriptionPlanTableView.activity_appSubscriptionPlanTableView.activity_titleSubscriptionPlanTableView.create_viewSubscriptionPlanTableView.filterset_classSubscriptionPlanTableView.modelSubscriptionPlanTableView.orderingSubscriptionPlanTableView.permission_requiredSubscriptionPlanTableView.pluralSubscriptionPlanTableView.singularSubscriptionPlanTableView.table_class
SubscriptionPlanUpdateViewSubscriptionPlanUpdateView.activity_appSubscriptionPlanUpdateView.activity_titleSubscriptionPlanUpdateView.cancel_viewSubscriptionPlanUpdateView.copy_viewSubscriptionPlanUpdateView.create_viewSubscriptionPlanUpdateView.delete_viewSubscriptionPlanUpdateView.form_classSubscriptionPlanUpdateView.get_context_data()SubscriptionPlanUpdateView.modelSubscriptionPlanUpdateView.permission_requiredSubscriptionPlanUpdateView.pluralSubscriptionPlanUpdateView.read_viewSubscriptionPlanUpdateView.singularSubscriptionPlanUpdateView.template_name
UpgradePlanView
- ferrosoft.apps.ferrobase.views.partner module
PartnerCreateViewPartnerDeleteViewPartnerFilterViewPartnerUpdateViewPartnerUpdateView.activity_appPartnerUpdateView.activity_titlePartnerUpdateView.cancel_viewPartnerUpdateView.create_viewPartnerUpdateView.delete_viewPartnerUpdateView.form_classPartnerUpdateView.form_templatePartnerUpdateView.modelPartnerUpdateView.permission_requiredPartnerUpdateView.pluralPartnerUpdateView.read_viewPartnerUpdateView.singular
- ferrosoft.apps.ferrobase.views.password module
- ferrosoft.apps.ferrobase.views.session module
- ferrosoft.apps.ferrobase.views.settings module
IDSequenceCreateIDSequenceReadIDSequenceUpdateIDSequenceUpdate.activity_appIDSequenceUpdate.activity_titleIDSequenceUpdate.cancel_viewIDSequenceUpdate.create_viewIDSequenceUpdate.delete_viewIDSequenceUpdate.form_classIDSequenceUpdate.modelIDSequenceUpdate.permission_requiredIDSequenceUpdate.pluralIDSequenceUpdate.read_viewIDSequenceUpdate.related_links_defaultIDSequenceUpdate.singular
MySettingsMySettings.activity_appMySettings.activity_titleMySettings.cancel_viewMySettings.create_viewMySettings.delete_viewMySettings.form_classMySettings.get_object()MySettings.get_success_url()MySettings.modelMySettings.permission_requiredMySettings.pluralMySettings.read_viewMySettings.singularMySettings.template_name
SettingsIndex
- ferrosoft.apps.ferrobase.views.tenant module
TenantCreateViewTenantDeleteViewTenantUpdateViewTenantUpdateView.activity_appTenantUpdateView.activity_titleTenantUpdateView.cancel_view()TenantUpdateView.create_viewTenantUpdateView.delete_viewTenantUpdateView.form_classTenantUpdateView.get_success_url()TenantUpdateView.modelTenantUpdateView.permission_requiredTenantUpdateView.pluralTenantUpdateView.read_viewTenantUpdateView.related_links_defaultTenantUpdateView.singular
choose_tenant()
- ferrosoft.apps.ferrobase.views.user module
UserCreateViewUserDeleteViewUserGroupsViewUserGroupsView.activity_appUserGroupsView.activity_titleUserGroupsView.cancel_viewUserGroupsView.create_viewUserGroupsView.delete_viewUserGroupsView.feature_requiredUserGroupsView.form_classUserGroupsView.modelUserGroupsView.permission_requiredUserGroupsView.pluralUserGroupsView.read_viewUserGroupsView.singular
UserLimiterUserListUserUpdateViewUserUpdateView.activity_appUserUpdateView.activity_titleUserUpdateView.cancel_viewUserUpdateView.create_viewUserUpdateView.delete_viewUserUpdateView.feature_requiredUserUpdateView.form_classUserUpdateView.get()UserUpdateView.modelUserUpdateView.permission_requiredUserUpdateView.pluralUserUpdateView.post()UserUpdateView.read_viewUserUpdateView.singularUserUpdateView.template_name
assign_user_resources()
Submodules¶
ferrosoft.apps.ferrobase.apps module¶
Django application configuration, menu registration types, and platform app metadata.
- class ferrosoft.apps.ferrobase.apps.AppMeta(title, app_name, home_view, icon_path, restricted_app_menu=False, templated_icon=False)[source]¶
Bases:
objectAppMeta provides information about platform applications.
- icon_path: str¶
File path to app icon. It must be a static file if templated_icon = False, otherwise a template file.
Show to the user only the menu of the app, even if the user navigates to a different app.
- class ferrosoft.apps.ferrobase.apps.FerrobaseConfig(app_name, app_module)[source]¶
Bases:
AppConfigDjango AppConfig for the ferrobase application.
Initialises the dependency injection container on startup and registers the HTTPX lifespan manager so a single connection pool is shared across all inbound requests.
- container = None¶
- default_auto_field = 'django.db.models.BigAutoField'¶
- name = 'ferrosoft.apps.ferrobase'¶
- verbose_name = 'Ferrosoft Base'¶
- class ferrosoft.apps.ferrobase.apps.MenuItem(display_name, view_name, permissions=<factory>, category_name=None, icon=None)[source]¶
Bases:
objectA single entry in a navigation or settings menu.
- class ferrosoft.apps.ferrobase.apps.SettingsMenuRegistry[source]¶
Bases:
objectRegistry of settings-page menu items with permission-based filtering.
- ITEMS = [MenuItem(display_name='My settings', view_name='ferrobase:my_settings', permissions=[], category_name='', icon=None), MenuItem(display_name='Account Security', view_name='two_factor:profile', permissions=[], category_name='', icon=None), MenuItem(display_name='Business partners', view_name='ferrobase:businesspartner_index', permissions=['ferrobase.view_businesspartner'], category_name='Core Data', icon=None), MenuItem(display_name='API Tokens', view_name='ferrobase:apitoken_index', permissions=['ferrobase.view_apitoken'], category_name='System', icon=None), MenuItem(display_name='ID Sequences', view_name='ferrobase:idsequenceconfig_index', permissions=['ferrobase.view_idsequenceconfig'], category_name='System', icon=None), MenuItem(display_name='Material', view_name='ferrobase:material_index', permissions=['ferrobase.view_material'], category_name='Core Data', icon=None), MenuItem(display_name='Company Sites', view_name='ferrobase:companysite_index', permissions=['ferrobase.view_companysite'], category_name='Core Data', icon=None), MenuItem(display_name='Users', view_name='ferrobase:user_index', permissions=['ferrobase.view_user'], category_name='System', icon=None), MenuItem(display_name='Groups', view_name='ferrobase:group_index', permissions=['auth.view_group'], category_name='System', icon=None), MenuItem(display_name='Organizations', view_name='ferrobase:organization_index', permissions=['ferrobase.view_organization'], category_name='System', icon=None), MenuItem(display_name='Subscription Plans', view_name='ferrobase:subscriptionplan_index', permissions=['ferrobase.view_subscriptionplan'], category_name='System', icon=None), MenuItem(display_name='Files', view_name='ferrobase:directory_root', permissions=['ferrobase.view_directory'], category_name='', icon=None), MenuItem(display_name='Emitters', view_name='emiflow:emitter_index', permissions=['ferrobase.view_material'], category_name='Emissions', icon=None), MenuItem(display_name='Transport Defaults', view_name='emiflow:settings_transport_defaults', permissions=['emiflow.view_transportchaindefaults'], category_name='Emissions', icon=None), MenuItem(display_name='Lifecycle Categories', view_name='emiflow:lifecyclecategory_index', permissions=['emiflow.view_lifecyclecategory'], category_name='Emissions', icon=None), MenuItem(display_name='Energy Carriers', view_name='emiflow:energycarrier_index', permissions=['emiflow.view_energycarrier'], category_name='Emissions', icon=None)]¶
- classmethod add_items(*items)[source]¶
Add menu items to the registry.
- Parameters:
*items (
MenuItem) –MenuIteminstances to append to the shared registry.
Return settings menu items the requesting user is permitted to see.
- Parameters:
- Returns:
- Permitted items sorted by category then display
name.
- Return type:
ferrosoft.apps.ferrobase.consumers module¶
Django Channels WebSocket consumers for real-time frontend UI updates.
- exception ferrosoft.apps.ferrobase.consumers.EntityIdRequired[source]¶
Bases:
ExceptionRaised when a WebSocket consumer requires an entity ID that was not provided.
- class ferrosoft.apps.ferrobase.consumers.UIEventsConsumer(updater_class, *args, **kwargs)[source]¶
Bases:
JsonWebsocketConsumerWebSocket consumer that dispatches UI update events to the frontend.
Subclasses must implement
on_initial_ui()to push the initial UI state when a client connects. All incoming messages are processed inside the active tenant context resolved from the connection scope.- on_initial_ui()[source]¶
Push the initial UI state to a newly connected client.
Subclasses must override this method and call
self.send_jsonwith the appropriate payload.
- receive_json(content, **kwargs)[source]¶
Handle an incoming JSON message within the active tenant context.
Sends a
ui-failedresponse and logs exceptions if processing fails.
ferrosoft.apps.ferrobase.containers module¶
- class ferrosoft.apps.ferrobase.containers.Container(**overriding_providers)[source]¶
Bases:
DeclarativeContainerFerrobase dependency injection container.
Provides a shared HTTPX
AsyncClientand a factory for the cheapyq task queue. Configuration is injected from Django settings via theFERROBASE_CONTAINERkey insideAppConfig.ready.- cls_providers = {'config': <dependency_injector.providers.Configuration('config')>, 'httpx_client': <dependency_injector.providers.Singleton(<class 'httpx.AsyncClient'>)>, 'task_queue': <dependency_injector.providers.Factory(<class 'ferrosoft.apps.ferrobase.services.taskqueue.CheapyTaskQueue'>)>}¶
- config = <dependency_injector.providers.Configuration('config')>¶
- containers = {}¶
- httpx_client = <dependency_injector.providers.Singleton(<class 'httpx.AsyncClient'>)>¶
- inherited_providers = {}¶
- providers = {'__self__': <dependency_injector.providers.Self(<class 'ferrosoft.apps.ferrobase.containers.Container'>)>, 'config': <dependency_injector.providers.Configuration('config')>, 'httpx_client': <dependency_injector.providers.Singleton(<class 'httpx.AsyncClient'>)>, 'task_queue': <dependency_injector.providers.Factory(<class 'ferrosoft.apps.ferrobase.services.taskqueue.CheapyTaskQueue'>)>}¶
- task_queue = <dependency_injector.providers.Factory(<class 'ferrosoft.apps.ferrobase.services.taskqueue.CheapyTaskQueue'>)>¶
- wiring_config = <dependency_injector.containers.WiringConfiguration object>¶
ferrosoft.apps.ferrobase.context module¶
ASGI lifespan context managers for shared HTTP client lifecycle.
- ferrosoft.apps.ferrobase.context.httpx_lifespan_manager()[source]¶
Manage an HTTPX
AsyncClientacross the full ASGI application lifespan.Yields a state dict so that request handlers can reuse one long-lived connection pool via
request.state.httpx_clientinstead of opening a new connection per request.- Yields:
dict –
{"httpx_client": httpx.AsyncClient}
ferrosoft.apps.ferrobase.context_processors module¶
Django template context processors for ferrobase.
- ferrosoft.apps.ferrobase.context_processors.ferrobase(request)[source]¶
Inject ferrobase-wide variables into every Django template context.
- Parameters:
request – The current HTTP request.
- Returns:
- Context variables covering the active tenant, colour theme,
contact email, map config, debug flag, login URL, and subscription plan URL.
- Return type:
ferrosoft.apps.ferrobase.encodings module¶
Character-encoding detection and the catalogue of supported text encodings.
- class ferrosoft.apps.ferrobase.encodings.Encoding(name, aliases, languages)[source]¶
Bases:
objectDescriptor for a single character encoding.
- class ferrosoft.apps.ferrobase.encodings.SupportedEncodings[source]¶
Bases:
objectCatalogue of character encodings offered as user-selectable form choices.
- ferrosoft.apps.ferrobase.encodings.detect_text_encoding(file)[source]¶
Detect the character encoding of a binary file-like object.
Reads up to
_DETECTOR_BUFFER_SIZElines usingchardet’s universal detector and stops early when confidence is high enough.- Parameters:
file – A binary file-like object to inspect.
- Returns:
The detected encoding name (e.g.
"utf-8").- Return type:
ferrosoft.apps.ferrobase.filter module¶
Django-filter FilterSet definitions for ferrobase models.
- class ferrosoft.apps.ferrobase.filter.APITokenFilterSet(data=None, queryset=None, *, request=None, prefix=None)[source]¶
Bases:
FilterSetFilter API tokens by name substring or exact status.
- base_filters = {'name__icontains': <django_filters.filters.CharFilter object>, 'status': <django_filters.filters.ChoiceFilter object>}¶
- declared_filters = {}¶
- class ferrosoft.apps.ferrobase.filter.CompanySiteFilterSet(data=None, queryset=None, *, request=None, prefix=None)[source]¶
Bases:
FilterSetFilter company sites by code, name, city, street, postal code, or country.
- class Meta[source]¶
Bases:
object- fields = {'city': ['icontains'], 'code': ['istartswith'], 'country': ['exact'], 'name': ['icontains'], 'postal_code': ['istartswith'], 'street': ['icontains']}¶
- model¶
alias of
CompanySite
- base_filters = {'city__icontains': <django_filters.filters.CharFilter object>, 'code__istartswith': <django_filters.filters.CharFilter object>, 'country': <django_filters.filters.ModelChoiceFilter object>, 'name__icontains': <django_filters.filters.CharFilter object>, 'postal_code__istartswith': <django_filters.filters.CharFilter object>, 'street__icontains': <django_filters.filters.CharFilter object>}¶
- declared_filters = {}¶
- class ferrosoft.apps.ferrobase.filter.IDSequenceFilter(data=None, queryset=None, *, request=None, prefix=None)[source]¶
Bases:
FilterSetFilter ID sequence configurations by name or prefix.
- base_filters = {'name': <django_filters.filters.ChoiceFilter object>, 'prefix': <django_filters.filters.CharFilter object>}¶
- declared_filters = {}¶
- class ferrosoft.apps.ferrobase.filter.MaterialFilterSet(data=None, queryset=None, *, request=None, prefix=None)[source]¶
Bases:
FilterSetFilter materials by code prefix or name substring.
- base_filters = {'code__istartswith': <django_filters.filters.CharFilter object>, 'name__icontains': <django_filters.filters.CharFilter object>}¶
- declared_filters = {}¶
- class ferrosoft.apps.ferrobase.filter.OrganizationFilterSet(data=None, queryset=None, *, request=None, prefix=None)[source]¶
Bases:
FilterSetFilter organisations by name, contact email, or contact name.
- class Meta[source]¶
Bases:
object- fields = {'contact_email': ['icontains'], 'contact_first_name': ['icontains'], 'contact_last_name': ['icontains'], 'name': ['istartswith']}¶
- model¶
alias of
Organization
- base_filters = {'contact_email__icontains': <django_filters.filters.CharFilter object>, 'contact_first_name__icontains': <django_filters.filters.CharFilter object>, 'contact_last_name__icontains': <django_filters.filters.CharFilter object>, 'name__istartswith': <django_filters.filters.CharFilter object>}¶
- declared_filters = {}¶
- class ferrosoft.apps.ferrobase.filter.StoredFileFilterSet(data=None, queryset=None, *, request=None, prefix=None)[source]¶
Bases:
FilterSetFilter stored files by name substring.
- base_filters = {'name__icontains': <django_filters.filters.CharFilter object>}¶
- declared_filters = {}¶
- class ferrosoft.apps.ferrobase.filter.SubscriptionPlanFilterSet(data=None, queryset=None, *, request=None, prefix=None)[source]¶
Bases:
FilterSetFilter subscription plans by name or maximum tonnage range.
- class Meta[source]¶
Bases:
object- fields = ['name', 'max_tonnage']¶
- model¶
alias of
SubscriptionPlan
- base_filters = {'max_tonnage': <django_filters.filters.RangeFilter object>, 'name': <django_filters.filters.CharFilter object>}¶
- declared_filters = {'max_tonnage': <django_filters.filters.RangeFilter object>}¶
- class ferrosoft.apps.ferrobase.filter.UserFilterSet(data=None, queryset=None, *, request=None, prefix=None)[source]¶
Bases:
FilterSetFilter users by username, email address, or organisation.
- base_filters = {'email': <django_filters.filters.CharFilter object>, 'organization': <django_filters.filters.ModelChoiceFilter object>, 'username': <django_filters.filters.CharFilter object>}¶
- declared_filters = {}¶
ferrosoft.apps.ferrobase.frontend module¶
Primitives for pushing real-time UI updates over WebSocket to the frontend.
Provides a hierarchy of serialisable element types (Element, UpdateSpec,
etc.) and FrontendUIUpdater, which broadcasts update specs to a Django
Channels channel group so connected clients can apply them without a full page
reload.
- class ferrosoft.apps.ferrobase.frontend.AppendSpec(selector, elements)[source]¶
Bases:
objectSpecification for appending new elements to a CSS-selector target.
- class ferrosoft.apps.ferrobase.frontend.AppendSpecSerializer(*args, **kwargs)[source]¶
Bases:
SerializerDRF serialiser for
AppendSpec.
- class ferrosoft.apps.ferrobase.frontend.Attr(name, value=None)[source]¶
Bases:
objectRepresents HTML element attribute.
- class ferrosoft.apps.ferrobase.frontend.DeleteSpec(selector)[source]¶
Bases:
objectSpecification for removing all elements matching a CSS selector.
- class ferrosoft.apps.ferrobase.frontend.DeleteSpecSerializer(*args, **kwargs)[source]¶
Bases:
SerializerDRF serialiser for
DeleteSpec.
- class ferrosoft.apps.ferrobase.frontend.Element(name, *args, **kwargs)[source]¶
Bases:
objectElement represents HTML elements or XML elements of certain namespace (i.e. SVG).
- class ferrosoft.apps.ferrobase.frontend.ElementSerializer(*args, **kwargs)[source]¶
Bases:
SerializerDRF serialiser for
Elementthat outputs camelCase JSON for the frontend.
- class ferrosoft.apps.ferrobase.frontend.FrontendUIUpdater(*args, **kwargs)[source]¶
Bases:
objectUpdate frontend UI with new or replacing elements.
- property group_name¶
Django Channels group name this updater broadcasts to.
- update(spec, transition=None)[source]¶
Broadcast an update specification to all clients in the channel group.
- Parameters:
spec (
UpdateSpec) – The UI update to apply. Elements targeted byreplace_childrenmust carry anidattribute.transition (
Transition|None) – Optional named transition to accompany the update.
- class ferrosoft.apps.ferrobase.frontend.Namespace(*values)[source]¶
Bases:
StrEnumXML/HTML namespace identifiers used when serialising mixed-namespace elements.
- HTML = 'html'¶
- SVG = 'http://www.w3.org/2000/svg'¶
- class ferrosoft.apps.ferrobase.frontend.Position(x, y)¶
Bases:
tupleA 2D coordinate used to position UI elements (x, y).
- x¶
Alias for field number 0
- y¶
Alias for field number 1
- class ferrosoft.apps.ferrobase.frontend.Transition(transition, detail=None)[source]¶
Bases:
objectA named UI transition with an optional detail payload.
- class ferrosoft.apps.ferrobase.frontend.TransitionSerializer(*args, **kwargs)[source]¶
Bases:
SerializerDRF serialiser for
Transition.
- class ferrosoft.apps.ferrobase.frontend.UpdateSpec(replace_children=None, append_children=None, delete_elements=None)[source]¶
Bases:
objectAggregate specification describing a complete frontend UI update.
Combines element replacements, appends, and deletions into a single payload that
FrontendUIUpdater.updatebroadcasts to connected clients.
ferrosoft.apps.ferrobase.templating module¶
Utilities for rendering Django templates from raw strings.
ferrosoft.apps.ferrobase.urls module¶
URL configuration for the ferrobase application.