Как включить посреднические модели в мой Django Rest Framework API - PullRequest
0 голосов
/ 16 января 2019

Я сейчас нахожусь в процессе изучения Django Rest Framework, и я наткнулся на сложную часть, которую я не могу заставить работать в нашем проекте. Наш проект - это вымышленный интернет-магазин, где вы можете положить собачек в корзину и заказать их. Наш интерфейс взаимодействует с бэкэндом через Django Rest Framework. У нас есть три основные модели: пользователи, заказы и щенки. Поскольку у каждого заказа есть список щенков, и у каждого щенка есть свое количество, нам была нужна промежуточная модель для двух, поскольку простой ManyToManyField, очевидно, не может обрабатывать дополнительные столбцы. Это значительно усложняет ситуацию (по крайней мере, для новичка в Django).

Как я могу отразить взаимосвязь в сериализаторах и представлениях и как мне сформировать объект JSON, отправляемый из внешнего интерфейса?

также:

Где бы рассчитать общую стоимость заказа?

Нужно ли мне представление OrderDetail, даже если я не хочу редактировать или получать один конкретный заказ или достаточно представления OrderList?

Здесь есть вопрос, который решает аналогичную проблему: Включить посредника (через модель) в ответы в Django Rest Framework Я реализовал это в коде и получил его сейчас, благодаря комментарию Дэниэла Роузмана. Создание заказов вручную через / admin и получение их теперь работает как надо. Я все еще не могу создавать заказы через API.

Это мой код:

models.py:

from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver


class Puppy(models.Model):
    name = models.CharField(max_length=30)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    image_url = models.CharField(max_length=200)
    age = models.IntegerField(null=True)
    weight = models.IntegerField(null=True)
    description_de = models.CharField(max_length=500, null=True)
    description_en = models.CharField(max_length=500, null=True)

    def __str__(self):
        return self.name


class Order(models.Model):
    total_price = models.DecimalField(max_digits=9, decimal_places=2)
    puppies = models.ManyToManyField(Puppy, through='PuppyOrder')
    date = models.DateTimeField()
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='orders')

    def save(self, *args, **kwargs):
        """
        Create a new Order
        """
        print("An order is being saved: " + str(Order.id))
        super(Order, self).save(*args, **kwargs)

    def __str__(self):
        return str(self.id)


class PuppyOrder(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE)
    puppy = models.ForeignKey(Puppy, on_delete=models.CASCADE)
    amount = models.IntegerField()

    def __str__(self):
        return self.id


class Address(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    # Some more address fields ...


@receiver(post_save, sender=User)
def create_user_address(sender, instance, created, **kwargs):
    if created:
        Address.objects.create(user=instance)


@receiver(post_save, sender=User)
def save_user_address(sender, instance, **kwargs):
    instance.address.save()

views.py:

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from .serializers import UserSerializer, UserSerializerWithToken, OrderSerializer, PuppySerializer
from .permissions import IsOwner


@api_view(['GET'])
def api_root(request, format=None):
    return Response({
        'users': reverse('user-list', request=request, format=format),
        'orders': reverse('order-list', request=request, format=format),
        'puppies': reverse('puppy-list', request=request, format=format),
    })


class OrderList(generics.ListCreateAPIView):
    http_method_names = ["get", "post"]
    queryset = Order.objects.all()
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
    serializer_class = OrderSerializer

    def get_queryset(self):
        # Validation Code and getting self.request.user from jwt token ...
        if self.request.user.is_anonymous:
            return None
        return Order.objects.all().filter(user=self.request.user)

    def perform_create(self, serializer):
        # Validation Code and getting self.request.user from jwt token ...
        serializer.save(user=self.request.user)


class OrderDetail(generics.RetrieveUpdateDestroyAPIView):
    http_method_names = ["get", "post"]
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
    queryset = Order.objects.all()
    serializer_class = OrderSerializer


class UserList(generics.ListAPIView):
    http_method_names = ["get", "post"]
    queryset = User.objects.all().select_related('address')
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    http_method_names = ["get", "post"]
    queryset = User.objects.all().select_related('address')
    serializer_class = UserSerializer


class PuppyList(generics.ListAPIView):
    http_method_names = ["get"]
    permission_classes = (permissions.AllowAny,)
    queryset = Puppy.objects.all()
    serializer_class = PuppySerializer


class PuppyDetail(generics.RetrieveAPIView):
    http_method_names = ["get"]
    permission_classes = (permissions.AllowAny,)
    queryset = Puppy.objects.all()
    serializer_class = PuppySerializer

serializers.py:

from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
from django.contrib.auth.models import User
from .models import Order, Puppy, PuppyOrder


class UserSerializer(serializers.ModelSerializer):
    orders = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = User
        fields = ('id', 'username', 'orders')


class UserSerializerWithToken(serializers.ModelSerializer):

# Neccessary code for generating a JWT token ...


class PuppySerializer(serializers.ModelSerializer):
    description = serializers.SerializerMethodField()

    def get_description(self, puppy):
        return {'DE': puppy.description_de, 'EN': puppy.description_en}

    class Meta:
        model = Puppy
        fields = ('id', 'name', 'price', 'image_url', 'age', 'weight', 'description')


class PuppyOrderSerializer(serializers.ModelSerializer):
    puppy = serializers.ReadOnlyField(source='order.puppy')
    order = serializers.ReadOnlyField(source='order.order')

    class Meta:
        model = PuppyOrder
        fields = ('id', 'puppy', 'amount')


class OrderSerializer(serializers.ModelSerializer):
    user = serializers.ReadOnlyField(source='user.username')
    puppies = PuppyOrderSerializer(source='puppyorder_set', many=True) 
    # This is currently producing an error
    # puppies = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Order
        fields = ('id', 'total_price', 'puppies', 'date', 'user')

Не стесняйтесь сообщить мне, видите ли вы в моем коде что-то, что может быть улучшено или не имеет смысла. Заранее спасибо, любая помощь будет высоко ценится!

1 Ответ

0 голосов
/ 16 января 2019

Вы сломали вещи, указав related_names на своих ForeignKeys в модели PuppyOrder. Удалить те.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...