I'm trying to use function based view to create a new object, but keeps shows this error:
HTTP 405 Method Not Allowed
Allow: GET, OPTIONS
Content-Type: application/json
Vary: Accept
{
"detail": "Method "POST" not allowed."
}
I've read alot stackoverflow questions, but none of them worked for me, here is my code:
#api_view(['POST'])
#permission_classes([permissions.IsAuthenticated])
def todo_create(request,format=None):
if request.method == 'POST':
serializer = TodoSerializer(data=request.data)
if serializer.is_valid():
serializer.owner = request.user
serializer.save()
return Response(serializer.data)
return Response(serializer.errors)
urls
urlpatterns = [
path('',todo_links,name='todo_links'),
path('lists/',todo_lists,name='todo_lists'),
path('create/',todo_create,name='todo_create'),
path('lists/<int:pk>/',todo_detail,name='todo_detail'),
path('lists/<int:pk>/update',todo_update,name='todo_update'),
path('lists/<int:pk>/delete',todo_delete,name='todo_delete'),
path('data',template,name='template'),
]
main url
from snippet.views import api_root
urlpatterns = [
path('admin/', admin.site.urls),
path('auth/',include('django.contrib.auth.urls')),
path('posts/',include('post.urls')),
path('api-auth/',include('rest_framework.urls')),
path('',include('snippet.urls')),
path('todo/',include('todos.urls'),name='todos-app'),
]
my settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
],
'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE':1
}
serializers.py
class TodoSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.HyperlinkedIdentityField(view_name='user-detail',format='html')
class Meta:
model = Todo
fields = ['id','owner','title','describe','complete']
my models.py
class Todo(models.Model):
owner = models.ForeignKey(User,on_delete=models.CASCADE,related_name='todos')
title = models.CharField(max_length=255)
describe = models.TextField(null=True,blank=True)
complete = models.BooleanField(default=False)
def __str__(self):
return self.title
i know how to implement it with class based view, i'm not sure why it shows that error with function based view.
thank you in advance
Related
I am trying to pass a list of dictionaries through my django rest framework API, loop through them, update the associated records in the model, and ultimately return the serialized data to the front end.
I am receiving the following error when submitting the list of dictionaries:
HTTP 400 Bad Request
Allow: PUT, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"non_field_errors": [
"Expected a list of items but got type \"dict\"."
]
},
{
"non_field_errors": [
"Expected a list of items but got type \"dict\"."
]
}
]
Here is the JSON I'm passing through the django rest framework API template:
[
{
"id": "1",
"order": "5"
},
{
"id": "2",
"order": "3"
}
]
Here is a view of my model:
class Waitlist(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50, blank=True)
identification = models.CharField(max_length=50)
email = models.EmailField(max_length=100, validators=[], blank=True)
mobile_phone = PhoneNumberField(blank=True)
store_number = models.PositiveSmallIntegerField()
order = models.PositiveSmallIntegerField()
status = models.PositiveSmallIntegerField()
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.first_name + ' ' + self.last_name
Here is a stripped down view of my views.py:
from django.shortcuts import render, get_object_or_404
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from .models import Waitlist
from .serializers import WaitlistOrderSerializer, WaitlistOrderListSerializer
#api_view(['PUT'])
def waitlistOrder(request):
waitlist_data = request.data
waitlist = []
for data in waitlist_data:
waitlist_id = data.get('id')
w = Waitlist.objects.get(id=waitlist_id)
waitlist.append(w)
serializer = WaitlistOrderListSerializer(
waitlist, data=waitlist_data, many=True)
if serializer.is_valid():
serializer.save()
return Response({'success': 'Waitlist order updated successfully'})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
And, here is a stripped down view of my my serializers.py
class WaitlistOrderSerializer(serializers.Serializer):
id = serializers.IntegerField()
order = serializers.IntegerField()
def update(self, instance, validated_data):
instance.order = validated_data.get('order', instance.order)
instance.save()
return instance
class WaitlistOrderListSerializer(serializers.ListSerializer):
child = WaitlistOrderSerializer()
def update(self, instances, validated_data):
for instance, data in zip(instances, validated_data):
self.child.update(instance, data)
return instances
Thanks for any suggestions.
The error is at this line:
serializer = WaitlistOrderListSerializer(
waitlist, data=waitlist_data, many=True)
It should be:
serializer = WaitlistOrderListSerializer(waitlist)
But there is no need to create ListSerializer. When you pass many=True, this ListSerializer is automatically created and WaitlistOrderSerializer will be passed as a child. Similar to what you you are doing.
So you can do:
serializer = WaitlistOrderSerializer(waitlist, many=True)
and remove your listserializer entirely.
I'm facing a issue with setting up a few generic views on this same certain URL. I would like to have a modeled my API as below:
GET /cars/ <- car list
POST /cars/ <- add new car
GET /cars/uuid/ <- get particular car
PUT /cars/uuid/ <- edit particular car
DELETE /cars/uuid/ <- delete specific car
So finally my issue is that the first include in urlconfig is able to perform the HTTP method, on rest of them I'm always getting "Method Not Allowed:". In above example it's occurs for PUT and DELETE. Is it possible to resolve URL as above or should I add prefixes like /cars/get/uuid?
urls.py
urlpatterns = [
path('', CarListApi.as_view(), name='list'),
path('', CarCreateApi.as_view(), name='create'),
path('<uuid:uuid>/', CarDeleteApi.as_view(), name='delete'),
path('<uuid:uuid>/', CarUpdateApi.as_view(), name='update'),
path('<uuid:uuid>/', CarDetailApi.as_view(), name='detail'),
]
partial views.py
class CarUpdateApi(generics.UpdateAPIView,
generics.GenericAPIView):
permission_classes = (IsOwnerOrReadOnly,)
lookup_field = 'uuid'
http_method_names = ['delete', 'get', 'post', 'put']
#extend_schema_serializer(component_name="CarUpdateInputSerializer")
class InputSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = ['plate', 'make', 'model', 'uuid']
#extend_schema(request=InputSerializer, responses={201: InputSerializer})
def put(self, request, uuid):
serializer = self.InputSerializer(data=request.data, context={'request': request})
if serializer.is_valid(raise_exception=True):
car = car_update(serializer.validated_data, uuid)
return Response(self.InputSerializer(car).data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class CarDeleteApi(generics.DestroyAPIView,
generics.GenericAPIView):
http_method_names = ['delete', 'get', 'post', 'put']
#extend_schema_serializer(component_name='CarDeleteInputSerializer')
class InputSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = ['uuid']
def get_serializer_class(self):
return self.InputSerializer
def delete(self, request, uuid, *args, **kwargs):
serializer = self.InputSerializer(data=request.data)
if serializer.is_valid(raise_exception=True) and car_delete(uuid):
return Response(status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_404_NOT_FOUND)
Any ideas how to fix this issue?
The url pattern has to have only the urls, and in the views you separated the business logic for the http methods. On urls.py:
urlpatterns = [
path('', CarApi.as_view(), name='car'),
path('<uuid:uuid>/', SpecificApi.as_view(), name='specific'),
]
and then in the views, something like:
class CarApi(APIView):
def get(self, request):
# logic for get and return car list
def post(self, request):
# logic for add card
I'm building an rest api in django rest but I'm getting this and I don't know how to solve it.
"ImproperlyConfigured at /conversation/1/
Could not resolve URL for hyperlinked relationship using view name "conversation-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field. "
models.py
class Conversation(models.Model):
storeId = models.ForeignKey(Store, on_delete=models.SET_NULL, null=True)
operatorId = models.ForeignKey(Operator, on_delete=models.SET_NULL, null=True, related_name='operatorId')
clientId = models.ForeignKey(Client, on_delete=models.SET_NULL, null=True)
operatorGroup = models.ForeignKey(Operator, to_field='group', on_delete=models.SET_NULL, null=True, related_name='operatorGroup')
views.py
class ConversationApiView(APIView):
def get(self, request, id, *args, **kwargs):
conversations = Conversation.objects.filter(id=id)
serializer = ConversationSerializer(conversations, context={'request': request}, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
serializers.py
class ConversationSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Conversation
fields = ('__all__')
ursl.py
urlpatterns = [
path('conversation/<int:id>/', ConversationApiView.as_view()),
path('chat/<int:id>/', ChatApiView.as_view()),
]
root urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('', include('api.urls')),
]
THANK YOU in advance!
Inside view, you should use ModelViewSet instead of APIView. I saw you are using generic view which is not developed for HyperlinkedModelSerializer. So, Your code should be something like this:
from rest_framework import viewsets
class ConversationApiView(viewsets.ModelViewSet):
def get(self, request, id, *args, **kwargs):
conversations = Conversation.objects.filter(id=id)
serializer = ConversationSerializer(conversations, context={'request': request}, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
And url.py should be like this:
from rest_framework import routers
router = routers.DefaultRouter()
router.register('conversation', ConversationApiView)
urlpatterns = [
path('', include(router.urls)),
]
After that Every CRUD Url auto generate like localhost:8000/conversation/,
I have a RichTextUploadField in my models which is added by ckeditor's library. I want to add a support to create simple html field related content which includes some img and audio tags. I have added a support to upload html content through admin just have no idea how to add support to create through API endpoint?
models.py
from ckeditor_uploader.fields import RichTextUploadingField
class Part(models.Model):
title = models.CharField(max_length=255)
textfield = RichTextUploadingField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-created_at', )
def __str__(self):
return str(self.explanation)
settings.py (!some parts)
...
INSTALLED_APPS = [
...
# third party installed apps
'rest_framework',
'ckeditor',
'ckeditor_uploader',
...
]
CKEDITOR_UPLOAD_PATH = "uploads/"
...
### I am using html5audio package to upload audios
CKEDITOR_CONFIGS = {
'default': {
'toolbar': 'full',
'extraPlugins': ','.join(
[
'html5audio',
]
),
},
}
Question: How can I add support to create HTML content through an REST API endpoint?
I think you should implement two different endpoints which you can upload audio or image files. Note you should configure upload_to to the folder where ckeditor has an access. Following my custom implementation for what you are asking.
models.py
from django.db import models
import os
from uuid import uuid4
def get_custom_audio_upload_path(filename, instance):
ext = str(filename).split('.')[-1]
filename = f'{uuid4()}.{ext}'
return os.path.join("uploads/custom_audio/", filename)
def get_custom_image_upload_path(filename, instance):
ext = str(filename).split('.')[-1]
filename = f'{uuid4()}.{ext}'
return os.path.join("uploads/custom_image/", filename)
class CustomAudio(models.Model):
audio = models.FileField(upload_to=get_custom_audio_upload_path)
def __str__(self):
return self.audio.name
class CustomImage(models.Model):
image = models.ImageField(upload_to=get_custom_image_upload_path)
def __str__(self):
return self.image.name
serializers.py
class CustomAudioSerializer(ModelSerializer):
class Meta:
model = CustomAudio
fields = '__all__'
class CustomImageSerializer(ModelSerializer):
class Meta:
model = CustomImage
fields = '__all__'
views.py
...
class CustomAudioViewSet(ModelViewSet):
queryset = CustomAudio.objects.all()
serializer_class = CustomAudioSerializer
permission_classes = [IsAdminUser, ]
def create(self, request, *args, **kwargs):
data = request.data
serialized = CustomAudioSerializer(data=data)
if serialized.is_valid():
serialized.save()
return Response(serialized.data, status=status.HTTP_201_CREATED)
return Response(serialized.errors, status=status.HTTP_400_BAD_REQUEST)
def get_permissions(self):
if self.action == "list" or self.action == "retrieve":
permission_classes = [AllowAny, ]
else:
permission_classes = [AllowAny, ]
return [permission() for permission in permission_classes]
class CustomImageViewSet(ModelViewSet):
queryset = CustomImage.objects.all()
serializer_class = CustomImageSerializer
permission_classes = [IsAdminUser]
def create(self, request, *args, **kwargs):
data = request.data
serialized = CustomAudioSerializer(data=data)
if serialized.is_valid():
serialized.save()
return Response(serialized.data, status=status.HTTP_201_CREATED)
return Response(serialized.errors, status=status.HTTP_400_BAD_REQUEST)
def get_permissions(self):
if self.action == "list" or self.action == "retrieve":
permission_classes = [AllowAny, ]
else:
permission_classes = [AllowAny, ]
return [permission() for permission in permission_classes]
urls.py
from rest_framework.routers import SimpleRouter
from django.urls import path, include
from someapp.views import CustomImageViewSet, CustomAudioViewSet
router = SimpleRouter()
router.register('customaudio', CustomAudioViewSet)
router.register('customimage', CustomImageViewSet)
urlpatterns = [
path('', include(router.urls)
]
Just a little explanation: when you upload a audio or image you should make a post request to the endpoint and you will get url for the result and just embed it into tag. It should retrieve it from your media folder.
This is my serializer:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('user', 'post', 'reblog',)
def create(self, validated_data):
post = Post(
user = validated_data['user'],
post = validated_data['post'],
)
post.save()
# manually create a list of "reblog" users
post.reblog.add(validated_data['user'])
return post
and this is my model:
class Post(models.Model):
user = models.ForeignKey(User)
post = models.CharField(max_length=400)
reblog = models.ManyToManyField(User)
Now, I don't want my reblog field to ever be null in the database (it can be an empty list, but not null). So I did not set "null=True" in the model field. However, the end user should not have to provide a list of "reblog" users when creating a post (I'll do that manually in the create() mtehod).
I know that here: http://www.django-rest-framework.org/api-guide/fields/#core-arguments it says I can add "required = false" to a serializer field but the fields I am using in my serializer are directly coming from the model (i.e., fields = ('reblog')).
From my understanding, I can't add "required = false" to my model because here:
https://docs.djangoproject.com/en/1.8/ref/models/fields/ it does not mention "required" as a parameter to a model field.
With that said, how can I get my "reblog" ModelSerializer field to not be required during serailzation / deserialization?
Edit: the view which handles the post request sent is:
class post_list(APIView):
"""
List all posts or create a new post.
"""
permission_classes = (IsAuthenticated,)
def get(self, request):
posts = Post.objects.all()
serializer = PostSerializer(posts, many=True)
return Response(serializer.data)
def post(self, request):
serializer = PostSerializer(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)
pip freeze:
You are using pip version 6.1.1, however version 7.1.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
Django==1.8
djangorestframework==3.1.1
uWSGI==2.0.10
Add the parameter blank=True to the model field:
class Post(models.Model):
user = models.ForeignKey(User)
post = models.CharField(max_length=400)
reblog = models.ManyToManyField(User, blank=True)
#^^^^^^^^^^
Now, the parameter may be omitted on requests, but it may never be a NULL value.
Altogether now:
urls.py (with serializers.py essentially inlined):
from django.conf.urls import url, include
from rest_framework import routers, serializers, viewsets
from proj.models import Post
# Serializers define the API representation.
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('user', 'post', 'reblog',)
def create(self, validated_data):
post = Post(
user = validated_data['user'],
post = validated_data['post'],
)
post.save()
# manually create a list of "reblog" users
post.reblog.add(validated_data['user'])
return post
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'Post', PostViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
models.py:
from django.contrib.auth.models import User
from django.db import models
class Post(models.Model):
user = models.ForeignKey(User)
post = models.CharField(max_length=400)
reblog = models.ManyToManyField(User, blank=True,related_name='reblog_users')
Now, when I send the POST request:
{
"user": 1,
"post": "foo"
}
It returns:
{
"user": 1,
"post": "foo",
"reblog": [
1
]
}
pip freeze:
Django==1.8.4
djangorestframework==3.2.4