Repository: django-json-api/django-rest-framework-json-api

Issue:
Sparse field set should not return all fields when value is empty
## Description of the Bug Report

The [JSON:API spec](https://jsonapi.org/format/#fetching-sparse-fieldsets) states

> The value of any fields[TYPE] parameter MUST be a comma-separated (U+002C COMMA, “,”) list that refers to the name(s) of the fields to be returned. An empty value indicates that no fields should be returned.

Currently, DJA returns all fields though when value is empty, but it should return none. None meaning no attributes and relationships but id and type remaining.

## Checklist

- [x] Certain that this is a bug (if unsure or you have a question use [discussions](https://github.com/django-json-api/django-rest-framework-json-api/discussions) instead)
- [ ] Code snippet or unit test added to reproduce bug


Discussion:
sliverc: I agree that the sentence you mentioned is a bit ambiguous, however it would not make any sense to first specify that `The value of any fields[TYPE] parameter MUST` and then afterward say that the `server MAY`. So the sentence you mention is, as far as I see it, if there is no query parameter set at all for a given resource type, but not if it is explicitly set to an empty value.

In any case, I have double-checked what was the behavior before change #1221. It turned out when defining an empty value it would also return no fields. So this means it is a regression and this PR will revert to the behavior we had before version 7.0.0. I have clarified this in the changelog as well now.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 07ea2cd..d60e486 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ any parts of the framework not mentioned in the documentation should generally b
 ### Fixed
 
 * Re-enabled overwriting of url field (regression since 7.0.0)
+* Ensured that no fields are rendered when sparse fields is set to an empty value. (regression since 7.0.0)
 
 ## [7.0.1] - 2024-06-06
 
diff --git a/rest_framework_json_api/renderers.py b/rest_framework_json_api/renderers.py
index 03a95b3..8c19934 100644
--- a/rest_framework_json_api/renderers.py
+++ b/rest_framework_json_api/renderers.py
@@ -439,7 +439,7 @@ class JSONRenderer(renderers.JSONRenderer):
             sparse_fieldset_value = request.query_params.get(
                 sparse_fieldset_query_param
             )
-            if sparse_fieldset_value:
+            if sparse_fieldset_value is not None:
                 sparse_fields = sparse_fieldset_value.split(",")
                 return {
                     field_name: field
diff --git a/rest_framework_json_api/serializers.py b/rest_framework_json_api/serializers.py
index 370ae37..d59dbd8 100644
--- a/rest_framework_json_api/serializers.py
+++ b/rest_framework_json_api/serializers.py
@@ -89,7 +89,7 @@ class SparseFieldsetsMixin:
                 sparse_fieldset_value = request.query_params.get(
                     sparse_fieldset_query_param
                 )
-                if sparse_fieldset_value:
+                if sparse_fieldset_value is not None:
                     sparse_fields = sparse_fieldset_value.split(",")
                     return (
                         field


Test patch:
diff --git a/tests/test_views.py b/tests/test_views.py
index 6dfde90..349d37d 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -219,6 +219,19 @@ class TestModelViewSet:
             }
         ]
 
+    @pytest.mark.urls(__name__)
+    def test_list_with_sparse_fields_empty_value(self, client, model):
+        url = reverse("basic-model-list")
+        response = client.get(url, data={"fields[BasicModel]": ""})
+        assert response.status_code == status.HTTP_200_OK
+        data = response.json()["data"]
+        assert data == [
+            {
+                "type": "BasicModel",
+                "id": str(model.pk),
+            }
+        ]
+
     @pytest.mark.urls(__name__)
     def test_retrieve(self, client, model):
         url = reverse("basic-model-detail", kwargs={"pk": model.pk})