[OSDEV-2356] Add PartnerFieldGroup model and API endpoint#891
[OSDEV-2356] Add PartnerFieldGroup model and API endpoint#891protsack-stephan merged 11 commits intomainfrom
PartnerFieldGroup model and API endpoint#891Conversation
📝 WalkthroughWalkthroughAdds PartnerFieldGroup model and nullable FK on PartnerField, registers admin/export updates, exposes a read-only paginated Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant CDN as CDN (CloudFront)
participant API as Django API
participant DB as Database
Client->>CDN: GET /api/partner-field-groups/?cursor...
CDN->>API: Forward request (cache miss or origin request)
API->>DB: Query PartnerFieldGroup with prefetch partner_fields (order)
DB-->>API: Return groups + partner_fields
API-->>CDN: Return paginated response (Cursor pagination headers)
CDN-->>Client: Serve response (cached per TTL variables)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
src/django/api/serializers/partner_field_group/partner_field_group_serializer.py (1)
27-37: Prefer tuple forMeta.fieldsto avoid mutable class-attribute warnings.Using a tuple here avoids RUF012-style mutable default concerns and is the common serializer pattern.
✅ Suggested fix
- fields = [ + fields = ( "uuid", "name", "order", "icon_file", "description", "helper_text", "partner_fields", "created_at", "updated_at", - ] + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/django/api/serializers/partner_field_group/partner_field_group_serializer.py` around lines 27 - 37, In the PartnerFieldGroupSerializer's Meta class, change the mutable list assigned to Meta.fields to an immutable tuple to avoid mutable class-attribute warnings (RUF012); locate the Meta class inside PartnerFieldGroupSerializer and replace the fields = [...] list with fields = ("uuid", "name", "order", "icon_file", "description", "helper_text", "partner_fields", "created_at", "updated_at") so the serializer uses a tuple instead of a list.src/django/api/views/partner_field_groups/partner_field_groups_view_set.py (1)
24-34: Consider explicitly setting permission classes.The docstring states "Available for all users," but no
permission_classesis defined. DRF will use the default from settings, which may or may not beAllowAny. For clarity and to ensure the intended public access, consider explicitly setting permissions.💡 Suggested explicit permission
+from rest_framework.permissions import AllowAny + class PartnerFieldGroupsViewSet(ReadOnlyModelViewSet): """ Allows listing of the partner field groups. Available for all users. """ queryset = PartnerFieldGroup.objects.prefetch_related( "partner_fields" ).all() serializer_class = PartnerFieldGroupSerializer pagination_class = PartnerFieldGroupCursorPagination + permission_classes = [AllowAny]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/django/api/views/partner_field_groups/partner_field_groups_view_set.py` around lines 24 - 34, The view set claims to be public but doesn't set explicit permissions; modify PartnerFieldGroupsViewSet to declare permission_classes = [AllowAny] and import AllowAny from rest_framework.permissions so intent is explicit and not reliant on project defaults; update the class definition near queryset/serializer_class to include this attribute.src/django/api/admin.py (1)
456-456: Consider adding a custom ModelAdmin for PartnerFieldGroup.
PartnerFieldGroupis registered without a custom admin class. Consider adding one to improve the admin experience with features likelist_display,search_fields, andordering.💡 Suggested custom admin class
class PartnerFieldGroupAdmin(admin.ModelAdmin): list_display = ("name", "order", "created_at", "updated_at") search_fields = ("name",) ordering = ("order",) readonly_fields = ("uuid", "created_at", "updated_at") admin_site.register(PartnerFieldGroup, PartnerFieldGroupAdmin)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/django/api/admin.py` at line 456, PartnerFieldGroup is registered without a custom admin class — create a PartnerFieldGroupAdmin (subclassing admin.ModelAdmin) and register it with admin_site.register(PartnerFieldGroup, PartnerFieldGroupAdmin); in the admin class add list_display = ("name", "order", "created_at", "updated_at"), search_fields = ("name",), ordering = ("order",) and readonly_fields = ("uuid", "created_at", "updated_at") to improve list view, searchability and prevent edits to audit fields.src/django/api/migrations/0201_add_partnerfieldgroup_alter_partnerfield.py (1)
36-42: Consider adding an index on theorderfield.The model uses
ordering = ["order"]in Meta, and the viewset paginates by this field. For larger datasets, an index would improve query performance.💡 Suggested index addition
( "order", models.IntegerField( default=0, help_text="Order for the partner field group in the UI.", + db_index=True, ), ),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/django/api/migrations/0201_add_partnerfieldgroup_alter_partnerfield.py` around lines 36 - 42, Update the migration so the "order" IntegerField is indexed: modify the field definition in the migration (the models.IntegerField(...) for "order") to include db_index=True (e.g., models.IntegerField(default=0, db_index=True, help_text="...")), or alternatively add an AddIndex operation for the PartnerFieldGroup model targeting the "order" column; ensure the change targets the same migration that defines the field so the database will create the index when applied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@deployment/terraform/variables.tf`:
- Around line 70-128: Several variable descriptions (e.g.,
api_partner_field_groups_cache_default_ttl,
api_partner_field_groups_cache_max_ttl, api_partner_fields_cache_default_ttl,
api_partner_fields_cache_max_ttl, api_contributors_cache_default_ttl,
api_contributors_cache_max_ttl, api_contributor_lists_sorted_cache_default_ttl,
api_contributor_lists_sorted_cache_max_ttl,
api_parent_companies_cache_default_ttl, api_parent_companies_cache_max_ttl)
still reference "OS ID detail endpoint" due to copy/paste; update each
description to accurately describe the TTL as applying to the list/collection or
endpoint type they control (for example: "Default TTL (seconds) for partner
field groups list endpoint" or "Max TTL (seconds) for API contributors list
endpoints"), ensuring wording matches whether the variable is for a
single-resource detail vs. a list/collection endpoint.
In `@src/django/api/tests/test_partner_field_groups_view_set.py`:
- Around line 55-61: The test test_returns_200_for_user is making an
unauthenticated request — update it to authenticate the test client first (e.g.
call self.client.force_authenticate(user=self.user) or set credentials/token on
self.client) before calling self.client.get(self.url) so the request exercises
the authenticated path; ensure you de-authenticate after if needed or rely on
test isolation. Reference the test method name test_returns_200_for_user and the
test client usages (self.client.get) to locate where to insert the
authentication step.
- Around line 117-118: In the test loop comparing sorted_uuids and
response_uuids, change the zip() call to include the explicit strict=True
parameter to satisfy lint rule B905; update the loop that binds sorted_uuid and
response_uuid (where zip(sorted_uuids, response_uuids) is used) to
zip(sorted_uuids, response_uuids, strict=True) so the test will error if the
iterables differ in length.
---
Nitpick comments:
In `@src/django/api/admin.py`:
- Line 456: PartnerFieldGroup is registered without a custom admin class —
create a PartnerFieldGroupAdmin (subclassing admin.ModelAdmin) and register it
with admin_site.register(PartnerFieldGroup, PartnerFieldGroupAdmin); in the
admin class add list_display = ("name", "order", "created_at", "updated_at"),
search_fields = ("name",), ordering = ("order",) and readonly_fields = ("uuid",
"created_at", "updated_at") to improve list view, searchability and prevent
edits to audit fields.
In `@src/django/api/migrations/0201_add_partnerfieldgroup_alter_partnerfield.py`:
- Around line 36-42: Update the migration so the "order" IntegerField is
indexed: modify the field definition in the migration (the
models.IntegerField(...) for "order") to include db_index=True (e.g.,
models.IntegerField(default=0, db_index=True, help_text="...")), or
alternatively add an AddIndex operation for the PartnerFieldGroup model
targeting the "order" column; ensure the change targets the same migration that
defines the field so the database will create the index when applied.
In
`@src/django/api/serializers/partner_field_group/partner_field_group_serializer.py`:
- Around line 27-37: In the PartnerFieldGroupSerializer's Meta class, change the
mutable list assigned to Meta.fields to an immutable tuple to avoid mutable
class-attribute warnings (RUF012); locate the Meta class inside
PartnerFieldGroupSerializer and replace the fields = [...] list with fields =
("uuid", "name", "order", "icon_file", "description", "helper_text",
"partner_fields", "created_at", "updated_at") so the serializer uses a tuple
instead of a list.
In `@src/django/api/views/partner_field_groups/partner_field_groups_view_set.py`:
- Around line 24-34: The view set claims to be public but doesn't set explicit
permissions; modify PartnerFieldGroupsViewSet to declare permission_classes =
[AllowAny] and import AllowAny from rest_framework.permissions so intent is
explicit and not reliant on project defaults; update the class definition near
queryset/serializer_class to include this attribute.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
deployment/terraform/cdn.tfdeployment/terraform/variables.tfdoc/release/RELEASE-NOTES.mdsrc/django/api/admin.pysrc/django/api/migrations/0201_add_partnerfieldgroup_alter_partnerfield.pysrc/django/api/models/__init__.pysrc/django/api/models/partner_field.pysrc/django/api/models/partner_field_group.pysrc/django/api/serializers/partner_field_group/partner_field_group_serializer.pysrc/django/api/tests/test_partner_field_groups_view_set.pysrc/django/api/views/__init__.pysrc/django/api/views/partner_field_groups/partner_field_groups_view_set.pysrc/django/oar/urls.py
There was a problem hiding this comment.
♻️ Duplicate comments (2)
src/django/api/tests/test_partner_field_groups_view_set.py (2)
52-58:⚠️ Potential issue | 🟡 MinorAuthenticated-path test still does not authenticate.
test_returns_200_for_userat Line 54 performs the same unauthenticated request as the anonymous test, so it doesn’t exercise authenticated behavior.✅ Suggested fix
+from django.contrib.auth import get_user_model from rest_framework import status from rest_framework.test import APITestCase @@ def test_returns_200_for_user(self): """Verify endpoint returns 200 for authenticated user requests.""" + user_model = get_user_model() + user = user_model.objects.create_user( + email="[email protected]", + password="strong-test-password", + ) + self.client.force_authenticate(user=user) response = self.client.get(self.url) self.assertEqual( response.status_code, status.HTTP_200_OK, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/django/api/tests/test_partner_field_groups_view_set.py` around lines 52 - 58, test_returns_200_for_user is currently making an unauthenticated GET; authenticate the test client before calling self.client.get so the authenticated path is exercised (e.g., call self.client.force_authenticate(user=self.user) or self.client.login(...) at the start of test_returns_200_for_user, then perform the GET and assert status.HTTP_200_OK; if your test suite requires cleanup, call self.client.force_authenticate(None) or logout after the request).
114-115:⚠️ Potential issue | 🟡 MinorAdd explicit
strict=tozip()(Ruff B905).At Line 114,
zip()should be explicit to avoid silent truncation and satisfy lint gates.✅ Suggested fix
- for sorted_uuid, response_uuid in zip(sorted_uuids, response_uuids): + for sorted_uuid, response_uuid in zip( + sorted_uuids, response_uuids, strict=True + ): self.assertEqual(sorted_uuid, response_uuid)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/django/api/tests/test_partner_field_groups_view_set.py` around lines 114 - 115, The zip over sorted_uuids and response_uuids should be explicit about truncation; update the zip(...) call in the test (where sorted_uuids and response_uuids are iterated) to zip(sorted_uuids, response_uuids, strict=True) so the test will raise if lengths differ and satisfy the Ruff B905 lint rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/django/api/tests/test_partner_field_groups_view_set.py`:
- Around line 52-58: test_returns_200_for_user is currently making an
unauthenticated GET; authenticate the test client before calling self.client.get
so the authenticated path is exercised (e.g., call
self.client.force_authenticate(user=self.user) or self.client.login(...) at the
start of test_returns_200_for_user, then perform the GET and assert
status.HTTP_200_OK; if your test suite requires cleanup, call
self.client.force_authenticate(None) or logout after the request).
- Around line 114-115: The zip over sorted_uuids and response_uuids should be
explicit about truncation; update the zip(...) call in the test (where
sorted_uuids and response_uuids are iterated) to zip(sorted_uuids,
response_uuids, strict=True) so the test will raise if lengths differ and
satisfy the Ruff B905 lint rule.
VadimKovalenkoSNF
left a comment
There was a problem hiding this comment.
Looks clear, left few minor comments.
src/django/api/serializers/partner_field_group/partner_field_group_serializer.py
Show resolved
Hide resolved
src/django/api/views/partner_field_groups/partner_field_groups_view_set.py
Show resolved
Hide resolved
src/django/api/views/partner_field_groups/partner_field_groups_view_set.py
Show resolved
Hide resolved
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/django/api/views/partner_field_groups/partner_field_groups_view_set.py (1)
23-23: Stabilize cursor pagination ordering with a deterministic tie-breaker.Line 23 orders cursors by
orderonly, which is a non-unique integer field with default=0. This can lead to missing or duplicated items at page boundaries under concurrent inserts or updates. Add a stable secondary key to ensure deterministic ordering.♻️ Proposed change
- ordering = "order" + ordering = ("order", "uuid")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/django/api/views/partner_field_groups/partner_field_groups_view_set.py` at line 23, The view's cursor pagination ordering is non-deterministic because ordering = "order" uses a non-unique field; update the PartnerFieldGroupsViewSet (where ordering is set) to use a deterministic compound ordering such as ("order", "pk") or ("order", "id") so the cursor has a stable tie-breaker; change the ordering attribute to a sequence with the secondary key and ensure any related ordering_fields/ordering_filters accept the secondary key if present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/django/api/views/partner_field_groups/partner_field_groups_view_set.py`:
- Line 23: The view's cursor pagination ordering is non-deterministic because
ordering = "order" uses a non-unique field; update the PartnerFieldGroupsViewSet
(where ordering is set) to use a deterministic compound ordering such as
("order", "pk") or ("order", "id") so the cursor has a stable tie-breaker;
change the ordering attribute to a sequence with the secondary key and ensure
any related ordering_fields/ordering_filters accept the secondary key if
present.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
deployment/terraform/variables.tfsrc/django/api/serializers/partner_field_group/partner_field_group_serializer.pysrc/django/api/tests/test_partner_field_groups_view_set.pysrc/django/api/views/partner_field_groups/partner_field_groups_view_set.py
🚧 Files skipped from review as they are similar to previous changes (1)
- src/django/api/tests/test_partner_field_groups_view_set.py



Introduce the
PartnerFieldGroupmodel to organize partner fields.This change adds a foreign key relationship from
PartnerFieldto the newPartnerFieldGroupmodel and exposes these groups via a read-only API endpoint atGET /api/partner-field-groups/. It also registers the new model in the Django Admin, updates thePartnerFieldadmin interface to include the group association, and configures CDN caching for the new endpoint in Terraform (also for additional endpoints, like contributors, contributors-sorted etc.).