I have registerd some of my endpoints through routers and written others in classic style, by classic style I mean that they are not registerd through router but directly written in path. Now problem is that api root lists only the endpoints that are registered through router and i want both to be listed.
app/views.py
from uuid import UUID
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from event.models import Event
from event.serializers import EventSerializer
from food_album.models import FoodAlbums
from food_album.serializers import FoodAlbumsSerializer
from restaurant.models import RestaurantProfile
from restaurant.serializers import RestaurantProfileSerializer
from .serializers import *
customer_favourites = {
0: [RestaurantProfile, RestaurantProfileSerializer],
1: [Event, EventSerializer],
2: [FoodAlbums, FoodAlbumsSerializer]
}
class CustomerProfileViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = CustomerProfile.objects.all()
serializer_class = CustomerProfileSerializer
lookup_field = 'public_id'
def get_serializer_class(self):
if self.action in ['create', 'update']:
return CustomerProfileCreateSerializer
return self.serializer_class
class RewardTypesViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = RewardTypes.objects.all()
serializer_class = RewardTypesSerializer
lookup_field = 'public_id'
class RewardActionsViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = RewardActions.objects.all()
serializer_class = RewardActionsSerializer
lookup_field = 'public_id'
class ReportItemViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = ReportItem.objects.all()
serializer_class = ReportItemSerializer
lookup_field = 'public_id'
class CustomerFavouritesViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
serializer_class = CustomerFavouritesSerializer
permission_classes = (IsAuthenticated,)
lookup_field = 'public_id'
def perform_create(self, serializer):
serializer.save(customer=self.request.user.customer)
def get_queryset(self):
user = self.request.user
if user.customer:
return CustomerFavourites.objects.filter(customer=user.customer)
else:
return None
class UserFeedsViewSet(viewsets.ViewSet):
"""
A viewset that provides the standard actions
"""
serializer_class = RestaurantProfileSerializer
def retrieve(self, request):
r = RestaurantProfile.objects.all()
serializer = RestaurantProfileSerializer(r, many=True)
return Response(serializer.data)
class CustomerBookmarkViewSet(viewsets.ViewSet):
"""
A viewset that provides the standard actions
"""
permission_classes = (IsAuthenticated,)
def list(self, request, reference_type):
bookmarks = CustomerFavourites.objects.filter(type=0,
reference_type=reference_type).values_list(
'reference_id', flat=True)
public_ids = [UUID(id_) for id_ in bookmarks]
r = customer_favourites.get(reference_type)[0].objects.filter(
public_id__in=public_ids).all()
serializer = customer_favourites.get(reference_type)[1](r, many=True)
return Response(serializer.data)
class CustomerFavouriteViewSet(viewsets.ViewSet):
"""
A viewset that provides the standard actions
"""
permission_classes = (IsAuthenticated,)
def list(self, request, reference_type):
favs = CustomerFavourites.objects.filter(type=1,
reference_type=reference_type).values_list(
'reference_id', flat=True)
public_ids = [UUID(id_) for id_ in favs]
r = customer_favourites.get(reference_type)[0].objects.filter(
public_id__in=public_ids).all()
serializer = customer_favourites.get(reference_type)[1](r, many=True)
return Response(serializer.data)
app/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register(r'profile', views.CustomerProfileViewSet,
base_name='profile')
router.register('reward-actions', views.RewardActionsViewSet, base_name='reward_actions')
router.register('reward-types', views.RewardTypesViewSet, base_name='reward_types')
router.register('report-item', views.ReportItemViewSet, base_name='report_item')
router.register('toggle-favourites', views.CustomerFavouritesViewSet, base_name='toggle_favourites')
urlpatterns = [
path('customer/', include(router.urls)),
path('customer/user-feeds/',
views.UserFeedsViewSet.as_view({'get': 'retrieve'})),
path('customer/favourites/<int:reference_type>/',
views.CustomerFavouriteViewSet.as_view({'get': 'list'})),
path('customer/bookmarks/<int:reference_type>/',
views.CustomerBookmarkViewSet.as_view({'get': 'list'}))
]
You need to append router.urls to the list of existing views as
urlpatterns += router.urls. For more information, kindly look into - https://www.django-rest-framework.org/api-guide/routers/
Related
How to retrieve an object using multiple parameters in url? Example : 'customer/3/order/1/' or 'customer/<int:customer_pk>/order/<int:pk>/'
Using shell i'm able to get the object like this but i can't seem to do it from the views.
>>> from temp.models import Customer, Order
>>> from temp.serializers import CustomerSerializer, OrderSerializer
>>> q = Order.objects.filter(customer=1).get(order_number=2)
>>> q.order_name
'fruit'
views.py
from rest_framework import generics
from rest_framework.views import APIView
from .models import *
from .serializers import *
class OrderDetailView(generics.RetrieveDestroyAPIView):
serializer_class = OrderSerializer
def get_queryset(self):
queryset = Order.objects.filter(customer_id=self.kwargs["customer_pk"]).get(order_number=self.kwargs["pk"])
return queryset
urls.py
from django.urls import path
from .views import *
urlpatterns = [
path('<int:customer_pk>/orders/<int:pk>/', OrderDetailView.as_view()),
]
We can override queryset:
class OrderDetailView(generics.RetrieveDestroyAPIView):
lookup_field = "pk"
serializer_class = OrderSerializer
def get_queryset(self):
return Order.objects.filter(customer_id=self.kwargs["customer_pk"])
Just it, retrieve object Order already handled by get_object() method.
If want more secure, make sure customer is valid (customer is available):
from django.shortcuts import get_object_or_404
class OrderDetailView(generics.RetrieveDestroyAPIView):
lookup_field = "pk"
serializer_class = OrderSerializer
def get_customer(self):
return get_object_or_404(
Customer.objects.all(),
id=self.kwargs["customer_pk"]
)
def get_queryset(self):
customer = self.get_customer()
return Order.objects.filter(customer=customer)
filter Query with multiple parameters
queryset = Order.objects.filter(customer_id=self.kwargs["customer_pk"],order_number=self.kwargs["orde_pk"])
I want to create a serializer without model.
myproject/myapp/views.py :
from rest_framework import viewsets
from rest_framework.response import Response
from .serializers import DemoSerializer
class DemoView(viewsets.ModelViewSet):
def get(self, request):
my_data = [{"name": "Harsha"}, {"name": "Harsha"}]
results = DemoSerializer(my_data, many=True).data
return Response(results)
myproject/myapp/urls.py
from django.urls import path, include
from . import views
from rest_framework import routers
router = routers.DefaultRouter()
router.register('demo', views.DemoView, basename='Demo')
urlpatterns = [
path('', include(router.urls)),
]
myproject/myapp/serializer.py
from rest_framework import serializers
class DemoSerializer(serializers.Serializer):
name = serializers.CharField()
When I goto URL: http://localhost:8000/demo/, it is giving me following error:
DemoView should either include a queryset attribute, or override the get_queryset() method.
How can I resolve this error?
from .models import Demo
class DemoView(viewsets.ModelViewSet):
def get_queryset(self):
return Demo.objects.all()
def get(self, request):
my_data = [{"name": "Harsha"}, {"name": "Harsha"}]
results = DemoSerializer(my_data, many=True).data
return Response(results)
If you don't want to create a model then you should extend viewsets.ViewSet.
class DemoView(viewsets.ViewSet):
def get(self, request):
my_data = [{"name": "Harsha"}, {"name": "Harsha"}]
results = DemoSerializer(my_data, many=True).data
return Response(results)
I think you need to customize list method if you inherit from the ModelViewSet.
class DemoView(viewsets.ModelViewSet):
queryset = Demo.objects.all()
def list(self, request):
my_data = [{"name": "Harsha"}, {"name": "Harsha"}]
results = DemoSerializer(my_data, many=True).data
return Response(results)
I am trying to create Rest API using DRF. Wanted to get detail by using UniqueId. I can use PK and get the output but wanna use unique id (token_id in my jobs Model) created in the model field.
Models.py
from django.db import models
from rest_api.util import unique_slug_generator
from django.urls import reverse
# Create your models here.
class Jobs(models.Model):
token_id = models.CharField(max_length=64, unique=True)
name = models.CharField(max_length=100)
url = models.URLField()
environment = models.CharField(max_length=100, null=True)
runtestnow = models.BooleanField(default=False)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('token_id', kwargs={'token_id':self.token_id})
class Queue(models.Model):
tokenid = models.ForeignKey(Jobs, on_delete=models.CASCADE)
date = models.DateField(auto_now=True)
def __str__(self):
return self.tokenid
class VM(models.Model):
vm_count = models.IntegerField(default=120)
def __str__(self):
return f"VM Count: {self.vm_count}"
Urls.py
from django.urls import path, include
from . import views
from .views import (RegisterTestMethodView,
RegisterTestMethodViewDetail,
CheckStatusView,
ReleaseTokenView
)
from rest_framework import routers
from rest_framework.authtoken.views import obtain_auth_token
from rest_framework.urlpatterns import format_suffix_patterns
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
router = routers.DefaultRouter()
router.register('jobs', views.JobsView)
urlpatterns = [
path('', include(router.urls)),
path('registertestmethod/', RegisterTestMethodView.as_view()),
path('registertestmethod/<int:pk>/',
RegisterTestMethodViewDetail.as_view()),
path('checkstatus/<int:pk>', CheckStatusView.as_view()),
path('checkstatus/<token_id>', CheckStatusView.as_view()),
path('releasetoken/<int:pk>', ReleaseTokenView.as_view()),
]
Serializers.py
from rest_framework import serializers
from .models import Jobs
from django.utils.crypto import get_random_string
class JobsSerializers(serializers.HyperlinkedModelSerializer):
token_id = serializers.CharField(default=get_random_string(length=25))
class Meta:
model = Jobs
fields = ('id', 'name', 'url','runtestnow','token_id')
class CheckStatusSerializers(serializers.HyperlinkedModelSerializer):
class Meta:
model = Jobs
fields = ('id','runtestnow')
class RegisterTestMethodSerializers(serializers.HyperlinkedModelSerializer):
class Meta:
model = Jobs
fields = ('id', 'name', 'url', 'environment', 'runtestnow', 'token_id')
Views.py
from rest_framework import viewsets, permissions, authentication
from .models import Jobs, VM, Queue
from .serializers import (JobsSerializers,
RegisterTestMethodSerializers,
CheckStatusSerializers)
import json
import datetime
import collections
collections.deque()
#3rd time
from rest_framework import generics
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authentication import (SessionAuthentication,
BasicAuthentication,
TokenAuthentication)
from rest_framework.permissions import IsAuthenticated
from django.utils.crypto import get_random_string
with open('greet/static/greet/static/config.json', 'r') as
data_config:
data_ready = json.load(data_config)
totalVM = int(data_ready['totalVM'])
max_vm = int(data_ready['max_vm_count'])
grid_name = (data_ready['GridNameForDev'])
min_vm = int(data_ready['min_vm_count'])
class RegisterTestMethodView(APIView):
# authentication_classes = [SessionAuthentication,
TokenAuthentication, BasicAuthentication]
# permission_classes = [IsAuthenticated] # No access (not even
read if not authorized)
def get(self, request):
snippets = Jobs.objects.all()
serializer = RegisterTestMethodSerializers(snippets,
many=True)
return Response(serializer.data)
def post(self, request):
queue = VM.objects.all()
id_token = get_random_string(length=25)
if not queue:
queue = VM(vm_count=totalVM)
queue.save()
else:
for queue_obj in queue:
queue = queue_obj
if queue.vm_count > min_vm:
queue.vm_count -= max_vm
queue.save()
request.data["token_id"] = id_token
request.data["runtestnow"] = True
else:
request.data["token_id"] = id_token
request.data["runtestnow"] = False
serializer = RegisterTestMethodSerializers(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({'TokenId': serializer.data['token_id'],
'RunTestNow': serializer.data['runtestnow'],
'VmCount': queue.vm_count,
'GridName': grid_name, 'Vm_left':
queue.vm_count}, status=status.HTTP_201_CREATED)
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
class JobsView(viewsets.ModelViewSet):
queryset = Jobs.objects.all()
serializer_class = JobsSerializers
lookup_field = 'token_id'
class CheckStatusView(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk, token_id):
try:
return Jobs.objects.get(pk=pk)
except Jobs.DoesNotExist:
raise Http404
def get(self, request, token_id):
pk = request.GET.get('pk')
print(pk)
queue = VM.objects.get()
job_list = Jobs.objects.exclude(runtestnow=True)
filtered = Jobs.objects.filter(id=pk)
next_q = job_list.order_by('id').first()
waitlist = 1
return Response(
{"tokenid": token_id, "Runtestnow": False, "VMcount":
queue.vm_count,
'GridName': grid_name, 'waitlist #': waitlist,
'Vm_left':
queue.vm_count}, status=status.HTTP_201_CREATED)
def post(self, request, pk):
queue = VM.objects.get()
vm_count = queue.vm_count
job_list = Jobs.objects.exclude(runtestnow=True)
filtered = Jobs.objects.filter(id=pk)
next_q = job_list.order_by('id').first()
waitlist = int(pk-next_q.id + 1)
if next_q:
print(next_q.id)
if next_q.id == pk and queue.vm_count > min_vm:
queue.vm_count -= max_vm
filtered.update(runtestnow=True)
queue.save()
vm_used = max_vm
else:
filtered.update(runtestnow=False)
queue.save()
vm_used = 0
snippet = self.get_object(pk)
serializer = RegisterTestMethodSerializers(snippet)
return Response({"tokenid": serializer.data["id"],
"Runtestnow": serializer.data['runtestnow'], "VMcount":
vm_used,
'GridName': grid_name, 'waitlist #': waitlist ,
'Vm_left': queue.vm_count},
status=status.HTTP_201_CREATED)
class ReleaseTokenView(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return Jobs.objects.get(pk=pk)
except Jobs.DoesNotExist:
raise Http404
def delete(self, request, pk, format=None):
queue = VM.objects.get()
if not queue:
queue = VM(vm_count=totalVM)
if not self.get_object(pk):
print("Not Method Called...")
return
if queue.vm_count < totalVM :
queue.vm_count += max_vm
queue.save()
elif queue.vm_count + max_vm > totalVM:
queue.vm_count = totalVM
queue.save()
snippet = self.get_object(pk)
snippet.delete()
return Response(data={'Released': True},
status=status.HTTP_204_NO_CONTENT)
I can get information using but I wanna user token_id. I can do that using Serializers as it is in jobs.
If I do
localhost/jobs/xJcn8XxF2g9DmmwQwGS0Em754. # --> I get the output but I
# wanna use and I am aware
#that this will return all CRUD methods but how do I apply the
#business logic in Serializers.
localhost/checkstatus/xJcn8XxF2g9DmmwQwGS0Em754 . # --> I wanna
# apply business logic before getting the output. Which
# returns Response related to the PK as well.
What is the best way to do it?
Do I add it on serializer.py(how) or views.py?
I would appreciate it if you provide any helpful documents.
You should set lookup_field as token_id in your serializer and viewset.
Here is answer Django Rest Framework: Access item detail by slug instead of ID
Actually I was able to do it by some research. It seems like I have to pass a unique id (token_id) in URL and query using the same unique id (token_id) on the views.py. I was aware that there is modelviewset that does it effortlessly as mentioned by Ishak, but I wanted to use APIView and on top of that I wanted some business logic to be added. I probably have to do some more research on how to add logic to modelviewset. Here is my Solution.
Views.py
def get(self, request, token_id):
get_job = Jobs.objects.get(token_id=token_id)
pk = get_job.id
job_list = Jobs.objects.exclude(runtestnow=True)
next_q = job_list.order_by('id').first()
queue = VM.objects.get()
waitlist = int(pk) - int(next_q.id)
if waitlist == 1:
waitlist = 'You are next on the queue. :)'
return Response(
{"tokenid": token_id, "Runtestnow": False, "VMcount":
queue.vm_count,
'GridName': grid_name, 'waitlist #': waitlist, 'Vm_left':
queue.vm_count}, status=status.HTTP_201_CREATED)
Urls.py
path('checkstatus/<token_id>', CheckStatusView.as_view()),
We can always use the slug field, but I really wanted token_id as input. This should work fine for me as of now.
There might be some other way as well. Feel free to share.
I have a model for Articles which has a lot of fields. When someone fetches a specific article by id I want to send almost all of the fields back to the client.
However, when the articles list is generated I don't want to send all of the articles with all of that data, but rather limit each article to a few important listing fields, and forgo long fields like content etc..
Can I achieve these with an elegant tweak to the the django_restframework.viewsets module, or should I just build the api methods myself using django_restframwork.generics instead?
example:
# articles/api/urls.py #
# -------------------- #
from articles.api.views import ArticlesViewSet
from django.urls import path
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'', ArticlesViewSet, basename='articles')
urlpatterns = router.urls
# articles/api/views.py #
# --------------------- #
from rest_framework.viewsets import ModelViewSet
from ..models import Article
from .serializers import ArticleSerializerFull, ArticleSerializerShort
class ArticlesViewSet(ModelViewSet):
# Perhaps some conditional code here?
serializer_class = ArticleSerializerFull
queryset = Article.objects.all()
You can use get_serializer_class() method in DRF to solve the problem...
serializers.py
class ArticleFullSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('__all__')
class ArticleShortSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('field1', 'field2' ... 'fieldn')
views.py
from rest_framework.viewsets import ModelViewSet
class ArticleViewSet(ModelViewSet):
serializer_class = ArticleSerializerFull
queryset = Article.objects.all()
def get_serializer_class(self):
if self.action == 'list':
serializer = ArticleShortSerializer
else:
serializer = ArticleFullSerializer
return serializer
**i believe you can achieve this by using generics views check_here **
class ArticleFullSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('__all__')
class ArticleShortSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('field1', 'field2'..... 'fieldn')
and in your views
class ArticleListView(ListAPIView):
permission_classes = (permissions.IsAuthenticated,)
queryset = Article.objects.all()
serializer_class = ArticleShortSerializer
class ArticleDetailView(RetrieveAPIView):
permission_classes = (permissions.IsAuthenticated,)
queryset = Article.objects.all()
serializer_class = ArticleFullSerializer
lookup_field = 'pk'
****and in urls for details****
path('detail/<int:pk>/', ArticleDetailView.as_view(), name='article-detail'),
I need to pass a set of config-settings (key-value pairs) through the django rest_framework to an api-enpoint. Read-only is fine. Django 1.7, Python3 and rest-framework v3.0.5.
I have pip installed django-solo, and I can access to it in the admin section, so I assume it works. I have set up a route which works, and now I need to make the 'View-like-thing' that actually returns the data.
This is as far as I have gotten (definitely wrong):
class ConfigViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
model = SiteConfiguration
permission_classes = (IsAuthenticatedOrReadOnly,)
def get_serializer_class(self):
# What goes here? I want _all_ the settings
def get_object(self):
obj = self.model.get_solo()
self.check_object_permissions(self.request, obj)
return obj
def list(self, *args, **kwargs):
return self.retrieve(*args, **kwargs)
Any help and hints appreciated.
PS! This is the config/models.py that has the settings:
from django.db import models
from solo.models import SingletonModel
class SiteConfiguration(SingletonModel):
site_name = models.CharField(max_length=255, default='Site Name')
maintenance_mode = models.BooleanField(default=False)
def __str__(self):
return u"Site Configuration"
class Meta:
verbose_name = "Site Configuration"
Oki, here goes:
1) pip-install 'django-solo'.
2) Make a new app with manage.py startapp config.
2a) File config/models.py:
from django.db import models
from solo.models import SingletonModel
class SiteConfiguration(SingletonModel):
site_name = models.CharField(max_length=255, default='Site Name')
maintenance_mode = models.BooleanField(default=False)
def __str__(self):
return u"Site Configuration"
class Meta:
verbose_name = "Site Configuration"
2b) File config/views.py:
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import SiteConfiguration
config = SiteConfiguration.get_solo()
class SiteConfiguration(APIView):
permission_classes = []
def get(self, request, format=None):
"""
Return site configuration key-values.
"""
return Response({
'name': config.site_name
})
3) The next problem is adding the view to the router. Using the DefaultRouter, one cannot register APIviews, so this guy had a simple HybridRouter solution [https://stackoverflow.com/a/23321478/1008905].
3a) Make a custom_routers.py in your project folder (where your main urls.py-file is located), with this content:
from rest_framework import routers, views, reverse, response
class HybridRouter(routers.DefaultRouter):
def __init__(self, *args, **kwargs):
super(HybridRouter, self).__init__(*args, **kwargs)
self._api_view_urls = {}
def add_api_view(self, name, url):
self._api_view_urls[name] = url
def remove_api_view(self, name):
del self._api_view_urls[name]
#property
def api_view_urls(self):
ret = {}
ret.update(self._api_view_urls)
return ret
def get_urls(self):
urls = super(HybridRouter, self).get_urls()
for api_view_key in self._api_view_urls.keys():
urls.append(self._api_view_urls[api_view_key])
return urls
def get_api_root_view(self):
# Copy the following block from Default Router
api_root_dict = {}
list_name = self.routes[0].name
for prefix, viewset, basename in self.registry:
api_root_dict[prefix] = list_name.format(basename=basename)
api_view_urls = self._api_view_urls
class APIRoot(views.APIView):
_ignore_model_permissions = True
def get(self, request, format=None):
ret = {}
for key, url_name in api_root_dict.items():
ret[key] = reverse.reverse(url_name, request=request, format=format)
# In addition to what had been added, now add the APIView urls
for api_view_key in api_view_urls.keys():
ret[api_view_key] = reverse.reverse(api_view_urls[api_view_key].name, request=request, format=format)
return response.Response(ret)
return APIRoot.as_view()
3b) In your main urls.py do this:
from .custom_routers import HybridRouter
# The rest is from the `rest-framework` polls-tutorial.
rest_router = HybridRouter()
rest_router.register(r'users', UserViewSet)
rest_router.register(r'polls', PollViewSet)
rest_router.add_api_view("config", url(r'^config/$', configViews.SiteConfiguration.as_view(), name='site_configuration'))
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^', include(rest_router.urls), name='rest_api'),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^admin/', include(admin.site.urls), name='admin'),
]
All which seems to work for me.
An alternate approach that doesn't require outside dependencies:
# models.py
from django.db import models
class MySingleton(models.Model):
def save(self, *args, **kwargs):
self.pk = 1
return super().save(*args, **kwargs)
#classmethod
def singleton(cls):
obj, _ = cls.objects.get_or_create(pk=1)
return obj
# serializers.py
from rest_framework import serializers
from . import models
class MySingletonSerializer(serializers.ModelSerializer):
class Meta:
model = models.MySingleton
fields = "__all__"
# views.py
from rest_framework import generics
from . import models
from . import serializers
class SingletonView(generics.RetrieveAPIView):
serializer_class = serializers.MySingletonSerializer
def get_object(self):
return models.MySingleton.singleton()