Вы имеете дело с проблемой nested serialization. Прежде чем продолжить, прочитайте связанную документацию.
Ваш вопрос относится к сложной области проблем в DRF и, следовательно, требует объяснения и обсуждения для понимания того, как работают сериализаторы и виды.
Я буду обсуждать вопрос о представлении вашего Subject
и Class
данные через ту же конечную точку, используя различные представления данных для различных методов HTTP, так как это обычно проблема, когда люди хотят, чтобы представить свои данные в вложенными форматах; они хотят предоставить своим пользовательским интерфейсам достаточную информацию для чистого использования, например. через селектора выпадающего списка.
По умолчанию Django и Django REST Framework (ФПИ) относятся к связанным объектам (ваш Subject
и Class
) их первичных ключей. По умолчанию они автоматически генерируют целые ключи с помощью Django. Если вы хотите сослаться на них другими способами, вам нужно написать переопределения для этого. Существует несколько различных вариантов.
- Первого вариант заключается в специализироваться логика создания и обновления: Обратитесь к классу с помощью какого-либо другого атрибута (ы) и вручную писать просмотр для создания самостоятельно, или установить ключ вы ссылаетесь через как primary key вашего класса. Вы можете указать имя своего класса, UUID или любой другой атрибут в качестве основного ключа базы данных, если он является уникальным, single field (причина, по которой я упоминаю это, потому что вы на данный момент ищете свои модели
Class
с комбинированным поиском, который состоит из составного (числового, буквенного) слова поиска). Например, вы можете переопределить связанные запросы объектов в вашем методе просмотра create
(для POST), но тогда вам также придется обрабатывать похожие поиски в вашем методе просмотра update
(для PUT и PATCH).
- Во-вторых, на мой взгляд, предпочтительным вариантом, является специализироваться свои объектные представления: Обратитесь к классам, обычно с помощью первичного ключа и создать один сериалайзер для чтения на объект и один для создания и обновления его. Это может быть легко достигнуто путем наследования класса сериализатора и переопределения ваших представлений. Используйте первичный ключ в POST, PUT, PATCH и т. Д.запросы на обновление ссылок на классы и внешних ключей.
Вариант 1: Посмотрите Class и Subject с произвольным атрибутом в создании и обновление:
Установите вложенные сериализаторы класса, как только для чтения:
class ExamSerializer(serializers.ModelSerializer):
subject = SubjectSerializer(read_only=True)
clazz = ClassSerializer(read_only=True)
Override ваш вид создается для поиска связанных классов по атрибутам свободной формы. Кроме того, вы можете посмотреть how DRF implements this with mixins. Вы также должны переопределить метод update
правильно обрабатывать эти и принимать во внимание PATCH
(частичное обновление) поддержки в дополнение к PUT
(обновление), если вы берете этот маршрут:
def create(self, request):
# Look up objects by arbitrary attributes.
# You can check here if your students are participating
# the classes and have taken the subjects they sign up for.
subject = get_object_or_404(Subject, title=request.data.get('subject'))
clazz = get_object_or_404(
Class,
number=request.data.get('clazz_number')
letter=request.data.get('clazz_letter')
)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(clazz=clazz, subject=subject)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Вариант 2 : Специализируйте свои сериализаторы для чтения и записи и использования первичных ключей; Это идиоматическое подход:
Сначала определите по умолчанию ModelSerializer вы хотите использовать для обычных операций (POST, PUT, PATCH):
class ExamSerializer(serializers.ModelSerializer)
class Meta:
model = Exam
fields = ('id', 'subject', 'clazz', 'topic', 'date', 'details')
Затем переопределить необходимые поля с видом представления вы хотите дать им для чтения данных (GET):
class ExamReadSerializer(ExamSerializer):
subject = SubjectSerializer(read_only=True)
clazz = ClassSerializer(read_only=True)
Тогда specify the serializer you wish to use for different operations для Viewset. Здесь мы возвращаем вложенные Subject и класс данных для операций чтения, но только использовать их первичные ключи для операций обновления (намного проще):
class ExamViewSet(viewsets.ModelViewSet):
queryset = Exam.objects.all()
def get_serializer_class(self):
# Define your HTTP method-to-serializer mapping freely.
# This also works with CoreAPI and Swagger documentation,
# which produces clean and readable API documentation,
# so I have chosen to believe this is the way the
# Django REST Framework author intended things to work:
if self.request.method in ('GET',)
# Since the ReadSerializer does nested lookups
# in multiple tables, only use it when necessary
return ExamReadSerializer
return ExamSerializer
Как вы можете видеть, вариант 2, кажется довольно менее сложным и подверженным ошибкам, содержащий только 3 строки рукописного кода поверх DRF (реализация get_serializer_class). Просто позвольте логике структуры определить представления и создание и обновление объектов для вас.
Я видел много других подходов, но до сих пор это были те, которые дали наименьший код для поддержки для меня и использовали дизайн DRF в чистом виде.
Это обычная проблема, посмотрите на мой ответ, вы найдете это полезным. http://stackoverflow.com/questions/41308406/django-rest-framework-add-object-to-request-data-and-then-call-serializer-is-va –