Как поддерживать все операции 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
атрибут на представлении правильно.
- При отправке запроса на исправление я также хотел бы отправить поле "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'])
- Чтобы добавить другие параметры, вы можете сделать это для параметра .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 загрузит объект, который будет обновляться.
- При отправке запроса на исправление я также хотел бы отправить поле "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)
Это должно решить проблему обновления патча.
- Я не думаю, что вы можете добавить поле электронной почты в свой метод патча без атрибута в модели подписки. Это только догадка с моей стороны, и я могу ошибаться. Направляется ли поле электронной почты на что-либо на какой-либо объект? Вы можете использовать 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