Пути URL-адресов конечных точек Django REST API
У меня есть приложение на Django 4.2 с базой данных Postgres и REST API. Мой urls.py содержит этот путь в urlpatterns:
path('create/<int:pk>/<str:name>/', ComponentCreate.as_view(), name='create-component')
ComponentCreate in views.py относится к простой таблице базы данных (component) с идентификатором в качестве целочисленного первичного ключа и именем в качестве единственного другого столбца.
views.py содержит:
class ComponentCreate(generics.CreateAPIView):
queryset = Component.objects.all(),
serializer_class = ComponentSerializer
lookup_field = "id"
моделей.в py есть:
class Component(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = True
db_table = 'component'
serializers.py содержит:
class ComponentSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
name = serializers.CharField()
class Meta:
model = Component
fields = ('id', 'name')
Я пытаюсь использовать API для добавления строки в таблицу компонентов, используя, например, как показано ниже (где приложение называется SystemTestDB):
запись curl -X http://127.0.0.1:8000/SystemTestDB/create/2/whatever/
Однако это не удается с ответом:
{"идентификатор":["Это поле обязательно для заполнения."],"имя":["Это поле обязательно для заполнения."]}
У меня есть другие конечные точки, которые работают корректно, например, с path:
path('delete/<int:pk>/', ComponentDelete.as_view(), name='delete-component')
В этом случае, очевидно, идентификатор передается через int:pk, в то время как в случае сбоя в запросе не указаны ни идентификатор, ни имя.
Я неправильно указал путь к URL-адресу или что-то не так с model/view/serializer?
Удалите пользовательские поля, сделав их обязательными для заполнения:
class ComponentSerializer(serializers.ModelSerializer):
# id = serializers.IntegerField()
# name = serializers.CharField()
class Meta:
model = Component
fields = ('id', 'name')
или, если вы действительно хотите настроить, установите required=False
[drf-doc]:
class ComponentSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(required=False)
class Meta:
model = Component
fields = ('id', 'name')
в модели вы устанавливаете id
как AutoField
или просто скрываете его:
class Component(models.Model):
id = models.AutoField(primary_key=True)
# …
Предложенная вами настройка не подходит для передачи полей в запросе. С djangorestframework
URL-адрес не предназначен для сохранения динамических данных, он указывает серверу, к каким ресурсам вы хотите получить доступ.
ПРИМЕЧАНИЕ: Следующий фрагмент текста является учебным. Код приведен внизу.
Для пути delete
ваше приложение ведет себя корректно, потому что этот является правильным способом передачи такого запроса - вы хотите получить доступ к ресурсу, указанному в id
(чтобы удалить его), поэтому URL-адрес является подходящим местом для его размещения.
Для путей create
ресурс еще не существует, поэтому у него нет id
- и, как таковой, у вас нет возможности сообщить серверу, что вы хотите получить доступ к этому ресурсу. Вы хотите, чтобы сервер создал этот ресурс.
И подходящее место для размещения информации, необходимой для создания этого конкретного ресурса, находится в теле запроса, а не в URL-адресе.
То же самое верно для update
(или в HTML, PATCH
/PUT
), за исключением тех случаев, когда ресурс действительно существует, поэтому вы будете передавать id
как часть URL-адреса, но данные, с помощью которых можно обновить этот ресурс, все равно должны передаваться в теле запроса.
Вы можете выполнить настройку с передачей переменных данных в URL-адрес - это просто не обычно и не рекомендуется. Но если вы намеренно не выбираете этот шаблон по очень специфическим архитектурным соображениям, то почти всегда лучше придерживаться рекомендованного шаблона.
"Предполагаемый" способ ведения дел в djangorestframework
# models.py
class Component(models.Model):
# Instead of IntegerField, use (Big)AutoField to let the database handle
# the resource's primary key (meaning you don't have to pass it manually)
id = models.BigAutoField(primary_key=True)
# serializers.py
class ComponentSerializer(serializers.ModelSerializer):
# ModelSerializer fetches the fields from the model
# Only define them here if you need to override the default behavior
# (which your use-case doesn't seem like you do)
class Meta:
model = Component
fields = ('id', 'name')
class ComponentCreate(generics.CreateAPIView):
queryset = Component.objects.all()
serializer_class = ComponentSerializer
# You don't need lookup_field in this view, CreateAPIView doesn't perform any lookups
# urls.py
path('create/', ComponentCreate.as_view(), name='create-component')
# request
curl -X POST http://127.0.0.1:8000/SystemTestDB/create/ -H "Content-Type: application/json" -d '{"name": "whatever"}'