DJANGO: mixins.DestroyModelMixin delete - method not allowed - django

I have an API that returns list of all likes.
Here is model for likes:
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['user', 'post'], name='like_once')
]
Here is my view:
class likeList(mixins.CreateModelMixin, mixins.DestroyModelMixin, generics.ListAPIView):
serializer_class = likeserializers
def get_queryset(self):
return Like.objects.all()
def post(self, request, *args, **kwargs):
try:
return self.create(request, *args, **kwargs)
except IntegrityError:
content = {'error': 'IntegrityError'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
def destroy(self, request, *args, **kwargs):
try:
return self.destroy(request, *args, **kwargs)
except IntegrityError:
content = {'error': 'IntegrityError'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
Here are my urls:
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path
from . import views as API_views
urlpatterns = [
path('users/', API_views.userList.as_view(), name = 'users'),
path('users/id=<int:id>/', API_views.userListbyID.as_view(), name = 'usersid'),
path('posts/', API_views.postList.as_view(), name = 'post'),
path('posts/id=<int:id>', API_views.postListbyID.as_view(), name = 'postid'),
path('likes/', API_views.likeList.as_view(), name = 'likes'),
path('likes/test/<int:id>', API_views.likeListbyID.as_view(), name = 'likesid'),
path('likes/<int:id>', API_views.LikeCountView.as_view(), name='likecount'),
path('follows/', API_views.followList.as_view(), name = 'follows'),
path('follows/id=<int:id>', API_views.followListbyID.as_view(), name = 'followsid'),
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.MEDIA_URL)
I call the delete api with json body {"user":userid,"post":postid}
however, I receive method Not Allowd (HTTP 405). Also I can see on the API page "Allow: GET, POST, HEAD, OPTIONS"
What am I doing wrong?
FYI: The get and create methods work as expected.

You need to use two different views for these operations. List and Create mixins do not require object pk but Destroy mixin does. You need to split Destroy mixin to another view as sampled in docs. If you do not supply pk to delete, the operation fails.

Related

ValueError - The view didn't return an HttpResponse object. It returned None instead

My view currently returns an error of
The view viajecenter.views.receive_payment didn't return an
HttpResponse object. It returned None instead.
I tried some of the solutions from other related posts here but of no luck.
Here is my function in my views.py file:
def receive_payment(request, account_code):
template_name = 'receive-payment.html'
user = request.user
account = get_object_or_404(Account, account_code=account_code)
if user.is_assigned:
puv = Puv.objects.get(assignment_id=user.assignment_id)
locations = puv.route.locations.all().order_by('distance_from_base')
context = {
'account': account,
'puv': puv,
'locations': locations,
}
return render(request, template_name, context)
else:
return user
and the corresponding url in urls.py file:
from django.urls import path
from . views import ..., receive_payment
urlpatterns = [
...
path('receive-payment/<str:account_code>', receive_payment, name="receive-payment"),
]
and my Account model:
class Account(AbstractBaseUser, PermissionsMixin):
...
account_code = models.UUIDField(default=uuid.uuid4, editable=False)
is_assigned = models.BooleanField(default=False)
Thank you for lending me your time and answering my question :)
It seems you're trying to mix and match function-based views and class-based views.
You can rewrite your view as a DetailView class-based view like so:
from django.views.generic import DetailView
class ReceivePaymentView(DetailView):
template_name = "receive-payment.html"
model = Account
slug_url_kwarg = "account_code"
slug_field = "account_code"
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
account = context["object"]
user = self.request.user
if user.is_assigned:
puv = Puv.objects.get(assignment_id=user.assignment_id)
locations = puv.route.locations.all().order_by("distance_from_base")
context["account"] = account
context["puv"] = puv
context["locations"] = locations
return context
urlpatterns = [
path('receive-payment/<account_code>', ReceivePaymentView.as_view(), name="receive-payment"),
]

Django REST Framework - method patch not allowed with UpdateModelMixin

I would like to update a record in my database with the REST framework. I'm getting an message of method not allowed for anything but "GET".
views.py
class MetadataViewTest(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = deployment.objects.all()
serializer_class = SerializerTest
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
urls.py
urlpatterns = [
path('api/metadata_test/<int:pk>/', views.MetadataViewTest.as_view())
]
serializers.py
class SerializerTest(serializers.ModelSerializer):
class Meta:
model = deployment
fields = [field.name for field in deployment._meta.fields]
I've tried the request through postman as a PATCH PUT or POST at api/metadata_test/20/
This is a simplified version, I plan on overriding the get put and delete functions.
Update your code to:
views.py
class MetadataViewTest(generics.RetrieveUpdateDestroyAPIView):
queryset = deployment.objects.all()
serializer_class = SerializerTest
urls.py
urls.py
urlpatterns = [
path('api/metadata_test/', views.MetadataViewTest.as_view()) # Should to the list, not detail
]
Call API like this PUT/PATCH: api/metadata_test/20/
It better if you use viewsets.GenericViewSet instead of generics.GenericAPIView because you are using ...ModelMixin.
Your router like this
from rest_framework import routers
api_router = routers.DefaultRouter()
api_router.register('metadata_test', MetadataViewTest, basename='metadata_test')
...
All you need to do is instead of calling /api/metadata_test/ call /api/metadata_test/12345/
Where 12345 is the id/pk of the record. I am assuming that id is the primary key of the model.

Customize the Django Swagger Framework

I'm looking for how to make a GET in django swagger framework by refClient (a unique CharField in my client's model).
I found on the internet that I have to customize the routers, I have that as routers :
from rest_framework.schemas import get_schema_view
from rest_framework_swagger.renderers import SwaggerUIRenderer, OpenAPIRenderer
schema_view = get_schema_view(
title='Swagger documentation',
renderer_classes = [OpenAPIRenderer, SwaggerUIRenderer],
)
# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter(trailing_slash=False)
router.register(r'clients/{refClient}', ClientViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^admin/', admin.site.urls),
# To show the swagger documentation
url(r'^swagger/', schema_view, name="docs"),
url(r'^api/v1/', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
But I got this error
ValueError: invalid literal for int() with base 10: 'refClient'
[21/Jul/2017 15:25:22] "GET /swagger/ HTTP/1.1" 500 133494
Should I add something to my serializers's configuration
class ClientSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Client
fields = ('refClient', 'nom', 'prenom')
Or to my views ?
class ClientViewSet(viewsets.ModelViewSet):
queryset = Client.objects.all()
serializer_class = ClientSerializer
def get_queryset(self):
""" GET : get all clients """
return Client.objects.all()
def create(self, request):
""" POST : Create a Client object """
return super(ClientViewSet, self).create(request)
def retrieve(self, request, pk=None):
""" GET : Returns a single client item """
return super(ClientViewSet, self).retrieve(request, pk)
def update(self, request, *args, **kwargs):
""" PUT : Updates a single client item """
return super(ClientViewSet, self).update(request, *args, **kwargs)
def partial_update(self, request, *args, **kwargs):
""" PATCH : Partiel update a client """
return super(ClientViewSet, self).partial_update(request, *args, **kwargs)
def destroy(self, request, pk=None):
""" DELETE : Delete a client """
return super(ClientViewSet, self).destroy(request, pk)
Basically how I can customize my swagger ?
By default django rest frameworks uses this
lookup_field = 'pk'
lookup_url_kwarg = None
You can override this in your ClientViewSet class, change to
lookup_field = 'refClient'
However if you want to support both lookups, either by 'pk' or by 'refClient' I suggest to have a different endpoint for the later one (eg. /api/client_url/ref/), or add filtering option in your list view (eg. /api/client_url?refClient='something')

How Do I get a URL parameter passed into a django CBV?

I have a urls.py file setup as follows
from django.conf.urls import patterns, include, url
from .views import *
urlpatterns = patterns('',
url(r'^$', BlogListView.as_view()),
url(r'(?P<blog_id>)\d{1,}/$', BlogDetailView.as_view())
)
with the correlating view
class BlogDetailView(View):
def get(self, request, blog_id, *args, **kwargs):
post = Blog.objects.get(post_id=blog_id).to_detail_json
return HttpResponse(json.dumps(post), mimetype='application/json')
I get an error when I visit 127.0.0.1:8000/blog/1/
ValueError at /blog/4/
invalid literal for int() with base 10: ''
but if I change
post = Blog.objects.get(post_id=blog_id).to_detail_json
to
post = Blog.objects.get(post_id=1).to_detail_json
then I get the correct response.
In case it is wanted, here is my model
from mongoengine import *
from collections import OrderedDict
import datetime
import json
class Blog(Document):
post_id = IntField(unique=True)
title = StringField(max_length=144, required=True)
date_created = DateTimeField(default=datetime.datetime.now)
body = StringField(required=True)
def __init__(self, *args, **kwargs):
self.schema = {
"title": self.title,
"date": str(self.date_created),
"id": self.post_id,
"body": self.body
}
super(Blog, self).__init__(*args, **kwargs)
#property
def to_detail_json(self):
fields = ["id","title", "date", "body"]
return {key:self.schema[key] for key in fields}
#property
def to_list_json(self):
fields = ["title", "date"]
return {key:self.schema[key] for key in fields}
Update
I changed the BlogDetailView to return
return HttpResponse(json.dumps(self.kwargs),mimetype='application/json')
and it gives me
{
blog_id: ""
}
Which leads me to believe it is something with my urls.py file, but I do not see the error.
try
post = Blog.objects.get(post_id=self.kwargs['blog_id']).to_detail_json
It turns out that
url(r'(?P<blog_id>)\d{1,}/$', BlogDetailView.as_view())
should be
url(r'(?P<blog_id>\d{1,})/$', BlogDetailView.as_view())

What is the proper implementation of 'obj_get' in Django Tastypie?

I'm quite new to Django & Tastypie. I would like to return only one of the objects from the query. I've tried almost everything and cannot seem to find the solution. Here is my code below:
class ProfileResource(ModelResource):
person = fields.ForeignKey(UserResource, 'user', full=True)
class Meta:
queryset = Person.objects.all()
resource_name = 'profile'
authentication = BasicAuthentication()
authorization = DjangoAuthorization()
serializer = Serializer(formats=['json'])
Now the part I'm having trouble with is how can I return a single user object from a single resource using request.user.
If you only want to show one resource I would probably create new resource view (named as my_profile) that would call normal detail view with user in kwargs and removed other urls:
from django.conf.urls import url
from tastypie.utils import trailing_slash
class ProfileResource(ModelResource):
...
def base_urls(self):
return [
url(r"^(?P<resource_name>%s)%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('dispatch_my_profile'), name="api_dispatch_my_profile")
]
def dispatch_my_profile(self, request, **kwargs):
kwargs['user'] = request.user
return super(ProfileResource, self).dispatch_detail(request, **kwargs)