Skip to content

Configurable taxa lists#1094

Merged
mihow merged 43 commits intomainfrom
feat/taxa-lists
Feb 27, 2026
Merged

Configurable taxa lists#1094
mihow merged 43 commits intomainfrom
feat/taxa-lists

Conversation

@annavik
Copy link
Copy Markdown
Member

@annavik annavik commented Jan 20, 2026

Background

Will be a help for the upcoming class masking feature, but also something we have been wanting in general. This PR is still in draft mode.

Related to #997.

Missing BE stuff

  • Make it possible to add and remove taxa from taxa lists using the API ✅
  • Make it possible to access direct taxa only (exclude children) when using the API ✅
  • Getting error 500 when creating new taxa list after the BE updates in this branch ✅
  • Remove unused nested taxa endpoint taxalists/id/taxa/
  • Fix extra project_id in URL ✅
  • Decide what to do about user permissions for many2many objects, for now ✅

Missing FE stuff

  • Make sure breadcrumbs are rendered correctly ✅
  • Show taxon details from taxa list detail view without changing route ✅
  • Update logic after API updates ✅

Notes

  • When we have decided on naming, setup new tab "Collections" and add "Taxa lists" as a sub view to this tab (can happen in follow up PR)
  • Expand taxa list detail view with more columns, or is it nice to keep it simple?
  • Fetching taxa is very slow in production, can we make it quicker by not consider related occurrences BE side? Similar to what we did for captures? Or is child taxa the problem?
  • If we can return more information about taxa lists in the taxa response, it would open up for more display options and quick actions in the generic taxa view

Screenshots

List view:
Screenshot 2026-01-20 at 15 03 48

Create view:
Screenshot 2026-01-20 at 15 03 56

Edit view:
Screenshot 2026-01-20 at 15 04 11

Delete view:
Screenshot 2026-01-20 at 15 04 21

Detail view:
Screenshot 2026-01-20 at 15 04 31

Summary by CodeRabbit

  • New Features

    • Full taxa-list management: create lists, view details, and add/remove taxa individually.
  • UI / Navigation

    • Sidebar, routes, pages, dialogs, popovers, and table columns for taxa-list workflows; localized labels.
  • API / Backend

    • Taxa lists now include taxa counts, project scoping, nested taxa endpoints, automatic project association, and descendant filtering.
  • Hooks

    • New hooks to fetch taxa-list details and to add/remove taxa; list hook includes total count.
  • Permissions

    • Project-member read/write permission guard applied to taxa-list actions.
  • Tests

    • New API tests covering taxa-list taxon add/remove and validation.

annavik and others added 13 commits January 14, 2026 12:07
* Initial plan

* Add sorting, timestamps and taxa_count to TaxaList API

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: mihow <158175+mihow@users.noreply.github.com>

* Optimize taxa_count with query annotation

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: mihow <158175+mihow@users.noreply.github.com>

* Format lists with trailing commas per black style

Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: mihow <158175+mihow@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mihow <158175+mihow@users.noreply.github.com>
Co-authored-by: Anna Viklund <annamariaviklund@gmail.com>
@netlify
Copy link
Copy Markdown

netlify bot commented Jan 20, 2026

Deploy Preview for antenna-preview ready!

