I am wondering how to pass some context data to response in Django.
I have this endpoint:
/api/facebook/user/
which returns data like this:
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"facebook_account_id": "176890406129409",
"first_name": "Ivan",
"last_name": "K",
"token_status": "active",
"facebook_access_token": 1
},
{
"facebook_account_id": "123123123",
"first_name": "Ivan",
"last_name": "FFFF",
"token_status": null,
"facebook_access_token": null
},
{
"facebook_account_id": "123123",
"first_name": "Test",
"last_name": "test",
"token_status": null,
"facebook_access_token": null
}
]
Here is my list serializer:
class FacebookUserListSerializer(ModelSerializer):
token_status = SerializerMethodField()
class Meta:
model = FacebookUser
fields = ["facebook_account_id", "first_name", "last_name", "token_status", "facebook_access_token"]
def get_token_status(self, obj):
try:
facebook_token = obj.facebook_access_token
if facebook_token:
return "active" if facebook_token.is_active else "inactive"
except FacebookToken.DoesNotExist:
return None
Here is my list view:
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
data = response.data
inactive_accounts = sum(1 for account in data if account.get("token_status") == "inactive")
accounts_without_token = sum(1 for account in data if account.get("token_status") is None)
context = {"inactive_accounts": inactive_accounts, "accounts_without_token": accounts_without_token} # Is this the correct way of passing context data to a response?
response.context_data = context
return response
In list view, in the "context" variable, I am attempting to pass data to the frontend, but I am sure that I am doing this incorrectly, I must first convert it to JSON, or make this somewhere else.
So the question is - where (serializer, list method, or somewhere else?) and how should I put this context data according to REST conventions, so that data can be accessed in frontend without iterating over list items?
Do not loop through serialized data to find the information you want, use the view queryset
instead. Also, there is no need to convert your response, DRF Response
handles it automatically, given the correct content-type
.
Since the extra information is not directly related to your object, doing it inside the list
method is just fine. Else, you would use get_serializer_context
method.
Here is a more "advanced" way on how to extract this information, using aggregation
:
views.py
class FacebookUserList(ListAPIView):
queryset = FacebookUser.objects.all()
serializer_class = FacebookUserListSerializer
def list(self, request):
queryset = self.get_queryset()
accounts_without_token = Q(facebook_access_token__isnull=True)
inactive_accounts = (
Q(facebook_access_token__is_active=False) |
Q(facebook_access_token__isnull=True)
)
metadata = queryset.aggregate(
accounts_without_token=Count("pk", filter=accounts_without_token),
inactive_accounts=Count("pk", filter=inactive_accounts),
)
serializer = self.get_serializer(
queryset,
many=True,
)
context = {
"metadata": metadata,
"users": serializer.data,
}
return Response(context)