Отображение полей таблицы x в GET-запросе таблицы y, имеющей внешний ключ таблицы x (Django)
Мне нужна помощь, пожалуйста, я застрял на этом на некоторое время.
У меня есть две таблицы, Share и Fund.
Share.py
class Share(models.Model):
ShareName = models.CharField(max_length=100, unique=True, default="N/A")
ISINCode = models.CharField(max_length=100, primary_key=True, default="N/A")
Fund.py
class Fund(models.Model):
FundID = models.AutoField(primary_key=True)
FundName = models.CharField(max_length=100, default="N/A", unique=True)
FundIdentifier = models.CharField(max_length=100, default="N/A")
PercentOfFundNetAssets = models.DecimalField(decimal_places=2, max_digits=5, default=0.00)
ISINCode = models.ForeignKey(Share, to_field='ISINCode', related_name="toshares",
on_delete=models.CASCADE)
class Meta:
unique_together = ('FundIdentifier', 'ISINCode')
По отдельности они не представляли проблемы для GET/POST/PUT и т.д. Однако теперь важно, чтобы при GET-запросах получалась таблица фондов, а также соответствующие поля акций, которые относятся к каждой строке фонда. Смотрите ниже:
Как теперь выглядит JSON фонда при GET-запросе
[
{
"FundName": "Some fund",
"FundIdentifier": "FND001",
"PercentOfFundNetAssets": "3.82",
"ISINCode": "SHR001"
},
]
Как я хочу, чтобы JSON фонда выглядел при GET запросе
[
{
"FundName": "Some fund",
"FundIdentifier": "FND001",
"PercentOfFundNetAssets": 3.82,
"ISINCode": "SHR001",
"share": [
{
"ISINCode": "SHR001",
"ShareName": "Some share"
}
]
}
]
Я бы также согласился на что-то вроде этого.
[
{
"FundName": "Some fund",
"FundIdentifier": "FND001",
"PercentOfFundNetAssets": 3.82,
"ISINCode": "SHR001",
"ISINCode": "SHR001",
"ShareName": "Some share"
}
]
В любом случае, каждый объект fund json будет иметь только один объект share внутри с соответствующими полями в каждой строке, как показано ниже. Кажется достаточно простым... но я продолжаю сталкиваться с проблемами, поэтому буду благодарен, если кто-нибудь укажет, где я ошибаюсь.
ShareSerializer.py
class ShareSerializer(serializers.ModelSerializer):
class Meta:
model = Share
fields = ('ShareName', 'ISINCode')
def get_serializer(self, *args, **kwargs):
if "data" in kwargs:
data = kwargs["data"]
# check if many is required
if isinstance(data, list):
kwargs["many"] = True
return super(ShareViewSet, self).get_serializer(*args, **kwargs)
FundSerializer.py
class FundSerializer(serializers.ModelSerializer):
toshares = ShareSerializer(many=True, read_only=True)
class Meta:
model = Fund
fields = ('FundName', 'FundIdentifier', 'PercentOfFundNetAssets', 'ISINCode', 'toshares')
ShareViewSet.py
class ShareViewSet(ModelViewSet):
authentication_classes = []
permission_classes = []
queryset = Share.objects.all().order_by('ISINCode')
serializer_class = ShareSerializer
paginator = None
def get_serializer(self, *args, **kwargs):
if "data" in kwargs:
data = kwargs["data"]
# check if many is required
if isinstance(data, list):
kwargs["many"] = True
return super(ShareViewSet, self).get_serializer(*args, **kwargs)
FundViewSet.py
class FundViewSet(ModelViewSet):
authentication_classes = []
permission_classes = []
queryset = Fund.objects.all().order_by('FundIdentifier')
serializer_class = FundSerializer
paginator = None
def get_serializer(self, *args, **kwargs):
if "data" in kwargs:
data = kwargs["data"]
# check if many is required
if isinstance(data, list):
kwargs["many"] = True
return super(FundViewSet, self).get_serializer(*args, **kwargs)
Вот запрос PostgreSQL, который достигает того, что я хочу, но я не смог эмулировать в Django:
SELECT * FROM fund f
INNER JOIN share s ON s."ISINCode" = f."ISINCode_id"
ORDER BY "FundID" ASC
Что я пробовал:
Я пробовал использовать select_related в queryset в fund viewset (см. ниже):
queryset = Fund.objects.select_related('ISINCode').all().order_by('FundIdentifier') для Fund в queryset: print(Fund.ISINCode)
.
Я могу получить доступ к полям таблицы share таким образом (ну, по крайней мере, вывести их на консоль), но я не знаю, как использовать это для добавления их в queryset, чтобы я мог получить эти столбцы/данные в GET-запросе.
- related_name/set_share (пробовал оба варианта, но опции related_name или set_share в queryset не распознаются по какой-то причине...)
- raw sql (manager.raw и cursor, первый не работает для объединения таблиц, но работает в других случаях, cursor работает, но я не знаю, как перевести cursor.fetchall() в формат queryset). Также это не идеальное решение в любом случае, судя по комментариям/документации.
- Пробовал добавлять фильтр к queryset и ссылаться на share__ShareName или какое-то поле .
- Узнал о python shell, где были сделаны некоторые примеры этого (но я использую Postman, фронтенд или PGAdmin для проверки своей работы вместо этого), поэтому предпочел бы не использовать это, если это может дать решение каким-то образом .
- Я также видел упоминания о файле шаблона и использовании html для достижения того, что я хочу, но я бы предпочел не искать возможности сделать что-то подобное .
Все еще очень много новичков в Django, поэтому я уверен, что одна из вещей, которые я пробовал выше, была просто моей неправильной работой и отсутствием некоторых основ Django. Буду признателен за любую помощь... чем проще решение, тем лучше.
Спасибо!
Проблема, с которой я сталкивался все это время, заключалась в том, что я не включил глубину в мета-часть сериализатора фонда.
Изменение
class Meta:
model = Fund
fields = ('FundName', 'FundIdentifier', 'PercentOfFundNetAssets', 'ISINCode', 'toshares')
to
class Meta:
model = Fund
fields = ('FundName', 'FundIdentifier', 'PercentOfFundNetAssets', 'ISINCode', 'toshares')
depth = 1
исправил мою проблему. Набор запросов по умолчанию, который я использовал, работает нормально после внесения этого изменения.
Запрос Get теперь посылается примерно так.
[
{
"FundName": "Some fund",
"FundIdentifier": "FND001",
"PercentOfFundNetAssets": 3.82,
"ISINCode": {
"ISINCode": "SHR001",
"ShareName": "Some share"
}
}
]