Я создал класс, моделирующий группу файлов в сборке программного продукта на сервере Django с использованием пакета Django-REST. Дизайн заключается в том, что группа файлов (экземпляр хранилища) должна иметь возможность назначаться нескольким экземплярам сборки (например, как «альфа», так и «бета» сборки с использованием одного и того же точного хранилища аудиофайлов). Однако во время создания депо оно создается как часть создания одиночной сборки на клиенте; только позже служебный скрипт позволит добавить существующее хранилище к другим сборкам.
Мне казалось естественным, что класс Depot должен представлять эту связь с помощью ManyToManyField. Проблема в том, что сериализатор, похоже, не знает, что делать с этим ManyToManyField. Я пробовал несколько обходных путей, но у каждого своя ошибка. Я пытался сделать так, чтобы мой DepotSerializer был либо rest_framework.serializers.Serializer
, либо rest_framework.serializers.ModelSerializer
, но это, похоже, не имеет отношения к этой проблеме.
Модели.ру:
class Depot(models.Model):
name = models.CharField(max_length=64)
builds = models.ManyToManyField(Build)
TYPE_EXECUTABLE = 0
TYPE_CORE = 1
TYPE_STREAMING = 2
depot_type = models.IntegerField(choices = (
(TYPE_EXECUTABLE, 'Executable'),
(TYPE_CORE, 'Core'),
(TYPE_STREAMING, 'Streaming'),
))
def __str__(self):
return self.name
Просмотры.py:
class DepotCreate(mixins.CreateModelMixin,
generics.GenericAPIView):
serializer_class = DepotSerializer
queryset = Depot.objects.all()
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
Serializers.py версии 1:
class DepotSerializer(serializers.ModelSerializer):
builds = serializers.PrimaryKeyRelatedField()
class Meta:
model = Depot
fields = ('id', 'name', 'builds', 'depot_type')
read_only_fields = ('id',)
def validate(self, attrs):
build = attrs['builds']
if build == None:
raise serializers.ValidationError("Build could not be found")
for depot in build.depot_set.all():
if depot.name == attrs['name']:
raise serializers.ValidationError("Build already contains a depot \"{}\"".format(depot.name))
return attrs
def restore_object(self, attrs, instance=None):
# existence of the build has already been validated
return Depot(**attrs)
Эта версия приводит к следующей ошибке во время вызова init Depot:
Exception Type: TypeError
Exception Value:
'builds' is an invalid keyword argument for this function
Exception Location: /webapps/cdp_admin_django/lib/python3.4/site-packages/django/db/models/base.py in __init__, line 417
Похоже, это указывает на то, что модель Depot не может обрабатывать параметр builds, несмотря на то, что в ней есть член ManyToManyField.
Serializers.py 'restore_object' версия 2:
def restore_object(self, attrs, instance=None):
# existence of the build has already been validated
build = attrs['builds']
depotObj = Depot(name=attrs['name'], depot_type=attrs['depot_type'])
depotObj.builds.add(build)
return depotObj
Это дало мне ошибку:
Exception Type: ValueError
Exception Value:
"<Depot: depot_test4>" needs to have a value for field "depot" before this many-to-many relationship can be used.
Exception Location: /webapps/cdp_admin_django/lib/python3.4/site-packages/django/db/models/fields/related.py in __init__, line 524
После небольшого исследования я обнаружил, что отношения ManyToMany могут доставить вам проблемы, если вы не сохраните запись MYSQL, прежде чем пытаться манипулировать этим полем. Следовательно, restore_object версии 3:
def restore_object(self, attrs, instance=None):
# existence of the build has already been validated
build = attrs['builds']
depotObj = Depot(name=attrs['name'], depot_type=attrs['depot_type'])
depotObj.save()
depotObj.builds.add(build)
return depotObj
Это успешно создает запись таблицы для этого экземпляра, но в итоге выдает следующую ошибку:
Exception Type: IntegrityError
Exception Value:
(1062, "Duplicate entry '5' for key 'PRIMARY'")
Exception Location: /webapps/cdp_admin_django/lib/python3.4/site-packages/MySQLdb/connections.py in defaulterrorhandler, line 38
Эта ошибка возникает во время вызова rest_framework/mixins.py для serializer.save(force_insert=True). Похоже, что предполагается принудительное создание новой записи в таблице, что, по-видимому, противоречит моему предыдущему вызову Model.save.
Кто-нибудь знает правильный подход к такому дизайну? Я чувствую, что это не может быть такой необычной структурой таблицы.
EDIT 20/10/2014: После приведенного ниже предложения я экспериментировал с написанием нового ModelSerializer для одной из моих моделей; по большей части из-за таких проблем с порядком операций я отказался от использования ModelSerializer и выполнил всю обработку полей данных в объекте в views.py, читая serializer.data.
Наличие PrimaryKeyRelatedField(many=True) в справке ModelSerializer DID. Примечательно, что мне удалось создать экземпляр сериализатора с существующими моделями и получить правильный файл serializer.data. Однако у меня все еще есть проблема, когда restore_object может делать все, кроме создания нового экземпляра модели и передачи значения ManyToManyField. Я все еще получаю «TypeError: '[PrimaryKeyRelatedField name]' является недопустимым аргументом ключевого слова для этой функции», если я передаю поле функции инициализации модели. Я все еще не могу сохранить модель до того, как библиотека REST сделает это сама. Кроме того, в этом режиме сериализатор заполняет файл serializer.data значениями модели, а не значениями, указанными во входных данных. Поэтому, если вы не используете значение атрибута PrimaryKeyRelatedField в restore_object, оно отбрасывается.
Похоже, мне нужно переопределить ModelSerializer.save на какое-то предварительное сохранение, применить ввод ManyToMany и пост-сохранение, но мне потребуются значения attrs, чтобы я мог применить и изменить ManyToManyField в это время. Я понимаю, что сериализатор имеет поле init_data для просмотра исходных входных данных, но в случае, когда сериализатор используется для десериализации списка данных в список новых объектов, я не Думаю, есть способ отследить, какой сериализатор.init_data соответствует какому сериализатору.объекту.
many=True
на своем поле? это выглядит обязательным в документации и отсутствует в приведенном выше коде django-rest -framework.org/api-guide/ - person Anentropic   schedule 07.10.2014