Allow multiple projects/categories/lab priorities per entry - #22
Merged
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the WorkItem taxonomy model and surrounding UI/API surfaces to support multi-select classification for projects, categories, and lab priorities, and extends report filtering to accept multiple selections (match-any semantics).
Changes:
- Convert
WorkItem.project/category/lab_prioritysingle FKs toManyToManyrelations with a data-migrating schema change. - Update entry form + list/detail/manage/report templates, report filters, and exporters to display multiple taxonomy values.
- Update JSON entry-create API to accept plural lists (while still accepting legacy singular keys) and add/adjust test coverage.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_reports.py | Adjust report tests for multi-select filters and add match-any coverage. |
| tests/test_entries.py | Update entry CRUD tests for M2M taxonomy fields; add multi-project create + required validation coverage. |
| tests/test_audit.py | Update audit tests to create entries with M2M taxonomy relations. |
| tests/test_api_entries.py | Add new API tests covering backward-compatible multi-select payloads. |
| tests/test_accounts_permissions.py | Update permission test setup to assign projects/categories via M2M. |
| apps/reports/views.py | Switch report queryset loading/filtering to prefetch M2M taxonomy and apply project-scope filtering via M2M. |
| apps/reports/templates/reports/partials/_preview.html | Render multiple projects/categories in the preview table. |
| apps/reports/templates/reports/index.html | Replace single-select report filters with multi-select controls and preserve functional-lead scoping behavior. |
| apps/reports/filters.py | Convert taxonomy filters to ModelMultipleChoiceFilter (match-any). |
| apps/reports/exporters.py | Export projects/categories as comma-joined strings and prefetch M2M taxonomy. |
| apps/entries/views.py | Update entry list/manage/detail querysets and filters for M2M taxonomy fields. |
| apps/entries/templates/entries/manage.html | Render multiple projects in the manage table. |
| apps/entries/templates/entries/list.html | Render multiple projects/categories in “My Entries”. |
| apps/entries/templates/entries/form.html | Replace single-select widgets with multi-selects and adjust layout/header placement. |
| apps/entries/templates/entries/detail.html | Render multiple projects/categories and optionally lab priorities on the detail page. |
| apps/entries/models.py | Replace FK taxonomy fields with M2M fields; remove FK-based indexes. |
| apps/entries/migrations/0008_multiselect_taxonomy.py | Add M2M fields, backfill from legacy FK columns, then drop the old fields/indexes. |
| apps/entries/forms.py | Update ModelForm fields/querysets and apply consistent multi-select widget styling. |
| apps/entries/api_views.py | Accept singular-or-list taxonomy payloads and persist them via M2M sets; return plural lists plus legacy singular keys for project/category. |
| apps/entries/admin.py | Update admin list display/filters for M2M taxonomy and add filter_horizontal for new relations. |
| apps/core/views.py | Update dashboard querysets to prefetch M2M taxonomy fields. |
| apps/core/templates/core/dashboard.html | Render multiple projects/categories for recent entries on the dashboard. |
| apps/core/templates/core/api.html | Update API docs to describe plural taxonomy keys (and legacy behavior). |
| apps/audit/signals.py | Track taxonomy changes via m2m_changed handlers instead of FK tracked fields. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+44
to
+49
| qs = WorkItem.objects.select_related('author').prefetch_related('projects', 'categories', 'tags') | ||
| if group_scope is not None: | ||
| # Prefer explicit WorkItem.group; fall back to author.group only when entry has no group set. | ||
| qs = qs.filter(Q(group__in=group_scope) | Q(group__isnull=True, author__group__in=group_scope)) | ||
| if project_scope is not None: | ||
| qs = qs.filter(project__in=project_scope) | ||
| qs = qs.filter(projects__in=project_scope).distinct() |
Comment on lines
15
to
+16
| readonly_fields = ('created_at', 'updated_at') | ||
|
|
Comment on lines
+80
to
+84
| def test_missing_project_is_an_error(self, client, token, category): | ||
| resp = _post(client, token, {'categories': ['scientific']}) | ||
| assert resp.status_code == 400 | ||
| assert 'projects' in resp.json()['errors'] | ||
|
|
Comment on lines
+12
to
+21
| def _new_entry(project=None, category=None, lab_priority=None, **kwargs): | ||
| """Create a WorkItem and set its project/category/lab_priority M2M relations.""" | ||
| item = WorkItem.objects.create(**kwargs) | ||
| if project is not None: | ||
| item.projects.set(project if isinstance(project, (list, tuple)) else [project]) | ||
| if category is not None: | ||
| item.categories.set(category if isinstance(category, (list, tuple)) else [category]) | ||
| if lab_priority is not None: | ||
| item.lab_priorities.set(lab_priority if isinstance(lab_priority, (list, tuple)) else [lab_priority]) | ||
| return item |
Convert WorkItem.project / category / lab_priority from single foreign keys to many-to-many relations (projects / categories / lab_priorities) so an entry can be classified under several at once, and let the Reports page filter by multiple values (match ANY). A data migration copies each existing single value into the new M2M before dropping the old columns, so no data is lost. - model + migration 0008: FK -> M2M with forward/backward data copy. - entry form: multi-select widgets; >=1 project and >=1 category still required; lab priorities optional. Field order is now Projects, Categories, Lab Priorities, Group. - reports: ModelMultipleChoiceFilter (OR) for the three menus. - displays (detail/list/manage/reports/dashboard/exporters/PDF/AI) join multiple names with ", ". - JSON API: backward-compatible — accepts a single slug or a list, and returns plural lists plus the legacy singular keys (first value). - audit: project/category/lab_priority tracked via m2m_changed handlers. - tests: fixtures updated for M2M; new multi-select form, reports filter, and API back-compat coverage (205 passing). Also nudges the markdown cheat-sheet panel on the entry form to align with the top of the form card (moves the page header above the columns). Closes #15 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
normanajn
force-pushed
the
feature/multiselect-taxonomy
branch
from
June 25, 2026 18:44
84bd162 to
0a73a6e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lets an entry be classified under multiple projects, categories, and lab priorities, and lets the Reports page filter by multiple values. Addresses #15 (multi-select in both the entry form and the report page).
Schema
WorkItem.project / category / lab_priority(single FKs) →projects / categories / lab_prioritiesManyToMany.0008_multiselect_taxonomyadds the M2M fields, copies each existing single value across, then drops the old FKs — no data loss (verified on the existing 31 entries; reverse migration collapses back to the first value).Entry form
Reports
ModelMultipleChoiceFilter(match ANY) for Project / Category / Lab Priority; multi-selects with "none = all" semantics; functional-lead project-scope locking preserved.Displays
,(export column names unchanged).JSON API (backward-compatible)
projects/categories/lab_priorities(legacy singular keys still work).project/category(first value). API docs page updated.Audit
m2m_changedhandlers (like tags).Tests
test_api_entries.pyfor API back-compat. 205 passing.Note
main, so it does not include the still-open Entry Type work (Add Entry Type taxonomy to entries and reports #21); whichever of Add option to select multiple items in pull down menus #15/Add Entry Type taxonomy to entries and reports #21 merges second will need a small conflict resolution.Closes #15
🤖 Generated with Claude Code