fix: replace serializers.Serializer with MonitoringDeviceDetailSerial…#744
fix: replace serializers.Serializer with MonitoringDeviceDetailSerial…#744shakurah wants to merge 4 commits intoopenwisp:masterfrom
Conversation
…izer in DeviceMetricView inside api views to display correct response schema in API documentation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review infoConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
🧰 Additional context used🧠 Learnings (1)📚 Learning: 2026-02-21T18:44:28.852ZApplied to files:
🧬 Code graph analysis (1)openwisp_monitoring/device/api/views.py (1)
🔇 Additional comments (2)
📝 WalkthroughWalkthroughIn openwisp_monitoring/device/api/views.py the DeviceMetricView.serializer_class was changed from rest_framework.serializers.Serializer to MonitoringDeviceDetailSerializer. The import of serializers from rest_framework was removed from the module imports since serializers.Serializer is no longer used. Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 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: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@openwisp_monitoring/device/api/views.py`:
- Line 123: The get() method currently bypasses the view's serializer_class by
calling MonitoringDeviceDetailSerializer(self.instance).data directly, causing
double-serialization or schema/runtime mismatches; change get() to use the
view's standard serializer retrieval (call self.get_serializer(self.instance))
and use its .data so both drf-yasg/schema generation and runtime responses rely
on the same serializer; ensure MonitoringApiViewMixin.get() behavior is
compatible (i.e., if it already calls get_serializer(), remove the hardcoded
MonitoringDeviceDetailSerializer usage) and keep serializer_class =
MonitoringDeviceDetailSerializer as the single source of truth.
- Line 123: The view sets serializer_class = MonitoringDeviceDetailSerializer
but assigns a plain JSON schema dict to schema, which drf-yasg ignores and
causes incorrect POST/GET/CSV documentation; fix by creating a drf-yasg
AutoSchema subclass (e.g., MonitoringDeviceAutoSchema) that returns the correct
per-method requestBody and responses using the existing JSON schema dict from
openwisp_monitoring/device/schema.py and the actual merged GET response (device
detail + charts) and CSV response (charts only), then set schema =
MonitoringDeviceAutoSchema() on the view; alternatively, if
MonitoringDeviceDetailSerializer is not used at runtime (the view hardcodes
serialization), remove serializer_class to avoid misleading docs.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
openwisp_monitoring/device/api/views.py
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-21T18:44:28.852Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/device/api/views.py:263-281
Timestamp: 2026-02-21T18:44:28.852Z
Learning: In openwisp-monitoring, MonitoringIndoorCoordinatesList inherits organization scoping from the parent IndoorCoordinatesList (from openwisp-controller), which uses FilterByParentManaged mixin and filters by location_id in get_queryset(). The child class only overrides the queryset attribute to add monitoring-specific select_related fields; this pattern is safe as long as get_queryset() from the parent is not bypassed. During reviews, verify that MonitoringIndoorCoordinatesList continues to rely on the parent's get_queryset() and that any added select_related fields in the child do not alter the parent's filtering logic.
Applied to files:
openwisp_monitoring/device/api/views.py
🧬 Code graph analysis (1)
openwisp_monitoring/device/api/views.py (1)
openwisp_monitoring/device/api/serializers.py (1)
MonitoringDeviceDetailSerializer(109-110)
| .all() | ||
| ) | ||
| serializer_class = serializers.Serializer | ||
| serializer_class = MonitoringDeviceDetailSerializer |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
serializer_class and the explicit MonitoringDeviceDetailSerializer call on line 154 are decoupled — consider aligning them
The get() method always explicitly constructs device detail data via MonitoringDeviceDetailSerializer(self.instance).data (line 154), regardless of serializer_class. This means:
- If
MonitoringApiViewMixin.get()internally callsself.get_serializer()(the standard DRF path), device data is now serialized twice per request — once by the mixin viaget_serializer()and again on line 154. - For the CSV path (
return responseon line 158),super().get()is returned directly. If the mixin usesget_serializer(), this response now includesMonitoringDeviceDetailSerializerfields that weren't present before (whenserializers.Serializerwas theserializer_class), potentially changing the CSV output structure.
If MonitoringApiViewMixin.get() does not call get_serializer() and the fix is purely for drf-yasg, the existing design is simply inconsistent — get() could be refactored to use self.get_serializer(self.instance) instead of hardcoding the class, so that both schema inference and runtime behaviour share a single source of truth:
♻️ Suggested alignment (contingent on mixin behaviour)
if not request.query_params.get("csv"):
charts_data = dict(response.data)
- device_metrics_data = MonitoringDeviceDetailSerializer(self.instance).data
+ device_metrics_data = self.get_serializer(self.instance).data
return Response(
{**device_metrics_data, **charts_data}, status=status.HTTP_200_OK
)Also applies to: 144-158
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@openwisp_monitoring/device/api/views.py` at line 123, The get() method
currently bypasses the view's serializer_class by calling
MonitoringDeviceDetailSerializer(self.instance).data directly, causing
double-serialization or schema/runtime mismatches; change get() to use the
view's standard serializer retrieval (call self.get_serializer(self.instance))
and use its .data so both drf-yasg/schema generation and runtime responses rely
on the same serializer; ensure MonitoringApiViewMixin.get() behavior is
compatible (i.e., if it already calls get_serializer(), remove the hardcoded
MonitoringDeviceDetailSerializer usage) and keep serializer_class =
MonitoringDeviceDetailSerializer as the single source of truth.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find and inspect the custom schema class
echo "=== Searching for schema.py in openwisp_monitoring/device/ ==="
find openwisp_monitoring/device -name "schema.py" -type f
echo -e "\n=== Searching for MonitoringApiViewMixin definition ==="
rg -n "class MonitoringApiViewMixin" --type py -A 40
echo -e "\n=== Inspecting DeviceMetricView more closely ==="
rg -n "class DeviceMetricView" openwisp_monitoring/device/api/views.py -A 100 | head -120
echo -e "\n=== Finding where schema is imported from ==="
rg -n "from.*schema import" openwisp_monitoring/device/api/views.pyRepository: openwisp/openwisp-monitoring
Length of output: 7739
🏁 Script executed:
#!/bin/bash
echo "=== Full MonitoringApiViewMixin.get() method ==="
rg -n "def get" openwisp_monitoring/views.py -A 80 | head -100
echo -e "\n=== Full openwisp_monitoring/device/schema.py ==="
cat openwisp_monitoring/device/schema.pyRepository: openwisp/openwisp-monitoring
Length of output: 24141
🏁 Script executed:
#!/bin/bash
echo "=== Checking if there are tests for DeviceMetricView POST/GET ==="
rg -n "class.*DeviceMetric.*Test" --type py -A 5
echo -e "\n=== Checking imports in views.py to understand drf-yasg integration ==="
head -50 openwisp_monitoring/device/api/views.py | grep -E "^from|^import"
echo -e "\n=== Checking if schema is used as drf-yasg AutoSchema ==="
rg -n "from drf_yasg" openwisp_monitoring/device/ --type py
</parameter>Repository: openwisp/openwisp-monitoring
Length of output: 1608
🏁 Script executed:
#!/bin/bash
echo "=== Checking MonitoringDeviceDetailSerializer fields ==="
rg -n "class MonitoringDeviceDetailSerializer" openwisp_monitoring/device/api/ -A 30
echo -e "\n=== Checking what data is actually returned in GET response ==="
rg -n "def get\|return Response" openwisp_monitoring/device/api/views.py | head -20Repository: openwisp/openwisp-monitoring
Length of output: 2682
drf-yasg will incorrectly document POST and GET schemas — the plain JSON schema dict does not override serializer-based schema generation
The schema = schema assignment (line 125) is a plain JSON schema dict from openwisp_monitoring/device/schema.py, not a drf-yasg AutoSchema subclass. drf-yasg ignores plain dicts and falls back to using serializer_class for schema generation.
This causes API documentation mismatches:
- POST request body: Documented as
MonitoringDeviceDetailSerializerfields, but the actual payload is raw device-metrics JSON (matching the NetJSON schema structure). - GET response: Documented as
MonitoringDeviceDetailSerializeronly, but the actual response merges device detail data withchartsdata (line 156). - CSV response: Documented as
MonitoringDeviceDetailSerializerbut actually returns only chart data (line 158).
Clients using auto-generated swagger clients or reading the API docs will have incorrect schema expectations. Either implement a custom drf-yasg AutoSchema subclass that properly documents per-HTTP-method schemas, or remove serializer_class if it's not used at runtime (line 154 hardcodes the serializer independently).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@openwisp_monitoring/device/api/views.py` at line 123, The view sets
serializer_class = MonitoringDeviceDetailSerializer but assigns a plain JSON
schema dict to schema, which drf-yasg ignores and causes incorrect POST/GET/CSV
documentation; fix by creating a drf-yasg AutoSchema subclass (e.g.,
MonitoringDeviceAutoSchema) that returns the correct per-method requestBody and
responses using the existing JSON schema dict from
openwisp_monitoring/device/schema.py and the actual merged GET response (device
detail + charts) and CSV response (charts only), then set schema =
MonitoringDeviceAutoSchema() on the view; alternatively, if
MonitoringDeviceDetailSerializer is not used at runtime (the view hardcodes
serialization), remove serializer_class to avoid misleading docs.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
openwisp_monitoring/device/api/views.py (1)
123-158: Previously raised concerns remain unaddressed.The
serializer_classassignment is the right intent for fixing drf-yasg schema generation. However, two pre-existing issues flagged in prior review rounds are still present in the current code and are directly relevant to this change:
Double-serialization / decoupled
serializer_class(line 154):get()still hardcodesMonitoringDeviceDetailSerializer(self.instance).dataindependently ofserializer_class. IfMonitoringApiViewMixin.get()callsself.get_serializer()internally, the serializer is invoked twice per request. At minimum, line 154 should useself.get_serializer(self.instance).dataso the runtime path and the schema-inferred path share a single source of truth.drf-yasg schema mismatch (lines 123, 125): The
schema = schemaassignment on line 125 is a plaindict(fromopenwisp_monitoring/device/schema.py), not a drf-yasgAutoSchemasubclass — drf-yasg silently ignores it and falls back entirely toserializer_class. This means:
- POST: Request body is documented as
MonitoringDeviceDetailSerializerfields, but the actual POST payload is raw device-metrics JSON (NetJSON format).- GET: Response is documented as
MonitoringDeviceDetailSerializeronly, but the actual response isMonitoringDeviceDetailSerializerdata merged with chart data (line 155–156).- CSV path (line 158): Returns only chart data, but is documented as
MonitoringDeviceDetailSerializer.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openwisp_monitoring/device/api/views.py` around lines 123 - 158, The view still double-serializes and misleads drf-yasg: replace the hardcoded MonitoringDeviceDetailSerializer(self.instance).data in get() with self.get_serializer(self.instance).data (so the runtime serializer is the same as serializer_class and used by any MonitoringApiViewMixin.get() path), and replace the plain dict assigned to schema (the schema variable) with a proper drf-yasg AutoSchema-compatible inspector or remove the dict so drf-yasg uses serializer_class correctly; ensure GET responses documented reflect the merged payload (MonitoringDeviceDetailSerializer + charts) and the CSV branch documents/returns only chart data by updating the view’s schema implementation to return the correct request/response schemas for get/post/csv.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@openwisp_monitoring/device/api/views.py`:
- Around line 123-158: The view still double-serializes and misleads drf-yasg:
replace the hardcoded MonitoringDeviceDetailSerializer(self.instance).data in
get() with self.get_serializer(self.instance).data (so the runtime serializer is
the same as serializer_class and used by any MonitoringApiViewMixin.get() path),
and replace the plain dict assigned to schema (the schema variable) with a
proper drf-yasg AutoSchema-compatible inspector or remove the dict so drf-yasg
uses serializer_class correctly; ensure GET responses documented reflect the
merged payload (MonitoringDeviceDetailSerializer + charts) and the CSV branch
documents/returns only chart data by updating the view’s schema implementation
to return the correct request/response schemas for get/post/csv.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
openwisp_monitoring/device/api/views.py
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-21T18:44:28.852Z
Learnt from: dee077
Repo: openwisp/openwisp-monitoring PR: 738
File: openwisp_monitoring/device/api/views.py:263-281
Timestamp: 2026-02-21T18:44:28.852Z
Learning: In openwisp-monitoring, MonitoringIndoorCoordinatesList inherits organization scoping from the parent IndoorCoordinatesList (from openwisp-controller), which uses FilterByParentManaged mixin and filters by location_id in get_queryset(). The child class only overrides the queryset attribute to add monitoring-specific select_related fields; this pattern is safe as long as get_queryset() from the parent is not bypassed. During reviews, verify that MonitoringIndoorCoordinatesList continues to rely on the parent's get_queryset() and that any added select_related fields in the child do not alter the parent's filtering logic.
Applied to files:
openwisp_monitoring/device/api/views.py
🧬 Code graph analysis (1)
openwisp_monitoring/device/api/views.py (1)
openwisp_monitoring/device/api/serializers.py (1)
MonitoringDeviceDetailSerializer(109-110)
🔇 Additional comments (1)
openwisp_monitoring/device/api/views.py (1)
16-16: LGTM — import cleanup is correct.Removing
serializersfrom the import is accurate sinceserializers.Serializeris no longer referenced anywhere in the file.
replace serializers.Serializer with MonitoringDeviceDetailSerializer in DeviceMetricView
Checklist
Reference to Existing Issue
Closes #716.
Description of Changes
The
DeviceMetricViewwas usingserializers.Serializeras itsserializer_classinstead of
MonitoringDeviceDetailSerializer. This caused drf-yasg to be unable toinfer the correct response schema, resulting in a generic example value being displayed
in the API documentation instead of the actual response fields.
Replacing it with
MonitoringDeviceDetailSerializerallows drf-yasg to correctlygenerate the schema for the endpoint, making the API documentation accurately reflect
the real response structure.
Screenshot