Сериализатор получает null для столбцов таблицы в обратном направлении внешнего ключа
В сценарии обработки транзакций мы имеем:
- таблицу
order
для регистрации входящих транзакций, и - таблица
order_log
для регистрации обновлений по записанному заказу, с внешним ключом к таблицеorder
.
Зарегистрированный заказ может иметь от нуля до нескольких обновлений в журнале.
Мы хотим сделать уплощенное представление двух таблиц, как SQL запрос, выбирающий из order left outer join order_log
, со следующим поведением:
- если у заказа нет обновления, перечислите заказ в совместном списке с нулевыми значениями;
- если ордер имеет одно обновление, перечислите ордер в совместном списке с журналом обновлений;
- если заказ имеет несколько обновлений, перечислите заказ несколько раз совместно с каждым журналом обновлений.
В приведенном ниже примере исходного кода мы использовали .prefetch_related('orderlog_set')
, и это дало желаемый эффект сглаживания, а журнал бэкенда Django показывает SQL-запрос left-outer-join, отправленный в базу данных, как и ожидалось. Таким образом, взаимное отношение работает корректно для всех трех условий, описанных выше.
Однако мы не можем получить столбцы в order_item
, так как таблица находится в обратном направлении внешнего ключа.
Сериализатор FlatOrderLogSerializer
основывает свою мета-модель на таблице order
, поэтому он обращается к таблице order_log
по обратному или обратному направлению внешнего ключа. В результате сериализатор не может получить нужный столбец и всегда получает нулевые значения.
Мы правильно извлекли столбцы в родной таблице и таблицах, на которые ссылается обычный внешний ключ. К сожалению, просто обратный/направленный внешний ключ не работает.
Мы новички в этой части Django и не уверены, как правильно установить атрибут source
объекта field. Мы пробовали serializers.ReadOnlyField(source='orderlog_set__update_ts', ...
и несколько других вариантов, но пока ничего не получается. Смотрите комментарии в коде примера для более подробной информации.
Просто дайте мне знать, если вам нужна дополнительная информация, и любые предложения будут высоко оценены.
# Table 'order'
class Order(models.Model):
...
# Table 'order_log'
class OrderLog(models.Model):
order = models.ForeignKey('Order')
update_ts = models.DateTimeField(auto_now=True, editable=False, verbose_name="Last Updated On")
...
# The serializer
class FlatOrderLogSerializer(serializers.ModelSerializer):
# Standing at table 'order', it refers to table 'order_log' by a reverse (backward) foreign-key.
# The field with source='orderlog_set__update_ts' always gets null, and
# we tried other source settings, e.g. 'orderlog_set.update_ts', or 'orderlog.update_ts',
# unfortunately nothing works so far.
update_ts = serializers.ReadOnlyField(source='orderlog_set__update_ts', allow_null=True)
...
class Meta:
model = Order
fields = (
'update_ts',
...
)
# The view
class FlatOrderLogView(generics.ListAPIView):
serializer_class = rest_models.FlatOrderLogSerializer
...
def get_queryset(self):
flat_orderlogs = Order.active_objects.filter(
...
).prefetch_related(
'orderlog_set',
).all()
return flat_orderlogs
После некоторого поиска в Google, это выглядит следующим образом:
- Фреймворк Django предоставляет стандартную функцию
select_related()
для объединения по внешнему ключу в прямом направлении. Бэкэнд реализует эту функцию как SQL-запрос с внутренним соединением. - Однако мы не нашли элегантного способа просмотра назад по внешнему соединению. Вместо этого в большинстве статей рекомендуется использовать функцию
prefetch_related()
и делать вложенный сериализатор. И этот шаблон проектирования, похоже, является обычной практикой.
Ниже приведены некоторые предварительные соображения, и мы надеемся получить отзывы или поправки от экспертов:
С помощью SQL-запроса мы можем обратиться к другой таблице по внешнему ключу в обратном направлении с помощью outer-join. Однако, похоже, что Django не поощряет этот интуитивный шаблон проектирования, поэтому не реализовал стандартный метод. Если это подтвердится, мы откажемся от запроса outer-join и будем искать другие решения.
Кстати, мы также можем сделать небольшое уточнение по терминологии:
Мы не нашли в учебниках определения направленного атрибута внешнего ключа, например, прямого, обратного или обратного направления; однако эти слова встречаются в сетевой литературе.
На ER-диаграмме ниже таблица app_orderitemlog содержит внешний ключ, указывающий на таблицу app_orderitem. Таким образом, с помощью этого отношения внешнего ключа:
- Таблица app_orderitemlog ссылается на таблицу app_orderitem в направлении внешнего ключа forward.
- Таблица app_orderitem ссылается на таблицу app_orderitemlog в направлении backward, также называемом reverse направлением.