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) и все отобразилось нормально.

Вернуться на верх