Как поддерживать все операции REST для конечной точки в django rest framework

У меня есть модель подписки, которая выглядит как

class Subscription(models.Model):
    name = models.CharField(max_length=100)
    quantity = models.IntegerField(max_length=20)
    stripe_id = models.CharField(max_length=100)
    user = models.ForeignKey(User)

Я хотел бы создать конечную точку, которая позволяет POST, PATCH, DELETE, GET

Итак, я сделал следующее:

views.py

class SubscriptionDetail(viewsets.ModelViewSet):
    serializer_class = SubscriptionSerializer
    permission_classes = (IsAuthenticated,)
    queryset = Subscription.objects.all()

serializers.py

class SubscriptionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subscription
        fields = ('name','quantity', 'stripe_id')

    def update(self, instance, validated_data):
        print "In update"

    #how do I write create and delete?

urls.py

 subscription = SubscriptionDetail.as_view({
     'patch': 'update'
 })
url(r'^rest-auth/subscription/$', subscription, name='something'),

Вопросы

  • Используя вышеприведенное при отправке запроса PATCH, я получаю сообщение об ошибке. Как я могу это исправить?

Ожидаемое представление SubscriptionDetail для вызова с ключевым словом URL аргумент с именем "pk". Исправьте URL conf или установите .lookup_fieldатрибут на представлении правильно.

  1. При отправке запроса на исправление я также хотел бы отправить поле "email", которое не относится к модели подписки. Возможно ли это сделать? Мне нужно поле email в операции POST (create), чтобы я знал, к кому принадлежит подписка.

Ответ 1

Самый простой способ - сделать это таким образом.

сохранить класс моделей тем же самым

views.py

from rest_framework import viewsets
#impost serializer and model class for subscription

class SubscriptionViewSet(viewsets.ModelViewSet):

    serializer_class = SubscriptionSerializer

    def get_queryset(self):
        queryset = Subscription.objects.all()
        #if you need to get subscription by name
        name = self.request.QUERY_PARAMS.get('name', None)
        if name is not None:
            queryset = queryset.filter(name=name)

        return queryset 

serializers.py

 class SubscriptionSerializer(serializers.ModelSerializer):
     class Meta:
         model = Subscription
         fields = ('name','quantity', 'stripe_id')

 # django will handle get, delete,patch, update for you ....
 # for customization you can use def update or def create ... to do whatever you need
 # def create(self, validated_data):
 # you can handle the email here
 # and something like subscription=        Subscription (name=validated_data['name'],vendor=validated_data['quantity']...)
 # subscription.save()
 # it will save whatever you want

urls.py

#use the router to handle everything for you
from django.conf.urls import patterns, include, url
from rest_framework import routers
#import your classes 


router = routers.DefaultRouter()
router.register(r'subscription', views.SubscriptionViewSet,base_name='subscription')

urlpatterns = patterns('',
url(r'^', include(router.urls)),

)

Ответ 2

Для создания объекта вы должны реализовать функцию create, как описано в официальной документации, здесь здесь. Для исправления вы можете использовать частичный аргумент изнутри класса вида:

