Как написать сериализатор / поле django-rest-framework для объединения данных из общих отношений?
У меня есть объекты с общим отношением, указывающим на различные другие объекты, и мне нужно, чтобы они были объединены (встроены), чтобы сериализованные объекты выглядели как единые целые объекты.
НАПРИМЕР:
class Enrollement(models.Model):
hq = models.ForeignKey(Hq)
enrollement_date = models.Datetime()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
object = generic.GenericForeignKey('content_type', 'object_id')
class Nurse(models.Model):
hospital = models.ForeignKey(Hospital)
enrollement = GenericRelation(Enrollement)
class Pilot(models.Model):
plane = models.ForeignKey(plane)
enrollement = GenericRelation(Enrollement)
После сериализации я хотел бы получить что-то вроде этого:
{
count: 50,
next: 'http...',
previous: null,
results: [
{
type: "nurse",
hq: 'http://url/to/hq-detail/view',
enrollement_date: '2003-01-01 01:01:01',
hospital: 'http://url/to/hospital-detail/view'
},
{
type: "pilot",
hq: 'http://url/to/hq-detail/view',
enrollement_date: '2003-01-01 01:01:01',
plante: 'http://url/to/plane-detail/view'
},
]
}
Могу ли я сделать это, и если да, то как?
Я могу вкладывать родовые отношения, и я могу опубликовать процесс serilizer.data, чтобы получить то, что я хочу, но мне интересно, есть ли лучший способ.
1 ответ
УВАЖАЕМЫЕ ДРУЗЬЯ ИЗ БУДУЩЕГО: На момент написания статьи команда Django REST Framework, похоже, работала над добавлением более зрелой поддержки родовых отношений. Но это еще не закончено. Прежде чем скопировать этот ответ в свою кодовую базу, сначала проверьте https://github.com/tomchristie/django-rest-framework/pull/755 чтобы убедиться, что он объединен с репо. Возможно, вас ждет более элегантное решение. - Твой древний предок Тайлер
Учитывая, что вы используете Django REST Framework, если вы действительно хотите выполнить некоторую постобработку (даже если вам это кажется нерешительным), вы можете достичь чего-то своей цели, переопределив get_queryset
или же list
по вашему мнению. Что-то вроде этого:
views.py:
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from models import *
from itertools import chain
class ResultsList(ListAPIView):
def list(self, request, *args, **kwargs):
nurses = Nurse.objects.all()
pilots = Pilot.objects.all()
results = list()
entries = list(chain(nurses, pilots)) # combine the two querysets
for entry in entries:
type = entry.__class__.__name__.lower() # 'nurse', 'pilot'
if isinstance(entry, Nurse):
serializer = NurseSerializer(entry)
hospital = serializer.data['hospital']
enrollement_date = serializer.data['enrollement.date']
hq = serializer.data['enrollement.hq']
dictionary = {'type': type, 'hospital': hospital, 'hq': hq, 'enrollement_date': enrollement_date}
if isinstance(entry, Pilot):
serializer = PilotSerializer(entry)
plane = serializer.data['plane']
enrollement_date = serializer.data['enrollement.date']
hq = serializer.data['enrollement.hq']
dictionary = {'type': type, 'plane': plane, 'hq': hq, 'enrollement_date': enrollement_date}
results.append(dictionary)
return Response(results)
serializers.py
class EnrollementSerializer(serializer.ModelSerializer):
class Meta:
model = Enrollement
fields = ('hq', 'enrollement_date')
class NurseSerializer(serializer.ModelSerializer):
enrollement = EnrollementSerializer(source='enrollement.get')
class Meta:
model = Nurse
fields = ('hospital', 'enrollement')
class PilotSerializer(serializer.ModelSerializer):
enrollement = EnrollementSerializer(source='enrollement.get')
class Meta:
model = Pilot
fields = ('plane', 'enrollement')
Возвращенный ответ будет выглядеть так:
[
{
type: "nurse",
hq: "http://url/to/hq-detail/view",
enrollement_date: "2003-01-01 01:01:01",
hospital: "http://url/to/hospital-detail/view"
},
{
type: "pilot",
hq: "http://url/to/hq-detail/view",
enrollement_date: "2003-01-01 01:01:01",
plane: "http://url/to/plane-detail/view"
},
]
Обращает на себя внимание:
- Мой serializers.py может быть немного не здесь, потому что моя память о том, как представлять родовые отношения в сериализаторах, немного туманна. YMMV.
- Как и в ^^, предполагается, что ваш serializers.py в порядке и правильно настроил свои родовые отношения в соответствии с вашими моделями.
- Мы делаем
get
вsource=enrollement.get
потому что в противном случае объект GenericRelatedObjectManager будет возвращен, если мы не укажем источник. Это потому, что это то, что представляет собой родовое отношение. С помощью.get
вынуждает запрос (как в запросе QuerySet), который обращается к модели, которую вы задали как источник родового отношения (в этом случае,class Enrollement(models.Model)
, - Мы должны использовать
list(chain())
вместо|
оператор, потому что наборы запросов приходят из разных моделей. Вот почему мы не можем сделатьentries = nurses | pilots
, for entry in entries
безусловно, можно сделать более сухим. GLHF.