Can't create HyperlinkedRelatedField in Django Rest Framework - django

I'm struggling to get HyperlinkedRelated fields working, and I can't figure out where I'm going wrong. No matter what I do, I get an error like:
Could not resolve URL for hyperlinked relationship using view name "court-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.
I feel that I've tried everything and I don't know where my error could be, nor how to identify it. My reading of this error message is that I am looking for a URL that corresponds to the court-detail view, but that that view doesn't exist.
Some piece of magic isn't working and any help would be greatly appreciated.
URLs:
from cl.api import views
from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'courts', views.CourtViewSet)
router.register(r'dockets', views.DocketViewSet)
router.register(r'clusters', views.OpinionClusterViewSet)
router.register(r'opinions', views.OpinionViewSet)
router.register(r'cited-by', views.OpinionsCitedViewSet)
urlpatterns = [
# url(r'^api/rest/(?P<version>[v3]+)/', include(router.urls)),
url(r'^api-auth/',
include('rest_framework.urls', namespace='rest_framework')),
url(r'^', include(router.urls)),
]
Views:
class DocketViewSet(viewsets.ModelViewSet):
queryset = Docket.objects.all()
serializer_class = serializers.DocketSerializer
class CourtViewSet(viewsets.ModelViewSet):
queryset = Court.objects.all()
serializer_class = serializers.CourtSerializer
class OpinionClusterViewSet(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.
"""
queryset = OpinionCluster.objects.all()
serializer_class = serializers.OpinionClusterSerializer
class OpinionViewSet(viewsets.ModelViewSet):
queryset = Opinion.objects.all()
serializer_class = serializers.OpinionSerializer
class OpinionsCitedViewSet(viewsets.ModelViewSet):
queryset = OpinionsCited.objects.all()
serializer_class = serializers.OpinionsCitedSerializer
Serializers:
from cl.audio import models as audio_models
from cl.search import models as search_models
from rest_framework import serializers
class DocketSerializer(serializers.HyperlinkedModelSerializer):
court = serializers.HyperlinkedRelatedField(
many=False,
view_name='court-detail',
read_only=True
)
class Meta:
model = search_models.Docket
fields = ('date_created', 'date_modified', 'date_argued',
'date_reargued', 'date_reargument_denied', 'court')
class CourtSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = search_models.Court
class AudioSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = audio_models.Audio
class OpinionClusterSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = search_models.OpinionCluster
fields = ('judges', 'per_curiam', )
class OpinionSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = search_models.Opinion
fields = ('pk',)
class OpinionsCitedSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = search_models.OpinionsCited
When I go to:
http://127.0.0.1:8000/dockets/
It tells gives me the error message above. Of course, if I remove the court reference from the serialization, it works fine...

I imagine this can be caused by a number of things, but in my case, I figured out that it was caused by having DEFAULT_VERSIONING_CLASS set without having it configured in the urls.py:
REST_FRAMEWORK = {
# Use URL-based versioning
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION': 'v3',
'ALLOWED_VERSIONS': {'v3'},
}
The solution, therefore, was either to disable it in the settings, or to set up a url in in urls.py that accepted the version parameter:
url(r'^api/rest/(?P<version>[v3]+)/', include(router.urls)),
Ugh. Took a long time to realize I had this setting in place. Bad error message.

Related

Expected view to be called with a URL keyword argument named "user_token"

I am using Django Rest Framework and here is my view:
class DeleteUserView(generics.DestroyAPIView):
permission_classes = (IsAuthenticated,)
serializer_class = UserSerializer
queryset = User.objects.all()
lookup_field = 'user_token'
and my urls.py:
from django.urls import path
from .views import CreateUserView, DeleteUserView
urlpatterns = [
path('add_user/', CreateUserView.as_view()),
path('delete_user/', DeleteUserView.as_view()),
]
serializer.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('user_token',)
I am trying to delete user by specific token but it doesn't work...I am using Postman and provide user_token in Body
If you set lookup_field paremeter, it basically looks for that variable in your URLconfig. Eg.
path('delete_user/(?P<user_token>[-\w]+)/', DeleteUserView.as_view()),
If you specify the URL like above, and then call http://127.0.0.1:8000/delete_user/1/, it should work
Note
In Your case, if you are going to have CRUD views like add_user, delete_user, update_user, then I suggest you use a model viewset

Django rest framework- Only see's one view

I'm creating 4 different views for my API. However, Django Rest Framework only see's one API/URL.
The last project in my views is always the one that appears with DRF. EG if I remove "ProjectViewSet" from my views, "Location" will appear as the URL at DRF.
This screenshot provides info:
My views
class DataViewSet(viewsets.ModelViewSet):
queryset = Task.objects.exlude(Q(tag=1)|Q(name=1))
serializer_class = TaskSerializer
class EventViewSet(viewsets.ModelViewSet):
queryset = Task.objects.exlude(Q(tag=2)|Q(name=2))
serializer_class = TaskSerializer
class LocationViewSet(viewsets.ModelViewSet):
queryset = Task.objects.exlude(Q(tag=3)|Q(name=3))
serializer_class = TaskSerializer
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Task.objects.exlude(Q(tag=4)|Q(name=4))
serializer_class = TaskSerializer
My URLS (again):
router = routers.DefaultRouter()
router.register(r'Tag', TagViewSet)
router.register(r'Info', InfoViewSet)
router.register(r'Data', DataViewSet)
router.register(r'Friends', FriendsViewSet)
urlpatterns = router.urls
urlpatterns += [
url(r'^1.1/tag/', rest_views.TagView.as_view()),
url(r'^1.1/task/', rest_views.TaskView.as_view()),
]
Found the solution. As my serializers share the same data model, DRF seems to be getting stuck trying to automatically discover the url naming pattern.
Giving a base_name argument to each of my models solved the issue!
router.register(r'Data', DataViewSet, base_name='Data')
router.register(r'Friends', FriendsViewSet, base_name='Friends')

django rest API route showing same api link for all end point

I have created a small Task app & define some end point like all task, Due Task, Completed Task.
Here is my url.py
#Define API Routes
router = routers.DefaultRouter()
#router = routers.SimpleRouter()
router.register(r'task', views.TaskViewSet)
router.register(r'due_task', views.DueTaskViewSet)
router.register(r'completed_task', views.CompletedTaskViewSet)
urlpatterns = [
# Examples:
# url(r'^$', 'TaskAPI.views.home', name='home'),
# url(r'^blog/', include('blog.urls')),
url(r'^',include(router.urls)),
url(r'^admin/', include(admin.site.urls)),
]
Serializers.py
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ('id','task_name','task_desc','completed','date_created')
View.py
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all().order_by('-date_created')
serializer_class = TaskSerializer
class DueTaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all().order_by('-date_created').filter(completed=False)
serializer_class = TaskSerializer
class CompletedTaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all().order_by('-date_created').filter(completed=True)
serializer_class = TaskSerializer
model.py
class Task(models.Model):
task_name = models.CharField(max_length=20)
task_desc = models.TextField(max_length=200)
completed = models.BooleanField(default=False)
date_created = models.DateTimeField(auto_now=True)
when I run server & hit 127.0.0.1:8080 API route windows will open with all endpoint pointing to the same link.
please suggest what is going wrong here.
Thanks
You probably need to specify the optional base_name argument in the register() method. It's normally auto-generated. However, the same serializer and relatively similar querysets might be why you're getting the same endpoint. I think you want something like this.
router.register(r'task', views.TaskViewSet, 'task')
router.register(r'due_task', views.DueTaskViewSet, 'due_task')
router.register(r'completed_task', views.CompletedTaskViewSet, 'completed_task')
According to the docs:
If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a queryset attribute then you must set base_name when registering the viewset.
You are indeed confusing DRF. Perhaps it should be able to handle your case, but it cannot.
I would collapse all three viewsets into one viewset with optional filters:
from rest_framework import filters
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
filter_fields = ('completed',)
ordering = ('-date_created',)
filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter,)
Then, query conditionally with the completed parameter:
/completed_task/
/completed_task/?completed=True
/completed_task/?completed=False

django rest framework - cannot get the class based view right

Here is my codes:
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ( 'username', 'email')
class AllListingSerializer(serializers.ModelSerializer):
class Meta:
model = Listing
fields = ('name', 'desc', 'thumbnail', 'office_no', 'mobile_no', 'email', 'web ')
views.py
class UserViewSet(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class AllListing(generics.ListCreateAPIView):
queryset = Listing.objects.all()
serializer_class = AllListingSerializer
urls.py
urlpatterns = patterns('',
url(r'^$', apiview.UserViewSet),
url(r'^listings/$', apiview.AllListing),
)
But when i goto the base url it shows
init() takes 1 positional argument but 2 were given
and when i goto '/listings/' url, it give me 404 Page Not Found, but I have few listings in the db.
I am pretty new in django. I can't figure out what wrong with them. I am using Django 1.7.1 in virtualwrappr, python 3.4.
You should call .as_view() for each API view:
urlpatterns = patterns('',
url(r'^$', apiview.UserViewSet.as_view()),
url(r'^listings/$', apiview.AllListing.as_view()),
)
Also, consider using REST framework's Routers which provide you with a simple, quick and consistent way of wiring your view logic to a set of URLs.
This happened to me when I extended generics.GenericAPIView in stead of viewsets.GenericViewSet in my custom ViewSet class.
Pretty obvious but easy to miss.

Django REST Framework: Two different ModelSerializers for the same model

I have one model for which I've defined two different HyperlinkedModelSerializers:
class Foo(models.Model):
...
class FooSerializer1(serializers.HyperlinkedModelSerializer):
...
class Meta:
model = Foo
fields = ('url', 'id', ...)
lookup_field= 'pk'
# A second view of the same model for another API use-case
class FooSerializer2(serializers.HyperlinkedModelSerializer):
...
class Meta:
model = Foo
fields = ('url', 'id', ...)
lookup_field= 'pk'
FooSerializer1 is being used by a couple GenericViews in one Django app (i.e. its own urls.py), and FooSerializer2 by a ModelViewSet in another Django app. I have the ModelViewSet registered in a DefaultRouter along with a few other viewsets:
urlpatterns = patterns('',
url(r'^$', 'myapp.views.api_root'),
url(r'^foo1/$', views.FooList1.as_view(), name='foo1-list'),
...
)
urlpatterns = format_suffix_patterns(urlpatterns)
...
class FooViewSet2(viewsets.ReadOnlyModelViewSet):
queryset = Foo.objects.all()
serializer_class = FooSerializer2
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def get_queryset(self):
...
router = routers.DefaultRouter()
...
router.register(r'foo2', views.FooViewSet2)
...
urlpatterns = router.urls
That router's auto-generated api root displays the endpoint for the GenericView of FooSerializer1 (foo1-list), instead of foo2/. If I manually GET foo2/, the results show Foo serialized according to FooSerializer2 (which is correct), however the URL for each result again displays the foo1 detail view.
I tried setting get_serializer in FooViewSet2, but that didn't work. How do I get the api-root and FooSerializer2 results to display the URLs corresponding to FooViewset2?
DRF fortunately allows the flexibility to support this scenario through setting a few parameters.
First, I set the basename parameter on the router entry:
router.register(r'foo2', views.Foo2ViewSet, 'foo2')
Next, I set the view_name in the HyperlinkedModelSerializers in order to not default to foo-detail as mentioned in the docs:
class FooSerializer1(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='foo1-detail',
)
...
urlpatterns = patterns('',
url(r'^$', 'myapp.views.api_root'),
url(r'^foo1/$', views.Foo1List.as_view(), name='foo1-list'),
url(r'^foo1/(?P<pk>[0-9]+)/$', views.FooDetail1.as_view(), name='foo1-detail'),
...
class FooSerializer2(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='foo2-detail',
)
...