I would like to get the details of my submodules on a route with multiple parameters.
My existing routes right now are:
http://localhost:8000/api/module/ - Gets all modules
http://localhost:8000/api/module/1/ - Gets all modules and connected submodules
I would like to do http://localhost:8000/api/module/1/submodules/1 which will get the details of the submodules. How should i do this using class based views?
Below is my existing code:
Views.py
class CourseModuleViewSet(viewsets.ModelViewSet):
queryset = CourseModule.objects.all()
serializer_class = CourseModuleSerializer
def retrieve(self, request, pk=None):
coursemodule = CourseModule.objects.get(id=pk)
submodule = SubModule.objects.filter(submodule_module_id=pk)
serializer = SubModuleSerializer(submodule, many=True)
response = {'message': 'Sucess!',
'result': serializer.data}
return Response(serializer.data, status=status.HTTP_200_OK)
Serializers.py
class CourseModuleSerializer(serializers.ModelSerializer):
class Meta:
model = CourseModule
fields = ['class_id', 'coursemodule_title', 'coursemodule_date_created',
'coursemodule_last_modified', 'coursemodule_created_by']
class SubModuleSerializer(serializers.ModelSerializer):
submodule_module_id = CourseModuleSerializer(many=False)
class Meta:
model = SubModule
fields = ['submodule_module_id','submodule_title', 'submodule_date_created', 'submodule_last_modified',
'submodule_created_by']
urls.py
router = routers.DefaultRouter()
router.register('module', CourseModuleViewSet)
urlpatterns = [
path('', include(router.urls)),
]
You could use drf-nested-routers.
You would need to include an extra NestedRouter in you urls.py like this:
router = routers.DefaultRouter()
router.register(r'module', CourseModuleViewSet)
sub_modules_router = routers.NestedSimpleRouter(router, r'module', lookup='module')
sub_modules_router.register(r'submodules', SubModuleViewSet, base_name='module-submodules')
urlpatterns = patterns('',
url(r'^', include(router.urls)),
url(r'^', include(sub_modules_router.urls)),
)
And then you can create a SubModuleViewSet like this:
class SubModuleViewSet(viewsets.ModelViewSet):
def get_queryset(self):
return SubModule(submodule_module_id =self.kwargs['module_pk'])
serializer_class = SubModuleSerializer
Related
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 want to get a model instance using URL as http://127.0.0.1:8000/db/User/email (i.e. using email as a query) and not by http://127.0.0.1:8000/db/User/1/. How to approach this.
Model:
class Employee(models.Model):
firstname = models.CharField(max_length=100)
email = models.CharField(max_length=100)
serializers.py
class EmployeeSerializers(serializers.ModelSerializer):
field = NestedSerializers()
class Meta:
model = Employee
fields = '__all__'
def create(self, validated_data):
#overwrite this method for writable nested serializers.
view.py:
class UserView(viewsets.ModelViewSet):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializers
urls.py:
router = routers.DefaultRouter()
router.register('User', views.UserView)
urlpatterns = [
path('', views.index, name='index'),
path('/', include(router.urls))
]
Is it possible to do using ModelViewSet?
I see you are using DRF viewset. If you only ever want to use the email and not the id then you can override the the retrieve function of the viewset like so:
from django.shortcuts import get_object_or_404
class UserView(viewsets.ModelViewSet):
queryset = Employee.objects.all()
serializer_class = EmployeeSerializers
def retrieve(self, request):
employee = get_object_or_404(
self.queryset,
email=self.kwargs['email']
)
serializer = self.serializer_class(employee)
return Response(serializer.data)
urls
router = routers.DefaultRouter()
router.register('^User/(?P<email>.+)/$', views.UserView)
urlpatterns = [
path('', views.index, name='index'),
path('/', include(router.urls))
]
retrieve is functionality already provided in the viewset class
I want to use slug as my pk by using lookup_field.
The error I have is "Could not resolve URL for hyperlinked relationship using view name "service-detail".
I figured that if I use generic view(ListAPIView or RetrieveAPIView), it'll work because in urls I can set up a simple route like path('services/slug/').
But I would like to know if there is a way of doing this with Viewsets. Which means how can I set up the urls (Default Router instead of Simple Router) to handle this?
serializers.py
class ServiceSerializer(serializers.HyperlinkedModelSerializer):
title = serializers.CharField(required=True)
slug = serializers.SerializerMethodField(read_only=True)
description = serializers.CharField(required=False)
price = serializers.IntegerField(required=True)
service_image = ServiceImageSerializer(many=True)
class Meta:
model = Service
fields = ('url', 'slug', 'title', 'description', 'price', 'service_image')
lookup_field = 'slug'
extra_kwargs = {
'url': {'lookup_field':'slug'}
}
def get_slug(self, instance):
return slugify(instance.vendor.username + "-" + instance.title)
views.py
class ServiceViewSet(viewsets.ModelViewSet):
queryset = Service.objects.all()
serializer_class = ServiceSerializer
lookup_field = 'slug'
urls.py
router = routers.DefaultRouter()
router.register('categories', CategoryViewSet)
router.register('services', ServiceViewSet)
router.register('images', ServiceImageViewSet)
urlpatterns = [
path('', include(router.urls)),
]
I decided to not use Viewsets anymore and using this instead:
views.py
class ServiceListAPIView(ListCreateAPIView):
queryset = Service.objects.all()
serializer_class = ServiceSerializer
class ServiceDetailAPIView(RetrieveUpdateDestroyAPIView):
queryset = Service.objects.all()
serializer_class = ServiceSerializer
lookup_field = 'slug'
urls.py
urlpatterns = [
path('', include(router.urls)),
path('services/', ServiceListAPIView.as_view(), name='service-list'),
path('services/<slug>/', ServiceDetailAPIView.as_view(), name='service-detail'),
]
I'm using
Django: 2.0
Djanfo REST Frameword: 3.8.2
drf-nested-routers: 0.90.2
My contacts/views.py
class ContactViewSet(viewsets.ModelViewSet):
serializer_class = ContactSerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
return Contact.objects.filter(user=self.request.user)
class ContactPhoneNumberViewSet(viewsets.ModelViewSet):
serializer_class = ContactPhoneNumberSerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
print(self.kwargs)
phone_numbers = ContactPhoneNumber.objects.all()
return phone_numbers
and app/urls.py
from rest_framework_nested import routers
from contacts.views import ContactViewSet, ContactPhoneNumberViewSet
router = routers.SimpleRouter()
router.register(r'contacts', ContactViewSet, 'contacts')
contact_router = routers.NestedSimpleRouter(router, r'contacts', lookup='contact')
contact_router.register(r'phone_number', ContactPhoneNumberViewSet, base_name='contact-phone-numbers')
api_urlpatterns = [
path('', include(router.urls)),
]
urlpatterns = [
path('api/', include(api_urlpatterns)),
url(r'^admin/', admin.site.urls),
]
using this setup, I'm able to access
/api/contacts/ # <= list all contacts
/api/contacts/<pk>/ # <= contact detail
But on trying to access
/api/contacts/<pk>/phone_number/ # <= list all phone numbers
It is giving Page Not Found error.
I also tried passing <phone_number_pk> but still Page not Found error is received.
api_urlpatterns = [
path('', include(router.urls)),
path('', include(contact_router.urls)),
]
you also need to register nested urls separately
I have a Playlist Model and a Track model.
class Playlist(models.Model):
created = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, related_name="playlists")
class Track(models.Model):
playlist = models.ForeignKey(Playlist, related_name="tracks")
track_id = models.CharField(max_length=50)
And the serializers:
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ("id", "track_id")
class PlaylistSerializer(serializers.ModelSerializer):
user = serializers.Field(source="user.username")
tracks = TrackSerializer(many=True)
class Meta:
model = Playlist
fields = ("id", "created", "user", "tracks")
How would I go about creating views (using viewsets preferably) that allow me list a playlist's tracks at playlists/<playlist_id> and also create tracks at the same url?
I currently get non_field_errors when I go to the above url.
Couldn't find much on how to do these nested views on the docs. Thanks.
You can ues drf-nested-routers (https://github.com/alanjds/drf-nested-routers).
The only interesting part in the code below is setting the track playlist in the pre_save method of the TrackViewSet.
views.py
class TrackViewSet(viewsets.ModelViewSet):
queryset = Track.objects.all()
serializer_class = TrackSerializer
def pre_save(self, obj):
obj.playlist = Playlist.objects.get(pk=self.kwargs['playlist_pk'])
class PlaylistViewSet(viewsets.ModelViewSet):
queryset = Playlist.objects.all()
serializer_class = PlaylistSerializer
def pre_save(self, obj):
obj.user = self.request.user
urls.py
from django.conf.urls import patterns, url, include
from rest_framework_nested import routers
from . import views
router = routers.SimpleRouter()
router.register(r'playlists', views.PlaylistViewSet)
playlists_router = routers.NestedSimpleRouter(router, r'playlists', lookup='playlist')
playlists_router.register(r'tracks', views.TrackViewSet)
urlpatterns = patterns('',
url(r'^', include(router.urls)),
url(r'^', include(playlists_router.urls)),
)