SubscriptionSerializer(subscription, data={'something': u'another', partial=True)

Для удаления подписки, которая может быть выполнена, когда вы получите вызов delete как это в классе вида:

if request.METHOD == 'DELETE':
       subscription = Subscription.objects.get(pk=pk)
       subscription.delete()

См. этот учебник для полного примера

Более того, я думаю, что вы должны включить поле "id" в Meta-классе SubscriptionSerialiser, иначе будет сложно выполнять обновления/удаления. Надеюсь, это немного помогло.

Cheers, Tobbe

Ответ 3

  • Если вы хотите использовать метод, позволяющий выполнять эти операции, вы должны использовать @detail_route(), где вы также можете сказать, какие методы вы будете использовать, например в документах:

    @detail_route (методы = [ 'Post']) def set_password (self, request, pk = None):   user = self.get_object()   serializer = PasswordSerializer (data = request.data)       ...

Чтобы иметь возможность использовать их, вы должны добавить следующий декоратор

@detail_route(methods=['post', 'patch'])

  1. Чтобы добавить другие параметры, вы можете сделать это для параметра .save(). Вам просто нужно указать имя этого, и они просто переопределяют вашу .save() модель, чтобы проверить, принадлежит ли этот адрес пользователю или нет, который пытается выполнить подписку. Здесь я вставляю вам то, что говорится в документах Django Rest:

"Передача дополнительных атрибутов в .save()

...

Вы можете сделать это, включив дополнительные аргументы ключевого слова при вызове .save(). Например:

serializer.save(owner=request.user)

Здесь я оставляю вам ссылку для получения дополнительной информации:

http://www.django-rest-framework.org/api-guide/serializers/#passing-additional-attributes-to-save

Ответ 4

  • Используя приведенное выше при отправке запроса PATCH, я получаю сообщение об ошибке. Как я могу это исправить?

Ожидаемое представление SubscriptionDetail для вызова с ключевым словом URL аргумент с именем "pk". Исправьте URL conf или установите .lookup_field атрибут на представлении правильно.

Ошибка вызвана тем, что в отличие от запроса create patch/update требуется pk знать, какой объект обновить. Вот почему вам нужно указать значение pk для него. Таким образом, ваш url для PUT, DELETE и patch должен иметь, по крайней мере, именованный параметр, подобный этому -

subscription = SubscriptionDetail.as_view({
     'patch': 'update'
 })
url(r'^rest-auth/subscription/(?<pk>(\d+))$', subscription, name='something'),

пример url будет - rest-auth/subscription/10, где 10 - это pk или id объекта. Затем Django Rest Framework загрузит объект, который будет обновляться.

  1. При отправке запроса на исправление я также хотел бы отправить поле "email", которое не относится к модели подписки. Возможно ли это сделать? Мне нужно поле электронной почты в операции POST (create), чтобы я знал, к какому пользователю принадлежит подписка.

Чтобы добавить пользовательские параметры, сначала объявите свойство в сериализаторе, лучше сохранить его required=False, чтобы другой запрос не выдавал ошибку -

class SubscriptionSerializer(serializers.ModelSerializer):
    custom_field = serialiers.BooleanField(required=False)

    class Meta:
        model = Subscription
        fields = ('name','quantity', 'stripe_id')

    def update(self, instance, validated_data):
        print "In update"

пока этого достаточно для рамки django rest, чтобы принять поле custom_field, и вы найдете значение в методе update. Чтобы получить значение, поместите его из атрибутов, предоставленных фреймворком, как это -

    def update(self, instance, validated_data):
        custom_field = validated_data.pop('custom_field', None)
        if custom_field is not None:
            # do whatever you like with the field
        return super().update(instance, validated_data)
        # for python < 3.0 super(SubscriptionSerializer, self).update(instance, validated_data)

Ответ 5

  • Когда вы переопределили (я не знаю, что это правильное сопряжение переопределения метода), метод обновления, вы остановили возможность PUT или PATCH и объекта. Ваш новый метод выводит только "In update", но не сохраняет экземпляр. Посмотрите на метод обновления из сериализатора. ОбъектModelSerializer:

    def update(self, instance, validated_data):
        raise_errors_on_nested_writes('update', self, validated_data)
    
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance
    

    Обратите внимание на последние несколько строк, где экземпляр сохраняется со значениями и затем возвращается. Удалите метод обновления объекта SubscriptionSerializer. Это позволяет создавать родительские объекты, обновлять, извлекать и удалять их магию, которая поддерживает обновления PATCH и PUT. Следующая проблема заключается в том, что ваш urls.py использует Django, а не RATE-инфраструктурный маршрутизатор. Измените его так:

    from rest_framework.routers import DefaultRouter
    router = DefaultRouter()
    router.register(r'subscription', SubscriptionDetail)  
    

Это должно решить проблему обновления патча.

  1. Я не думаю, что вы можете добавить поле электронной почты в свой метод патча без атрибута в модели подписки. Это только догадка с моей стороны, и я могу ошибаться. Направляется ли поле электронной почты на что-либо на какой-либо объект? Вы можете использовать ForeignKey для его сопоставления?

Я надеюсь, что это сработает для вас, удачи!

Ответ 6

В view.py вам просто нужно установить класс с помощью:

class SubscriptionDetail(mixins.CreateModelMixin,
        mixins.ListModelMixin,
        mixins.RetrieveModelMixin,
        mixins.UpdateModelMixin,
        generics.GenericAPIView):

и добавьте это для исправления .lookup_field:

    def update(self, request, *args, **kwargs):
        log.error("OBJ update kwargs= %s , data = %s" % (kwargs,  str(request.data)))
        pk = request.data.get('id')
        if (kwargs.get('pk') is not None):
            kwargs['pk'] = request.data.get('id')
        self.kwargs['pk'] = request.data.get('id')
        return super().update(request, *args, **kwargs)

и добавьте поддержку методов, которые вы хотите:

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

#    def get(self, request, *args, **kwargs):
#        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

#    def patch(self, request, *args, **kwargs):
#        return self.partial_update(request, *args, **kwargs)
#
#    def delete(self, request, *args, **kwargs):
#        return self.destroy(request, *args, **kwargs)

только изменить, что остается - получить для списка или получить для извлечения на элементе, но должно быть легко добавить что-то, если у нас есть один pk, который мы можем назвать self.retrieve else, мы можем называть self.list