Django REST Framework | отношение "многие-ко-многим" возвращает "деталь: не найдена"
Я использую фреймворк Django REST. Я создал модель для элементов в системе инвентаризации, а также сквозную таблицу (с именем subassembly) для отношений "многие-ко-многим" между самими элементами, так что элемент может быть частью других элементов и наоборот.
Я просто не уверен, что сделал все правильно, и не могу получить никаких результатов. Когда я посещаю бэкэнд по URL, например http://localhost:8000/api/subassemblies/2/, ответ такой
{"detail": "Not found."}
но я надеюсь увидеть все вложенные части элемента, или "детей". PUT или любой другой тип запроса имеет тот же результат.
Если это имеет значение, при доступе к подсборкам со страницы администратора, я могу создавать связи между элементами просто отлично. Но только по одной за раз. И мне нужно иметь возможность редактировать все вложенные части элемента за один раз (по крайней мере, фронтенд). В настоящее время тело запроса структурировано следующим образом:
{
"parent_id": 2,
"children": [
{ "child_id": 5, "qty": 2 },
{ "child_id": 4, "qty": 3 },
]
}
Это также позволяет мне использовать .set() для дочерних элементов конкретного элемента, что полезно, потому что я думаю, что это также удаляет все предыдущие дочерние элементы, которые не включены в новый набор.
views.py
class SubassemblyDetail(generics.RetrieveUpdateDestroyAPIView):
"""
Retrieve, update, or delete a particular items subassembly
"""
queryset = Subassembly.objects.all()
serializer_class = SubassemblySerializer
def get_queryset(self):
item_id = self.kwargs['pk']
item = Item.objects.get(pk=item_id)
return item.children.all()
models.py
class Item(models.Model):
# ... (various other fields)
children = models.ManyToManyField('self', through='Subassembly', blank=True)
class Subassembly(models.Model):
parent_id = models.ForeignKey(Item, related_name='parent_item', on_delete=models.CASCADE)
child_id = models.ForeignKey(Item, related_name='child_item', on_delete=models.CASCADE)
child_qty = models.PositiveSmallIntegerField(default=1)
serializers.py
class SubassemblySerializer(ModelSerializer):
parent_id = get_primary_key_related_model(ShortItemSerializer)
child_id = get_primary_key_related_model(ShortItemSerializer)
class Meta:
model = Subassembly
fields = '__all__'
def update(self, instance, validated_data):
children = validated_data.pop('children')
child_list = []
qty_list = []
for child in children:
pk = child['child_id']
qty = child['qty']
obj = Item.objects.get(id=pk)
child_list.append(obj)
qty_list.append(qty)
instance.children.set(child_list, through_defaults={'qty': qty_list})
instance.save()
return instance
def delete(self, instance):
instance.children.clear()
Для ссылки на родительский и дочерний объекты элементов я использую миксин или что-то из этого поста, так что я могу сделать это только с их первичными ключами.
Кроме того, код сериализатора элемента в настоящее время не имеет ссылки на таблицу subassembly through table или ее сериализатор. Я попробовал добавить что-то подобное, но ошибка осталась, и это также сделало поле children обязательным при создании элемента, чего я не хочу. Этот код выглядел следующим образом:
class ItemSerializer(ModelSerializer):
# ... (other fields)
children = SubassemblySerializer(many=true)
def create(self, validated_data):
return Item.objects.create(**validated_data)
class Meta:
model = Item
fields = '__all__'
urls.py
urlpatterns = [
path('', views.RouteList.as_view()),
path('items/', views.ItemList.as_view(), name='item-list'),
path('items/<int:pk>/', views.ItemDetail.as_view(), name='item-detail'),
path('suppliers/', views.SupplierList.as_view(), name='supplier-list'),
path('suppliers/<int:pk>/', views.SupplierDetail.as_view(), name='supplier-detail'),
path('subassemblies/<int:pk>/', views.SubassemblyDetail.as_view(), name='subassembly-detail')
]
Все остальные ссылки работают нормально, только url субсборки выдает эту ошибку.
Если я правильно понимаю, ваша проблема связана с извлечением children
из объекта Item
. Я подозреваю, что объект Item
не извлекается. Диагностировать это можно следующим образом:
Войдите в интерактивную оболочку Django, используя следующую команду:
python manage.py shell
Из оболочки выполните следующее:
# Import Item
from <your_model_name>.models import Item
# Retrieve an Item object
item = Item.objects.get(id=2)
# Retrieve children
children = item.children.all()
# Get the count of children
print(children.count())
Если children.count()
равно 0, это означает, что у объекта элемента нет дочерних элементов. Если вы можете исключить эту возможность, значит, проблема в ваших представлениях или сериализаторах.
При беглом взгляде метод get_queryset()
в SubAssemblyDetail
возвращает кверисет Item
, что и является вероятной проблемой. Под капотом SubAssemblyDetail
выполняет метод retrieve()
см: https://www.cdrf.co/3.13/rest_framework.generics/RetrieveUpdateDestroyAPIView.html
Решено! Спасибо Hamster Hooey - проблема была в том, что в моем методе get_queryset()
в представлении SubassemblyDetail
я возвращал кверисет элемента, а не кверисет подсборки, что кажется неприемлемым для Django.
Поэтому вместо item.children.all()
я сделал Subassembly.objects.filter(parent_id=item_id)
и все отобразилось нормально.