Name Link
🔨 Latest commit b94023d
🔍 Latest deploy log https://app.netlify.com/projects/antenna-preview/deploys/69a15aa270eb7a0009925ab7
😎 Deploy Preview https://deploy-preview-1094--antenna-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 75 (🟢 up 9 from production)
Accessibility: 89 (🟢 up 9 from production)
Best Practices: 92 (🔴 down 8 from production)
SEO: 100 (🟢 up 8 from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link
Copy Markdown

netlify bot commented Jan 20, 2026

Deploy Preview for antenna-ssec ready!

Name Link
🔨 Latest commit b94023d
🔍 Latest deploy log https://app.netlify.com/projects/antenna-ssec/deploys/69a15aa289d5700008c47adc
😎 Deploy Preview https://deploy-preview-1094--antenna-ssec.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds end-to-end taxa-list management: backend serializers, viewsets, permissions, nested routes, and tests; frontend routes, pages, hooks, components, models, and translations to list, add, and remove taxa from taxa lists.

Changes

Cohort / File(s) Summary
Backend — TaxaList serializers & input DTOs
ami/main/api/serializers.py
TaxaListSerializer now uses DefaultSerializer; adds taxa_count and projects SerializerMethodFields; adds TaxaListTaxonInputSerializer and TaxaListTaxonSerializer.
Backend — Views, filters, nested endpoints
ami/main/api/views.py, ami/main/api/...
TaxaListViewSetDefaultViewSet, annotates annotated_taxa_count, enforces require_project, auto-attaches active project on create; new TaxaListTaxonViewSet for list/create/delete_by_taxon; TaxaListFilter supports include_descendants flag.
Backend — Permissions & tests
ami/base/permissions.py, ami/main/tests.py
New IsProjectMemberOrReadOnly permission; ObjectPermission adjusted; extensive API tests for taxa-list taxa CRUD, validation, and M2M behavior added.
Backend — ML serializers & view tweaks
ami/ml/serializers.py, ami/ml/views.py
ProcessingServiceSerializer replaces write-only project with read-only projects field; ProcessingServiceViewSet adds require_project and perform_create to attach active project.
Backend — Routing
config/api_router.py
Adds nested router taxa_lists_router and registers TaxaListTaxonViewSet under /taxa/lists/{taxalist_id}/taxa/.
Frontend — Routes, constants, i18n
ui/src/app.tsx, ui/src/utils/constants.ts, ui/src/utils/language.ts
New routes and APP_ROUTES builders for taxa lists/taxa-in-list; added translation keys and strings.
Frontend — Pages & columns
ui/src/pages/taxa-lists/*, ui/src/pages/taxa-list-details/*, ui/src/pages/*
New TaxaLists and TaxaListDetails pages, column configs, pagination/sort integration, breadcrumb/sidebar updates, dialog integration for species/details.
Frontend — Add/Remove taxon components & hooks
ui/src/pages/taxa-list-details/add-taxa-list-taxon/*, ui/src/pages/taxa-list-details/remove-taxa-list-taxon/*, ui/src/data-services/hooks/taxa-lists/*
New AddTaxaListTaxon, AddTaxaListTaxonPopover, RemoveTaxaListTaxonDialog; hooks useTaxaListDetails, useAddTaxaListTaxon, useRemoveTaxaListTaxon (invalidate TAXA_LISTS & SPECIES).
Frontend — Data models & service signature changes
ui/src/data-services/models/taxa-list.ts, ui/src/data-services/models/entity.ts, ui/src/data-services/hooks/entities/useDeleteEntity.ts, ui/src/data-services/hooks/entities/utils.ts
ServerTaxaList adds taxa_count; TaxaList exposes taxaCount() and removes taxaUrl; Entity getters null-safe; convertToServerFieldValues uses project_id; useDeleteEntity signature changed to accept { collection, projectId, onSuccess? } and includes project_id query param.
Frontend — Misc UI tweaks
ui/src/components/..., ui/src/pages/species/*, ui/src/pages/occurrences/*
Localization of labels, preserved search params on links, minor prop-order and breadcrumb logic updates.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Client (UI)
    participant Auth as Auth Service
    participant API as Django API
    participant DB as Database

    UI->>Auth: include auth token
    UI->>API: POST /taxa/lists/{taxaListId}/taxa/?project_id={pid} { taxon_id }
    API->>Auth: validate token
    Auth-->>API: token OK
    API->>DB: fetch TaxaList scoped to project
    DB-->>API: TaxaList record
    API->>DB: fetch Taxon by id
    DB-->>API: Taxon record / not found
    alt taxon exists & not duplicate
        API->>DB: create M2M association (taxalist.taxa.add)
        DB-->>API: association persisted
        API-->>UI: 201 Created + taxon payload
    else duplicate or invalid
        API-->>UI: 400 Bad Request / 404 Not Found
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Suggested labels

backend

Suggested reviewers

  • mohamedelabbas1996

Poem

🐰 I hopped through serializers and routes with glee,

Nested lists now bloom for all to see.
Popovers pop, dialogs confirm the deed,
M2M connections planted like seed.
Tests hop in—now taxa-list is free.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description provides background and screenshots but lacks clear detail on changes, testing instructions, and specific deployment requirements. Add a structured 'List of Changes' section, include 'How to Test the Changes' with specific test cases, and clarify any deployment notes (e.g., database migrations, configuration changes). Ensure the description aligns with the provided template structure.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Configurable taxa lists' directly corresponds to the main feature implemented in this PR, which adds a comprehensive taxa lists feature with frontend UI and backend API integration.
Docstring Coverage ✅ Passed Docstring coverage is 85.19% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/taxa-lists

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow
Copy link
Copy Markdown
Collaborator

mihow commented Jan 22, 2026

Exciting! I will look into the missing BE parts. But would love to merge some version of this sooner and continue to improve

@annavik
Copy link
Copy Markdown
Member Author

annavik commented Jan 23, 2026

Exciting! I will look into the missing BE parts. But would love to merge some version of this sooner and continue to improve

Yes, let's try get something out! I'm fine leaving everything under "Notes" for later. I have updated the PR description with current status. We can now add and remove taxa lists members from UI! 🎉

I think these two backend issues would be nice to fix before we try get this out, at least the first one (listed under "Missing BE stuff")...

Screenshot 2026-01-23 at 12 58 39 Screenshot 2026-01-23 at 12 59 12

@annavik
Copy link
Copy Markdown
Member Author

annavik commented Jan 23, 2026

Some observations when using the new endpoints to add and remove taxa from lists:

  • The endpoint to add taxon to a list returns 200 even if the taxon is already added
  • The endpoint to remove taxon from a list returns 200 even if the taxon is not a member if the list
  • The endpoint to remove taxon from a list uses method POST, maybe should beDELETE?
  • Maybe these endpoints should follow same structure as the project member endpoints? I'm thinking we have a similar situation of "related objects"...

Just some thoughts, I'm fine leaving this as is for now! Thank you so much for the help @mohamedelabbas1996, it worked well to hook up with FE 🙏

@annavik
Copy link
Copy Markdown
Member Author

annavik commented Jan 23, 2026

Question: does it make sense to check "can update" permission for the taxa list, before rendering controls for "Add taxon" and "Remove taxon"? This is what I do know! :)

annavik and others added 5 commits January 23, 2026 15:38
… model)

Refactors taxa list endpoints to use nested routes and proper HTTP methods
while keeping the simple ManyToManyField (no through model).

**Backend changes:**
- Created TaxaListTaxonViewSet with nested routes under /taxa/lists/{id}/taxa/
- Simple serializers for input validation and output
- Removed deprecated add_taxon/remove_taxon actions from TaxaListViewSet
- Uses Django M2M .add() and .remove() methods directly

**Frontend changes:**
- Updated useAddTaxaListTaxon to POST to /taxa/ endpoint
- Updated useRemoveTaxaListTaxon to use DELETE method

**API changes:**
- POST /taxa/lists/{id}/taxa/ - Add taxon (returns 201, 400 for duplicates)
- DELETE /taxa/lists/{id}/taxa/by-taxon/{taxon_id}/ - Remove taxon (returns 204, 404 for non-existent)
- GET /taxa/lists/{id}/taxa/ - List taxa in list
- Removed POST /taxa/lists/{id}/add_taxon/ (deprecated)
- Removed POST /taxa/lists/{id}/remove_taxon/ (deprecated)

**Benefits:**
- Same API as project membership (consistency)
- No migration needed (keeps existing simple M2M)
- Proper HTTP semantics (POST=201, DELETE=204)
- RESTful nested resource design

**Tests:**
- Added comprehensive test suite (13 tests, all passing)
- Tests for CRUD operations, validation, and error cases

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@mihow
Copy link
Copy Markdown
Collaborator

mihow commented Jan 30, 2026

@annavik @mohamedelabbas1996 I updated the endpoints to use the same pattern as the project-user membership API, but no through-model to keep it a little simpler. I have in a separate PR here until I test it. Feel free to take a look before then. #1104

mihow and others added 3 commits February 17, 2026 17:09
Match the permission pattern used by other project-scoped viewsets
(ProjectViewSet, DeploymentViewSet, SiteViewSet, DeviceViewSet).
Without this, non-staff project members cannot manage taxa lists.

Co-Authored-By: Claude <noreply@anthropic.com>
The project write field was removed from the serializer in favour of
server-side assignment in ProcessingServiceViewSet.perform_create().

Co-Authored-By: Claude <noreply@anthropic.com>
Tests were sending `project` as a POST field, but the write-only field
was removed from ProcessingServiceSerializer in favor of server-side
assignment via `project_id` query parameter. Also removes dead code in
the serializer's create() override and fixes Prettier formatting.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow
Copy link
Copy Markdown
Collaborator

mihow commented Feb 18, 2026

Merge plan & interaction with #1110 and #1133

Permission fix (latest commit)

TaxaListViewSet was using ObjectPermission, which silently denied all write operations for every user. Root cause: TaxaList uses a M2M projects field, so BaseModel.get_project() returns None, and check_permission() always returns False. Switched to IsProjectMemberOrReadOnly which resolves the project via ProjectMixin.get_active_project(). Added 10 permission tests covering member CRUD, anonymous read-only, non-member rejection, and owner access.

Recommended merge order

  1. This PR (Configurable taxa lists #1094) — mergeable now, permission fix is self-contained
  2. Require project_id on list endpoints to prevent full table scans #1133 (require project_id) — minor conflict on TaxaListViewSet (require_project = True here vs require_project_for_list = False there). Resolve by adopting require_project_for_list = False since taxa lists are global M2M resources.
  3. feat: Add model-level permissions framework (rebase test - no not merge) #1110 (model-level permissions) — has merge conflicts, stale since Jan 31. After it merges:
    • Switch TaxaListViewSet back to ObjectPermission (the new check_permission() will route M2M models through model-level checks)
    • Add create_taxalist, update_taxalist, delete_taxalist to the AuthorizedUser role definition — without this, taxa list creation will break for non-superusers

Inline review comments

I've added inline comments on the two lines that will need updates when #1110 and #1133 merge.

Copy link
Copy Markdown
Collaborator

@mihow mihow left a comment

Choose a reason for hiding this comment

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

Inline notes for future merge compatibility with #1110 and #1133.

"created_at",
"updated_at",
]
permission_classes = [IsProjectMemberOrReadOnly]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

After #1110 merges: Switch back to ObjectPermission. The new check_permission() routes M2M models (where get_project_accessor() returns "projects") through check_model_level_permission(), which uses global Django permissions instead of guardian object-level perms.

This will work correctly only if create_taxalist, update_taxalist, delete_taxalist are added to the AuthorizedUser role definition in #1110. Without those, taxa list creation breaks for all non-superusers.

"updated_at",
]
permission_classes = [IsProjectMemberOrReadOnly]
require_project = True
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

After #1133 merges: Change to require_project_for_list = False.

PR #1133 makes require_project_for_list = True the default on ProjectMixin, but opts out TaxaListViewSet because taxa lists are global M2M resources (same rationale as TaxonViewSet and TagViewSet). Keep require_project = True for write operations -- only listing should be allowed without a project filter.

ObjectPermission doesn't work for M2M-to-project models because
BaseModel.get_project() returns None when get_project_accessor()
returns "projects". This caused all write operations (create, update,
delete) on taxa lists to be denied for every user.

Switch to IsProjectMemberOrReadOnly which resolves the project via
ProjectMixin.get_active_project() (from query param) instead of
through the model instance. Add 10 permission tests covering member
CRUD, anonymous read-only, non-member rejection, and owner access.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow
Copy link
Copy Markdown
Collaborator

mihow commented Feb 18, 2026

Code review

Found 3 issues:

  1. convertToServerFieldValues sends project_id instead of project, breaking entity creation for Sites, Devices, Storage Sources, and SourceImageCollections. All four serializers declare a writable project PrimaryKeyRelatedField (not project_id), so POST requests from NewEntityDialog for these entity types will fail with 400 validation errors.

...(name ? { name } : {}),
project_id: projectId,
...(customFields ?? {}),

Affected serializers at serializers.py L1602, L1618, L1635, L1228 all use project = serializers.PrimaryKeyRelatedField(...).

  1. user_permissions will always be empty for non-superusers on TaxaList responses. TaxaListSerializer extends DefaultSerializer, which calls add_object_level_permissions() -> get_project(). Since TaxaList uses a M2M to Project (via projects), get_project() explicitly returns None for M2M accessors (ami/base/models.py L143). This means the frontend will never see write permissions for taxa lists, so edit/delete buttons may be incorrectly hidden.

class TaxaListSerializer(DefaultSerializer):
taxa = serializers.SerializerMethodField()

Root cause at get_project():

antenna/ami/base/models.py

Lines 142 to 144 in 4682933

accessor = self.get_project_accessor()
if accessor == "projects" or accessor is None:
return None

  1. TaxaListTaxonViewSet.list() bypasses DRF pagination entirely, returning all taxa in a single response without limit/offset support. It also evaluates the queryset twice (queryset.count() + serializer.data). Every other list endpoint in the codebase uses LimitOffsetPaginationWithPermissions via DefaultViewSet.

antenna/ami/main/api/views.py

Lines 1686 to 1691 in 4682933

def list(self, request, taxalist_pk=None):
"""List all taxa in the taxa list."""
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True)
return Response({"count": queryset.count(), "results": serializer.data})

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@mihow
Copy link
Copy Markdown
Collaborator

mihow commented Feb 18, 2026

Additional issues from code review

Two more issues to track:

4. user_permissions always empty for non-superusers on TaxaList responses

TaxaListSerializer extends DefaultSerializer, which calls add_object_level_permissions()instance.get_user_object_permissions()get_project(). Since TaxaList uses M2M to Project (projects), get_project() returns None (ami/base/models.py L142-144), so _get_object_perms() returns [] for all non-superusers.

The frontend gates edit/delete buttons on canUpdate/canDelete which read from user_permissions (taxa-list-columns.tsx L84-99), so non-superuser project members will never see those buttons even though IsProjectMemberOrReadOnly allows them to write at the API level.

5. TaxaListTaxonViewSet.list() bypasses DRF pagination

The list() method manually returns {"count": queryset.count(), "results": serializer.data} without calling self.paginate_queryset() or self.get_paginated_response(). It also evaluates the queryset twice (once for count(), once for serializer.data).

antenna/ami/main/api/views.py

Lines 1686 to 1691 in 4682933

def list(self, request, taxalist_pk=None):
"""List all taxa in the taxa list."""
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True)
return Response({"count": queryset.count(), "results": serializer.data})

Note: the frontend currently doesn't call this endpoint (it uses useSpecies with a taxa_list_id filter on /api/v2/taxa/ instead), so this is low-impact for now but inconsistent with every other list endpoint.

🤖 Generated with Claude Code

mihow and others added 4 commits February 18, 2026 16:13
convertToServerFieldValues was sending `project_id` in the POST body,
but the serializers for Sites, Devices, Storage Sources, and
SourceImageCollections all declare `project` as the field name. Also
pass project_id as a query param so IsProjectMemberOrReadOnly can
resolve the active project for permission checks.

Co-Authored-By: Claude <noreply@anthropic.com>
TaxaList had no permission entries in Project.Permissions, so
non-superuser members could never receive create/update/delete
permissions through the guardian role system. Adds create_taxalist,
update_taxalist, and delete_taxalist to Project.Permissions, the
Meta permissions list, and the ProjectManager role.

Co-Authored-By: Claude <noreply@anthropic.com>
Models with an M2M relationship to Project (like TaxaList) return None
from get_project(), causing the default permission resolver to return
an empty list. This meant non-superuser members never saw edit/delete
buttons despite having the correct role permissions.

Adds add_m2m_object_permissions() to ami/base/permissions.py, which
resolves permissions against the active project from the request
context. TaxaListSerializer now calls this instead of the default
path. The function is designed to be reused by other M2M-to-Project
serializers (Taxon, Pipeline, ProcessingService) as part of #1120.

Co-Authored-By: Claude <noreply@anthropic.com>
The UI fetches taxa for a list via the main /taxa/ endpoint with a
taxa_list_id filter, not the nested /taxa/lists/{id}/taxa/ list
endpoint. Remove the custom list() method and corresponding tests.
The viewset now only provides create (POST) and delete (DELETE).

Co-Authored-By: Claude <noreply@anthropic.com>
mihow and others added 2 commits February 27, 2026 00:31
Merge main into feat/taxa-lists. The only conflict was in ui/src/app.tsx
where the taxa-lists routes needed to coexist with the collections-to-
capture-sets rename from main. Dropped a stale CollectionDetails route
reference (component doesn't exist).

Co-Authored-By: Claude <noreply@anthropic.com>
Use STRING.SELECT_TAXON_PLACEHOLDER and STRING.LOADING_DATA instead of
hardcoded English strings in taxa list detail pages.

Co-Authored-By: Claude <noreply@anthropic.com>
@mihow
Copy link
Copy Markdown
Collaborator

mihow commented Feb 27, 2026

All 3 issues from this review are now resolved:

  1. project_idproject — Fixed in 312e362 (fix: send project instead of project_id in entity creation requests)
  2. user_permissions empty for M2M models — Fixed in 3d2eb1e (fix: resolve user_permissions for M2M-to-Project models)
  3. TaxaListTaxonViewSet.list() bypasses pagination — Removed in d0c8c24 (refactor: remove unused list endpoint from TaxaListTaxonViewSet)

@mihow
Copy link
Copy Markdown
Collaborator

mihow commented Feb 27, 2026

Both issues from this comment are also resolved:

4. user_permissions always empty for non-superusers — Fixed in 3d2eb1e via add_m2m_object_permissions() in TaxaListSerializer.get_permissions(), which resolves the project from the request context instead of relying on get_project().

5. TaxaListTaxonViewSet.list() bypasses DRF pagination — The endpoint was removed entirely in d0c8c24 since the frontend uses useSpecies with a taxa_list_id filter on /api/v2/taxa/ instead.

Copy link
Copy Markdown
Collaborator

@mihow mihow left a comment

Choose a reason for hiding this comment

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

Tested once more with different user roles and I think it's ready to go!

@mihow mihow merged commit 0487cf9 into main Feb 27, 2026
7 of 9 checks passed
@mihow mihow deleted the feat/taxa-lists branch February 27, 2026 09:53
mihow added a commit that referenced this pull request Apr 4, 2026
The serializer had a write-only `project` PrimaryKeyRelatedField that was
passed through to ProcessingService.objects.create(), but ProcessingService
uses a M2M `projects` field, not a FK. This caused a TypeError on create.

The field was left behind when #1094 moved project assignment to
perform_create (which uses get_active_project from the query string).
Remove the field entirely to match the TaxaList pattern: no writable
project field on the serializer, M2M handled in perform_create.

Also rename `project` to `project_id` in the frontend's generic
convertToServerFieldValues to follow the codebase naming convention
(ID values use _id suffix).

Closes #1209

Co-Authored-By: Claude <noreply@anthropic.com>
mihow added a commit that referenced this pull request Apr 4, 2026
…1210)

* fix: remove stale project field from ProcessingServiceSerializer (#1209)

The serializer had a write-only `project` PrimaryKeyRelatedField that was
passed through to ProcessingService.objects.create(), but ProcessingService
uses a M2M `projects` field, not a FK. This caused a TypeError on create.

The field was left behind when #1094 moved project assignment to
perform_create (which uses get_active_project from the query string).
Remove the field entirely to match the TaxaList pattern: no writable
project field on the serializer, M2M handled in perform_create.

Also rename `project` to `project_id` in the frontend's generic
convertToServerFieldValues to follow the codebase naming convention
(ID values use _id suffix).

Closes #1209

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: remove unnecessary project pop from ProcessingService create view

DRF ignores unknown fields in request body, so no need to strip
`project` before passing to the serializer.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: revert project_id rename in convertToServerFieldValues

Site, Device, and StorageSource serializers still expect `project`
(not `project_id`) as a writable PrimaryKeyRelatedField in the request
body. The ProcessingService fix is backend-only — the serializer no
longer declares a `project` field, so DRF silently ignores it.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

5 participants