Сериализация RAW SQL запроса Django

Надеюсь, у вас все хорошо. Мне нужно сериализовать мой RAW SQL запрос:

SELECT nn.*,nm.*FROM notifications_newsletter nn LEFT JOIN notifications_message nm ON nn.id=nm.newsletter_id_id ORDER by nm.status DESC

models.py

from django.db import models


class Newsletter(models.Model):
    start_datetime = models.DateTimeField()
    text = models.TextField(blank=True)
    filter = models.ForeignKey('Filter', null=True, on_delete=models.SET_NULL)
    end_datetime = models.DateTimeField()


class Message(models.Model):
    send_datetime = models.DateTimeField('%Y-%m-%d %H:%M:%S', auto_now=True)
    status = models.BooleanField(default=False)
    newsletter_id = models.ForeignKey('Newsletter', on_delete=models.CASCADE)
    client_id = models.ForeignKey('Client', on_delete=models.CASCADE)

views.py

from django.core.serializers import serialize
from django.http import HttpResponse

from .models import Newsletter


def some_view(request):
    sql = 'SELECT nn.*,nm.*FROM notifications_newsletter nn ' \
          'LEFT JOIN notifications_message nm ' \
          'ON nn.id=nm.newsletter_id_id ORDER by nm.status DESC'
    qs = Newsletter.objects.raw(sql)
    qs_json = serialize('json', qs)
    return HttpResponse(qs_json, content_type='application/json')

Если я делаю это с помощью serializers.serialize() все объединенные данные (таблица сообщений) не существуют в ответе. Но если print(qs.columns) то колонки send_datetime, status, etc. будут выведены.

Ответ:

[
    {
        "model": "notifications.newsletter",
        "pk": 42,
        "fields": {
            "start_datetime": "2022-01-21T21:56:09Z",
            "text": "This is test message for 900 operator code.",
            "filter": 1,
            "end_datetime": "2022-01-21T18:00:00Z"
        }
    },
] 

Мне нужно что-то вроде:

[
    {
        "model": "notifications.newsletter",
        "pk": 43,
        "fields": {
            "start_datetime": "2022-01-21T22:03:26Z",
            "text": "This is test message for 904 operator code.",
            "filter": 2,
            "end_datetime": "2022-01-21T18:00:00Z",
            "messages": [
                {
                    "send_datetime": "2022-01-21T22:03:26Z",
                    "status": 0,
                    "newsletter_id": 43,
                    "client_id": 1
                },
            ]
        }
    },
]

Можно ли его нормально сериализовать?

Вы можете использовать вложенные сериализаторы моделей из Django REST Framework для своих целей следующим образом:

from rest_framework import serializers

class MessageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Message
        fields = "__all__"

class NewsletterSerializer(serializers.ModelSerializer):
    messages = MessageSerializer(many=True)

    class Meta:
        model = Newsletter
        fields = "__all__"

И используйте NewsletterSerializer в своем представлении:

from django.core.serializers import serialize
from django.http import HttpResponse

from .models import Newsletter


def some_view(request):
    sql = 'SELECT nn.*,nm.*FROM notifications_newsletter nn ' \
          'LEFT JOIN notifications_message nm ' \
          'ON nn.id=nm.newsletter_id_id ORDER by nm.status DESC'
    qs = Newsletter.objects.raw(sql)
    qs_json = NewsletterSerializer(qs, many=True).data
    return HttpResponse(qs_json, content_type='application/json')

Также вам нужно сделать некоторые другие изменения в вашем коде:

  1. Вам необходимо переименовать поля newsletter_id, client_id в newsletter, client соответственно. Подробнее об этом можно прочитать здесь.
  2. Вы должны указать related_name в вашем newsletter внешнем ключе в Message модели. Установите его в messages следующим образом:
newsletter = models.ForeignKey('Newsletter', on_delete=models.CASCADE, related_name='messages')
  1. Вы должны переименовать свое поле filter, потому что это зарезервированное ключевое слово.
Вернуться на верх