Skip to content

Organizations App Reference

The Organizations app provides multi-tenant functionality with role-based access control (RBAC). Users can belong to multiple organizations, each with its own settings, members, and data. This app is the foundation that all other apps depend on for permission checks and data scoping.

Overview

Location: app/organizations/

Purpose: Multi-tenant organization management with RBAC

Key Models: Organization, OrganizationMembership, OrganizationInvitation, OrganizationSettings

Used By: All other apps depend on this app for permission checks, org-scoped data filtering, and permission mixins.


File Structure

organizations/
├── models.py                # 4 models
├── views/                   # Views package (3 modules)
│   ├── __init__.py          # Re-exports all 16 views
│   ├── organization_views.py    # 5 CBVs + 3 FBVs (CRUD, switching, settings)
│   ├── member_views.py          # 3 FBVs (member list, remove, role change)
│   └── invitation_views.py      # 5 FBVs (send, list, accept, decline, my)
├── forms.py                 # OrganizationSettingsForm
├── urls.py                  # 16 URL patterns
├── signals.py               # Auto-create default org on user creation
├── middleware.py             # OrganizationMiddleware + switch_organization()
├── utils.py                 # 11 permission utility functions
├── mixins.py                # OrgPermissionMixin, DelegatedPermissionMixin
├── admin.py                 # Django admin config (4 model admins + 1 inline)
├── services/
│   ├── __init__.py
│   └── numbering_service.py # Charge/bucket numbering
└── templates/organizations/ # 13 HTML templates

Models

See Database Models - Organizations App for complete field reference.

Organization

Multi-tenant entity. Fields include name, org_type (personal/family/cooperative/commercial/club), description, logo (resized to 300x300px), and active status.

Key Methods:

  • get_owners() -- QuerySet of owner users
  • get_admins() -- QuerySet of admin and owner users
  • user_is_member(user) -- bool
  • user_role(user) -- role string or None

OrganizationMembership

User-Organization relationship with role-based access. Enforces a unique constraint on (organization, user).

Roles: owner, admin, member, viewer

Key Methods:

  • can_manage_members() -- owner/admin only
  • can_delete_organization() -- owner only
  • can_edit_resources() -- owner/admin/member
  • can_delete_resources() -- owner/admin only

OrganizationInvitation

Email invitation system with UUID tokens, 7-day expiration, and status tracking (pending/accepted/declined/expired).

Status Transitions: pending can move to accepted, declined, or expired. All three are terminal states.

Key Methods:

  • is_valid() -- checks if pending and not expired; auto-marks as expired if past expiration
  • accept(user) -- creates membership and marks as accepted
  • decline() -- marks as declined
  • effective_status -- property that computes true status including implicit expiration

OrganizationSettings

Per-organization preferences: date format, timezone, unit system, language, branding colors (primary/secondary), MFA policy, email notification preferences, and charge/bucket numbering templates.


Middleware

OrganizationMiddleware

Location: app/organizations/middleware.py

Adds organization context to every request:

Attribute Description
request.current_organization Active organization (or None for "all orgs" mode)
request.user_organizations All user's organizations (QuerySet)
request.org_colors Organization branding colors (dict with primary and secondary keys)

Organization Selection Priority:

  1. Session variable current_organization_id (user switched orgs or selected "all")
  2. First organization by membership join date
  3. None (not authenticated)

When the session value is explicitly set to None, the user sees data from all their organizations. When the session variable is absent, the system defaults to the first organization.

switch_organization() -- Helper function to switch current organization via session. Pass None to switch to the "all organizations" view.


Permission Utilities

Location: app/organizations/utils.py

11 functions providing centralized permission checks:

get_user_role(user, organization)           # Returns role or None
get_user_organizations(user)                # Returns QuerySet
user_is_member(user, organization)          # Returns bool
user_can_view(user, resource)               # Returns bool
user_can_create(user, organization)         # Returns bool
user_can_edit(user, resource)               # Returns bool
user_can_delete(user, resource)             # Returns bool
user_can_manage_members(user, organization) # Returns bool
user_can_delete_org(user, organization)     # Returns bool
user_can_change_org_settings(user, org)     # Returns bool
filter_by_user_organizations(queryset, user, request) # Returns filtered QuerySet

Permission Matrix:

Action Owner Admin Member Viewer
View resources Yes Yes Yes Yes
Create resources Yes Yes Yes No
Edit own resources Yes Yes Yes No
Edit all resources Yes Yes No No
Delete resources Yes Yes No No
Manage members Yes Yes No No
Change settings Yes Yes No No
Delete organization Yes No No No

See Permissions System for detailed documentation.


Permission Mixins

