Как переопределить нулевые значения внешнего ключа в сериализаторе django?

#python #django #serialization

Вопрос:

Я сериализую набор запросов в формат json с помощью natural_keys. Ссылка: документы

Я могу успешно сериализовать данные. В случае, если есть какие-либо внешние ключи, я также могу добавить его объект вместо внешнего ключа. Например:

 class Parent(models.Model):
    name = models.CharField()

    def get_natural_keys(self):
        return(
                 {'name': self.name, 'pk': self.pk}
        )

class Child(models.Model):
    name = models.CharField()
    parent = models.ForeignKey(Parent, null=True)
 

И при запросе данных:

 child = serializers.serialize('json', list(Child.objects.all()), user_natural_foreign_keys=True, use_natural_primary_keys=True)
 

Это вернет json:

 {
    "model": 'proj.child'
    "pk": 1,
    "fields": {
                "name": "child name",
                "parent": {"id": 1, "name": "parent name"}
              }
}
 

До этого момента все в порядке. Моя проблема в том, что когда родительский внешний ключ равен нулю в дочернем, он не возвращает ничего в родительском:

 fields: {
    "name": "child name",
    "parent": None
}
 

Как я ожидаю, это:

 fields: {
    "name": "child name",
    "parent": {"id": None. "name": None}
}
 

Как я могу переопределить значение None в другом словаре?
Один из способов — просмотреть список словарей и отредактировать его. Но я не считаю это лучшим вариантом.

[ПРАВИТЬ]

Чтобы сделать мой дизайн более конкретным:

 class Person(models.Model):
    name = models.CharField()
    phone = models.CharField()
 
class Building(modls.Model):
    name = models.CharField()
    address = models.CharField()
    build_by = models.ForeignKey(Person, null=False)
    owner = models.ForeignKey(Person)
    residing_by = models.ForeignKey(Person, null=True)
 

Во-первых, я попытался сериализовать building объект, и у него были foreign keys сериализованные данные. Однако я не ожидал внешних ключей в сериализованных данных. Вместо этого мне нужны другие сериализованные данные вместо внешнего ключа.Итак, я наткнулся на get_natural_keys (), с помощью которого я могу сериализовать объекты внешнего ключа. Я могу настроить get_natural_keys() возврат сериализованных данных.

В приведенной выше модели здания residing_by может быть равно нулю в какой-то момент времени. Для этого значения null в сериализованных данных я хотел перезаписать значение null другим словарем, например {'id': None, 'name': None} .

Комментарии:

1. Если вам нужна расширенная сериализация моделей, я предлагаю вам использовать сериализаторы django restframework

2. Я бы посоветовал этого не делать. Вашего родителя не существует. У вас нет родителя без удостоверения личности и имени. Разница важна, особенно когда в игру вступает больше полей, даже вложенных внешних ключей. Все, что потребляет ваши сериализованные данные, должно быть в состоянии справиться с этим.

3. @Melvyn, я попытался воспроизвести свою проблему здесь. На самом деле, parent_id существует в родительской таблице. Это тот случай, когда дочерняя таблица не желает ссылаться на родительскую таблицу. Поэтому мне пришлось сохранить значение null для родительской ссылки в дочерней таблице. Если это тоже не очень хороший подход, то предложите для него лучший.

Ответ №1:

Есть несколько проблем с вашей настройкой:

  • Весь смысл естественных ключей состоит в том, чтобы избежать автоматического создания данных, таких как автоматическое увеличение первичных ключей, чтобы вы могли идентифицировать запись в других базах данных (производственных, промежуточных), которые могут иметь разные порядки вставки. Вместо этого вы возвращаете автоматически сгенерированный первичный ключ в качестве естественного ключа.
  • Похоже, вы хотите использовать платформу сериализации для чего-то, для чего она не предназначена, или что другие пакеты, такие как Django REST Framework, работают лучше.
  • Ваши модели не подходят для естественных ключей, потому что у них есть только одно поле, и оно не уникально, поэтому невозможно ссылаться на запись без использования первичного ключа.
  • И, наконец, я не понимаю, зачем вам для начала нужны естественные ключи. Что заставило вас решиться на это?

Это сценарий, в котором дочерняя таблица не желает ссылаться на родительскую таблицу

Я не совсем понимаю, что это значит. Вы связываете ребенка с родителем или нет. Они не настоящие дети и должны подчиняться тому, что вы программируете :). Если требуется родительский элемент, то не добавляйте null=True во внешний ключ, чтобы он выдал ошибку, и тогда вы узнаете, в чем заключается ваша проблема с программированием.

В целом, я думаю, что вы сделали несколько предположений о том, как все работает и как вы могли бы их решить, но выбранные вами решения не подходят.

Как уже было сказано, вы должны сначала выяснить, почему дети могут создаваться без родителей, если это не то, чего вы хотите, и исправить это. Затем заново оцените, как должна работать сериализация, потому что вставлять автоматические идентификаторы в естественные ключи не имеет смысла. Вам, вероятно, не нужны естественные ключи. Если вы сделали это, чтобы изменить формат вывода, то, как предлагали другие, DRF предоставляет вам лучшие возможности, но он также имеет крутую кривую обучения.

Комментарии:

1. Это полезная информация. Я обновил свой вопрос. Я предполагаю, что сделал неправильный шаг в сериализации данных внешнего ключа с использованием естественных ключей.

Ответ №2:

Когда у меня была похожая проблема, для меня что-то подобное сработало:

 class Chield(models.Model):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if not self.parent:
            self.parent = Parent()
    name = models.CharField()
    parent = models.ForeignKey(Parent, null=True)
 

Комментарии:

1. Да, это то, что я ищу. Я бы рассмотрел этот ответ, поскольку мне не требуется изменять большую часть своего кода. Увеличит ли этот подход временную сложность, когда я получу больше данных? Потому что я предполагаю, что условный оператор срабатывает для каждой записи в базе данных. я прав?

Ответ №3:

Я бы рекомендовал использовать сериализаторы django-rest-framework для таких случаев использования.

 
from .models import Parent, Child

from rest_framework import serializers

### Define Serializers

class ParentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Parent
        fields = ['id', 'name']
        
class ChildSerializer(serializers.ModelSerializer):
    parent = ParentSerializer()
    
    class Meta:
        model = Child
        fields = ['id', 'name', 'parent']
        
   def to_representation(self, instance):
        # get representation from ModelSerializer
        ret = super(ChildSerializer, self).to_representation(instance)
        # if parent is None, overwrite
        if not ret.get("parent", None):
            ret["parent"] = {"id": None, "name": None}
        return ret
        
        
### example serialization

childs = ChildSerializer(Child.objects.all(), many=True)

print(childs.data)

"""
Output:
[
    {
        "id": 1,
        "name": "example child name",
        "parent": {
            "id": 1,
            "name": "example parent name"
        }
    },
    #...snip..
]
"""