Skip to content

Feature organization model - #6080

Open
nairaj2 wants to merge 10 commits into
learningequality:unstablefrom
nairaj2:feature_organization_model
Open

Feature organization model#6080
nairaj2 wants to merge 10 commits into
learningequality:unstablefrom
nairaj2:feature_organization_model

Conversation

@nairaj2

@nairaj2 nairaj2 commented Aug 4, 2026

Copy link
Copy Markdown

Summary

Adds backend API and controller support for the Organization and OrganizationRole models.

This change provides:

  • Organization create, read, update, and delete operations
  • Organization membership listing
  • Organization role updates
  • Permission checks for organization admins, editors, and viewers
  • Pagination and filtering for organization-related endpoints
  • Automated API tests covering organization management, membership management, role updates, and permission enforcement

Organization administrators can manage organization settings, memberships, and roles. Editors can view memberships but cannot manage organization settings, memberships, or roles. Viewers have read-only access to organization resources.

Frontend changes and data model changes are outside the scope of this PR.

References

References
Closes #5967
Builds on #5962

Reviewer guidance

Run the organization API tests with:

pytest -q contentcuration/contentcuration/tests/test_organization.py

Reviewers can verify that:

  • Authenticated users can create organizations
  • The organization creator is assigned the administrator role
  • Users can only access organizations permitted by their membership and role
  • Only organization administrators can update organization settings
  • Organization members can list memberships
  • Only organization administrators can update or remove memberships and roles
  • Editors and viewers cannot manage organization membership or roles
  • Organization and membership endpoints support pagination and filtering
  • Unauthorized requests are rejected appropriately

This PR does not include frontend changes.

AI usage

Used AI to help review the existing Studio API patterns and draft portions of the organization viewsets and tests.

I reviewed and edited the generated code to align it with Studio's ValuesViewset, serializer, routing, pagination, and permission conventions. I also ran the organization tests locally and used the failures to correct routing, response formatting, authentication expectations, and role-permission behavior.

@learning-equality-bot

Copy link
Copy Markdown

👋 Hi @nairaj2, thanks for contributing!

For the review process to begin, please verify that the following is satisfied:

  • Contribution is aligned with our contributing guidelines

  • Pull request description has correctly filled AI usage section & follows our AI guidance:

    AI guidance

    State explicitly whether you didn't use or used AI & how.

    If you used it, ensure that the PR is aligned with Using AI as well as our DEEP framework. DEEP asks you:

    • Disclose — Be open about when you've used AI for support.
    • Engage critically — Question what is generated. Review code for correctness and unnecessary complexity.
    • Edit — Review and refine AI output. Remove unnecessary code and verify it still works after your edits.
    • Process sharing — Explain how you used the AI so others can learn.

    Examples of good disclosures:

    "I used Claude Code to implement the component, prompting it to follow the pattern in ComponentX. I reviewed the generated code, removed unnecessary error handling, and verified the tests pass."

    "I brainstormed the approach with Gemini, then had it write failing tests for the feature. After reviewing the tests, I used Claude Code to generate the implementation. I refactored the output to reduce verbosity and ran the full test suite."

Also check that issue requirements are satisfied & you ran pre-commit locally.

Pull requests that don't follow the guidelines will be closed.

Reviewer assignment can take up to 2 weeks.

@rtibbles
rtibbles requested a review from rtibblesbot August 4, 2026 18:16
@rtibblesbot

rtibblesbot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🔵 Review posted

Last updated: 2026-08-04 18:38 UTC

@learning-equality-bot

Copy link
Copy Markdown

📢✨ Before we assign a reviewer, we'll turn on @rtibblesbot to pre-review. Its comments are generated by an LLM, and should be evaluated accordingly.

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #6080 — blockers inline; newest commits unreviewed.


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Ran a phased review pipeline over the pull request diff:

  • Classified the diff to select review passes (core, frontend, backend) and whether manual QA was required
  • Core review pass checked correctness, design, architecture, testing, completeness, and DRY/SRP/Rule-of-Three principles
  • Specialized frontend/backend review passes applied framework-specific lenses where those files changed
  • For UI changes: manual QA and an accessibility audit against a live dev server, when available
  • Checked CI status and linked issue acceptance criteria
  • Synthesized one review from those passes and chose the verdict from the findings, CI status, and QA evidence

class OrganizationMemberSerializer(BulkModelSerializer):
"""
Write serializer for updating OrganizationRole membership records.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: no create route; Invitation.accept() (models.py:3738) is channel-only. Sole OrganizationRole writer: perform_create (179). #5967 membership/roles unmet.

def _is_site_admin(user):
return bool(getattr(user, "is_admin", False))


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: Channel.filter_edit_queryset/filter_view_queryset (models.py:1216, 1235) ignore OrganizationRole — no Channel.organization access (#5967). Add Exists(OrganizationRole).

from rest_framework import serializers
from rest_framework.exceptions import PermissionDenied
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAuthenticated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: pre-commit fails on head (reorder-python-imports, black); Linting job fails.



class OrganizationMemberFilter(FilterSet):
organization = CharFilter(field_name="organization_id")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: User.id is an integer PK; ?user=abc → 500 (reproduced). Use NumberFilter, UUIDFilter.

raise ValidationError(
"An organization must have at least one active admin."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: serialize_object() re-reads get_queryset() (251, active-only): self-deactivation commits, then 404s (reproduced).

"created_at",
"updated_at",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: model-level filter_view_queryset/filter_edit_queryset (base.py:585-599); repeated at 162, 274, 293; _require_admin unreachable.


def perform_update(self, serializer):
with transaction.atomic():
membership = (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: select_for_update(of=("self",)); lock order against 334 deadlocks.



class OrganizationAPITestCase(BaseAPITestCase):
"""Shared organization API fixtures and URL helpers."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: convention: tests/viewsets/ + StudioAPITestCase. BaseAPITestCase.setUp adds an unused channel tree.

raise PermissionDenied(
"Only active organization admins may manage membership."
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: covers demotion, deactivation, deletion; locked in-transaction; tested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Organization API / Controller Support

2 participants