Location: app/organizations/mixins.py

OrgPermissionMixin

Reusable model mixin for organization-based permission checking. Add it to any model that has an organization ForeignKey and optionally a created_by field.

Methods:

  • user_can_view(user) -- any active organization member can view
  • user_can_edit(user) -- admins/owners can edit all; members can edit own records
  • user_can_delete(user) -- only admins/owners can delete

Configuration: Set the permission_owner_field class attribute to the name of the field that stores the record creator (default: 'created_by').

Used by: QueenBreeding, ColonySplit, InventoryItem, InventoryTransaction, ProductSale, WarehouseLocation

DelegatedPermissionMixin

Mixin for child models that delegate permission checks to a parent model (e.g., status change records that inherit permissions from their parent entity).

Configuration: Set the permission_delegate_field class attribute to the name of the ForeignKey pointing to the parent.

Used by: BreedingStatusChange (delegates to breeding), SplitStatusChange (delegates to split)


Views

Organization CRUD

View Type Purpose
OrganizationListView CBV List all orgs user belongs to, with member counts
OrganizationDetailView CBV Org details, members, hive/activity statistics
OrganizationCreateView CBV Create org, auto-create owner membership for creator
OrganizationUpdateView CBV Update org details (name, type, description, logo)
OrganizationDeleteView CBV Delete with cascade warning showing dependent data counts
switch_org_view FBV Switch to a different organization
switch_to_all_orgs_view FBV Switch to "all organizations" view
organization_settings_view FBV View and update organization settings

Member Management

View Type Purpose
member_list_view FBV List active members with roles
member_remove_view FBV Remove member (prevents self-removal and owner removal by non-owners)
member_role_change_view FBV Change member role (only owners can promote to owner)

Invitation Management

View Type Purpose
invitation_send_view FBV Send email invitation, validate duplicates and existing members
invitation_list_view FBV List all invitations for an organization
invitation_accept_view FBV Accept invitation, verify email match, create membership
invitation_decline_view FBV Decline invitation, verify email match
my_invitations_view FBV View valid pending invitations for current user

Forms

OrganizationSettingsForm

Location: app/organizations/forms.py

Form for editing organization settings.

Fields: date_format, time_zone, unit_system, language, primary_color, secondary_color, email notification toggles, charge/bucket numbering format fields (including custom format inputs).

Widgets: HTML5 color pickers for branding colors, select dropdowns for numbering format, text inputs for custom numbering templates.

Validation: Custom charge and bucket format validation via NumberingValidator when the "custom" format option is selected.


Services

NumberingService

Location: app/organizations/services/numbering_service.py

Generates charge and bucket identifiers using configurable templates.

Key Methods:

  • generate_charge_identifier(organization, honey_type, harvest_year) -- returns str
  • generate_bucket_number(organization, charge_identifier) -- returns str

Uses select_for_update() for thread-safe sequence incrementing.

Tokens: {YEAR}, {TYPE}, {LABEL}, {SEQ} for charges; {CHARGE}, {SEQ}, {EIMER}, {BUCKET} for buckets.

NumberingValidator

Validates custom numbering templates. Checks token existence, format specs, and uniqueness requirements.


Signals

create_default_organization

  • Trigger: post_save on User (creation only)
  • Action: Auto-creates a personal organization named "{username}'s Beekeeping" with owner membership for every new user
  • Purpose: Ensures all data belongs to an organization from the start

URL Patterns

organizations/                                → OrganizationListView (organization-list)
organizations/create/                         → OrganizationCreateView (organization-create)
organizations/<pk>/                           → OrganizationDetailView (organization-detail)
organizations/<pk>/edit/                      → OrganizationUpdateView (organization-update)
organizations/<pk>/delete/                    → OrganizationDeleteView (organization-delete)
organizations/<pk>/switch/                    → switch_org_view (organization-switch)
organizations/switch/all/                     → switch_to_all_orgs_view (organization-switch-all)
organizations/<pk>/members/                   → member_list_view (member-list)
organizations/<pk>/members/<user_id>/remove/  → member_remove_view (member-remove)
organizations/<pk>/members/<user_id>/change-role/ → member_role_change_view (member-role-change)
organizations/<pk>/invite/                    → invitation_send_view (invitation-send)
organizations/<pk>/invitations/               → invitation_list_view (invitation-list)
organizations/invitations/accept/<token>/     → invitation_accept_view (invitation-accept)
organizations/invitations/decline/<token>/    → invitation_decline_view (invitation-decline)
organizations/my-invitations/                 → my_invitations_view (my-invitations)
organizations/<pk>/settings/                  → organization_settings_view (organization-settings)

See Also