Обновление отношений ManytoMany в Django Rest Framework - PullRequest
0 голосов
/ 01 мая 2020

В моем приложении django есть отношение ManytoMany между Orders и Packages. В заказе может быть несколько пакетов. Я хочу знать об обновлении и методах создания

Models.py

class Package(models.Model):

    prod_name = models.CharField(max_length=255, default=0)
    quantity = models.IntegerField(default=0)
    unit_price = models.IntegerField(default=0)

class Orders(models.Model):

    order_id = models.CharField(max_length=255, default=0)
    package = models.ManyToManyField(Package)
    is_cod = models.BooleanField(default=False)

Serializers.py

class PackageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Package
        fields = "__all__"

class OrderSerializer(serializers.ModelSerializer):
    package = PackageSerializer(many=True)

    class Meta:
        model = Orders
        fields = "__all__"

Views.py

class OrdersCreateAPIView(generics.CreateAPIView):
    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = OrderSerializer

    def post(self, request):

        serializer = OrderSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Достаточно ли этого для обработки связанных данных? Я пытаюсь понять отношения ManytoMany как в Django, так и в DRF, поэтому, пожалуйста, объясните, нужно ли мне в любом случае изменить Модели или представления

Обновление:

Я обновил свой Сериализатор, а также просмотр для создания manytomany связанных объектов, таких как:

class OrderSerializer(serializers.ModelSerializer):
    package = PackageSerializer(many=True)

    class Meta:
        model = Orders
        fields = "__all__"

    def create(self, validated_data):
        package_data = validated_data.pop('package')
        pkgs = []
        order = Orders.objects.create(**validated_data)
        for i in package_data:
            try:
                p = Package.objects.create(**i)
                pkgs.append(p)
            except:
                pass
        order.package.set(pkgs)
        return order

Views.py

class OrdersCreateAPIView(CreateAPIView):
    permission_classes = (permissions.IsAuthenticated,)
    serializer_class = OrderSerializer

    def perform_create(self,serializer):
        serializer.save(owner=self.request.user)

Однако мне все еще неясно, переопределить метод update RetrieveUpdateDestroyAPIView. Кроме того, является ли вышеуказанный метод правильным способом хранения M2M связанных объектов?

Пожалуйста, помогите с обновлением части сериализатора, я понимаю, что должен передать запрос в сериализаторе

Ответы [ 2 ]

2 голосов
/ 07 мая 2020

Рабочая кодовая база

#serializers.py
class PackageSerializer(serializers.ModelSerializer):
    <b>id = serializers.IntegerField()</b>

    class Meta:
        model = Package
        fields = "__all__"


class OrderSerializer(serializers.ModelSerializer):
    package = PackageSerializer(many=True)

    <b>def get_or_create_packages(self, packages):
        package_ids = []
        for package in packages:
            package_instance, created = Package.objects.get_or_create(pk=package.get('id'), defaults=package)
            package_ids.append(package_instance.pk)
        return package_ids

    def create_or_update_packages(self, packages):
        package_ids = []
        for package in packages:
            package_instance, created = Package.objects.update_or_create(pk=package.get('id'), defaults=package)
            package_ids.append(package_instance.pk)
        return package_ids

    def create(self, validated_data):
        package = validated_data.pop('package', [])
        order = Orders.objects.create(**validated_data)
        order.package.set(self.get_or_create_packages(package))
        return order

    def update(self, instance, validated_data):
        package = validated_data.pop('package', [])
        instance.package.set(self.create_or_update_packages(package))
        fields = ['order_id', 'is_cod']
        for field in fields:
            try:
                setattr(instance, field, validated_data[field])
            except KeyError:  # validated_data may not contain all fields during HTTP PATCH
                pass
        instance.save()
        return instance</b>

    class Meta:
        model = Orders
        fields = "__all__"

#views.py
class OrderViewSet(viewsets.ModelViewSet):
    serializer_class = OrderSerializer
    queryset = Orders.objects.all()

Зарегистрируйте это представление с помощью DefaultRouter as,

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'order', OrderViewSet, basename='order')
urlpatterns = [

              ] + router.urls

Таким образом, вы будете получите базовые c конечные точки CRUD, как описано в этой таблице (см. DefaultRouter ref).

Пусть ваш список заказов конечная точка будет /foo-bar/order/

  1. HTTP POST к /foo-bar/order/ для создания нового экземпляра
  2. HTTP PUT или HTTP PATCH к /foo-bar/order/<ORDER_PK>/ для обновления содержимого

Примечание

В этом случае вы должны передать значение id пакета, если вы хотите sh сопоставить существующее отношение пакета с Order

Ссылки

  1. DRF ModelVieSet
  2. Django get_or_create(...)
  3. Django create_or_update(...)
  4. Django М2М set(...)
  5. DRF DefaultRouter

UPDATE-1

Вы можете подключить вид следующим образом

urlpatterns = [
    path('foo/order/', OrderViewSet.as_view({'post': 'create'})),  # create new Order instance
    path('foo/order/<int:pk>/', OrderViewSet.as_view({'patch': 'partial_update'})),  # update Order instance

]

Примечание: Поддерживает только HTTP POST и HTTP PATCH

0 голосов
/ 07 мая 2020

Эй, это так же просто, как создать новый APIView для Package. Что вам нужно сделать, это создать конечную точку для обновления режима.

# models.py
class Package(models.Model):

    prod_name = models.CharField(max_length=255, default=0)
    quantity = models.IntegerField(default=0)
    unit_price = models.IntegerField(default=0)

Теперь подключите эту модель к сериализатору.

# serializers.py
class PackageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Package
        fileds = '__all__'

Теперь создайте UpdateAPIView для этого.

# views.py
class PackageUpdateAPIView(generics.UpdateAPIView):
    serializer_class = PackageSerializer
    queryset = Package.objects.all()

Подключение к URL

# urls.py

# urls = [ ... ]
urls += [
    path('package-update/<int:pk>/', PackageUpdateAPIView.as_view())
]

Теперь сделайте запрос к конечной точке из вашего веб-приложения. Отправьте свои данные в конечную точку.

POST package-update/2/
data={...}
...