Improperly Configured URL for hyperlinked relationship - python

I am trying to use an alternative id besides the pk for a hyperlink, however I am getting an error:
Could not resolve URL for hyperlinked relationship using view name "foos-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.
During handling of the above exception (Reverse for 'foos-detail' with arguments '()' and keyword arguments '{'pk': 27}' not found. 2 pattern(s) tried: ['api/foos/(?P[^/.]+)\.(?P[a-z0-9]+)/?$', 'api/foos/(?P[^/.]+)/$']), another exception occurred:
Serializer:
class FooSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='foos-detail', format='html')
class Meta:
model = Foo
fields = ('url', 'alt_id', 'created', 'modified', 'name')
ViewSet:
class FooViewSet(viewsets.ModelViewSet):
queryset = Foo.objects.all()
serializer_class = FooSerializer
lookup_field = 'alt_id'
Urls:
router = DefaultRouter()
router.register(r'foos', FooViewSet, 'foos')
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]
Edit: This is definitely a result of trying to use a lookup_field. Deleting the lookup_field in the viewset results in hyperlinks being displayed correctly for the pk.

You are missing the namespace in the view_name:
url = serializers.HyperlinkedIdentityField(view_name='rest_framework :foos-detail', format='html')

It cost me hours, and I finally made it work. After you make the identification right on class Meta, try these:
name your 'field-list' or 'field-detail' following common habit
add namespace before them for restframework to work well, like
#api_view(['GET'])
def api_root(request, format=None):
return Response({
'users':reverse('snippets:user-list', request=request, format=format),
'snippets':reverse('snippets:snippet-list',request=request, format=format)
})
make var url clear when you use serializers.HyperlinkedModelSerializer
url = serializers.HyperlinkedIdentityField(view_name="snippets:user-detail")

Related

HyperlinkedRelatedField DRF does not work with a ViewSet

I have these models:
class ExamSheet (models.Model):
pass
class Exam(models.Model):
exam_sheet = models.ForeignKey('myapp.ExamSheet',
related_name='exams',
)
Serializer:
class ExamBaseSerializer(serializers.ModelSerializer):
exam_sheet = serializers.HyperlinkedRelatedField(queryset=ExamSheet.objects.all(), view_name='examsheet-detail')
class Meta:
model = Exam
fields = ('id', 'user', 'exam_sheet', )
read_only_fields = ('id', 'user',)
ViewSets:
class ExamViewSet(MultiSerializerViewSet):
queryset = Exam.objects.all()
class ExamSheetViewSet(MultiSerializerViewSet):
queryset = ExamSheet.objects.all()
Routes:
app_name = 'exams_api'
router = DefaultRouter()
router.register(r'exams', views.ExamViewSet)
router.register(r'exams_sheets', views.ExamSheetViewSet)
urlpatterns = []
urlpatterns += router.urls
Global app urls:
urlpatterns = [
path('api/', include('exams_api.urls')),
]
GenericViewSet:
class MultiSerializerViewSet(viewsets.ModelViewSet):
serializers = {
'default': None,
}
def get_serializer_class(self):
return self.serializers.get(self.action, self.serializers['default'])
But this throws me an error:
ImproperlyConfigured at /api/exams/
Could not resolve URL for hyperlinked relationship using view name
"examsheet-detail". You may have failed to include the related model
in your API, or incorrectly configured the lookup_field attribute on
this field.
How can I use HyperlinkedRelatedField to show a link to a related model in my serializer?
The view_name you set for the hyperlink field view_name='examsheet-detail, do you have a view named examsheet-detail in your urlconf(mostly urls.py) for that app? If not, after include(views.someview), add ,name='examsheet-detail' as a third parameter to path.
Have you set app_name in the specific app's urlsconf? Removing it solved the issue for me.
Make sure <int:pk> in the url path for that view and not <int:id> or something else. Else, set lookup_field='id' cos the default is pk.
Make sure that view takes in the pk parameter.

Django Rest Framework different format response format for same URL and HTTP method

I working on an application with uses Django Rest Framework to handle queries and I use django-rest-framework-datatables plugin to help me handle pagination to datatables.
Works fine, but when I request for a single register it keeps bringing me a json format for datatables, like this:
{
"count": 1,
"next": null,
"previous": null,
"results": [{
"id": 1,
"name": "University of Passo Fundo",
"country": "Brazil"
}]
}
This is not a big issue, but I would prefer to receive just the result field. How can I defined two different response format for a same URL and same method, just checking request parameters in django rest framework?
Follow my code:
urls.py
router = routers.DefaultRouter()
router.register(r'institution', InstitutionViewSet, base_name='Institution')
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
# api
path('api/', include(router.urls)),
# views
url(r'^$', Home.as_view(), name='index'),
url(r'institution/', Institution.as_view(), name='institution'),
]
serializer.py
class InstitutionSerializer(serializers.ModelSerializer):
class Meta:
model = Institution
fields = '__all__'
datatables_always_serialize = ('id', 'name', 'country')
models.py
class Institution(models.Model):
id = models.AutoField(db_column='id', primary_key=True)
name = models.CharField(db_column='name', max_length=255, null=False)
country = models.CharField(db_column='country', max_length=255, null=False)
class Meta:
db_table = 'Institution'
managed = True
verbose_name = 'Institutions'
verbose_name_plural = 'Institutions'
ordering = ['id']
def __str__(self):
return self.name
views.py
class InstitutionViewSet(viewsets.ModelViewSet):
serializer_class = InstitutionSerializer
def get_queryset(self):
if 'type' in self.request.GET and self.request.GET['type'] == 'edit':
return Institution.objects.filter(id=self.request.GET['id'])
return Institution.objects.all().order_by('id')
First of all, that's the way Django render response for pagination.
So you can see the next or previous list of items based on the page.
And second you should override the list view of Django to be like this:
class InstituttionViewSet(viewsets.ModelViewSet):
serializer_class = InstitutionSerializer
pagination_class = None
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
In here, we are overriding the list method which is responsible to render a list of items API. So it will first get all items in queryset, then pass it to serializer to write it to a specific format, and at last return that lists in json for a response.
Also, remember I also set pagination_class=None So Django will not use pagination for APIs anymore.
I think You shouldn't override get_queryset view's method, this should solve your problem. The drf views handle default param by default via retrieve method. You can use your own pagination class to edit response schema.
You can override the method, get_serializer_class and use two different serializer depend of request parameter.

Django: DetailView and multiple slugs

I have an issue with my DetailView. I want to make sure both values are in the url string and then want to display the page. However I am always receiving this error here:
KeyError at /orders/ticket/ug2dc78agz-1/d04fkjmo37/
'order_reference'
views.py
class TicketView(DetailView):
model = Attendee
template_name = 'orders/ticket_view.html'
def get_queryset(self):
return Attendee.objects.filter(
order__order_reference=self.kwargs['order_reference'],
).filter(
access_key=self.kwargs['access_key'],
)
urls.py
urlpatterns = [
path(
'ticket/<slug:ticket_reference>/<slug:access_key>/',
TicketView.as_view(),
name='ticket'
),
]
You get the error because you are trying to access self.kwargs['order_reference'], but you don't use order_reference in the path().
Your path() uses,
'ticket/<slug:ticket_reference>/<slug:access_key>/'
therefore you can use self.kwargs['ticket_reference'] and self.kwargs['access_key'].
Since your path does not contain slug or pk, Django will not know how to fetch the object for the detail view. I would override get_object instead of get_queryset:
def get_object(self):
return get_object_or_404(
Attendee,
order__order_reference=self.kwargs['slug:ticket_reference'],
access_key=self.kwargs['access_key'],
)
You have ticket_reference url variable, but in view using order_reference. You should rename it:
class TicketView(DetailView):
model = Attendee
template_name = 'orders/ticket_view.html'
def get_queryset(self):
return Attendee.objects.filter(
order__order_reference=self.kwargs['ticket_reference'],
).filter(
access_key=self.kwargs['access_key'],
)

django.core.exceptions.ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "user-detail"

TL;DR: I am getting this error and don't know why:
django.core.exceptions.ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "user-detail". You may have failed to include the related model in your API, or incorrectly configured the 'lookup_field' attribute on this field.
I am going through the django-rest-framework tutorial and am currently at a point where function based views (FBV) were switched to class, mixin, and generic based views (CBV, MBV, GBV respectively). After switching to GBV, when I went to test my API, I received this error AssertionError: Expected view SnippetDetail to be called with a URL keyword argument named "pk". Fix your URL conf, or set the '.lookup_field' attribute on the view correctly.. I did some research and found that lookup_field needs to be set to the in the urlpatterns. Currently, my urls.py look like this:
from django.conf.urls import url, include
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
# API endpoints
urlpatterns = format_suffix_patterns([
url(r'^$', views.api_root),
url(r'^snippets/$',
views.SnippetList.as_view(),
name='snippet-list'),
url(r'^snippets/(?P<id>[0-9]+)/$',
views.SnippetDetail.as_view(),
name='snippet-detail'),
url(r'^users/$',
views.UserList.as_view(),
name='user-list'),
url(r'^users/(?P<id>[0-9]+)/$',
views.UserDetail.as_view(),
name='user-detail')
])
# Login and logout views for the browsable API
urlpatterns += [
url(r'^auth/', include('rest_framework.urls',
namespace='rest_framework')),
]
and my views.py look like so:
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer, UserSerializer
from snippets.permissions import IsOwnerOrReadOnly
from rest_framework import generics
from rest_framework import permissions
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
from django.contrib.auth.models import User
#api_view(['GET'])
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'snippets': reverse('snippet-list', request=request, format=format)
})
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly, )
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly, )
when I add lookup_field = 'id' in both UserDetail and SnippetDetail, the exception resolves itself. (Yay!). But, when I visit http://127.0.0.1/users/1/ ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "user-detail". You may have failed to include the related model in your API, or incorrectly configured the 'lookup_field' attribute on this field. is thrown. When I check the console, however, there is a second exception:
django.urls.exceptions.NoReverseMatch: Reverse for 'user-detail' with
arguments '()' and keyword arguments '{'pk': 1}' not found. 2
pattern(s) tried: ['users/(?P[0-9]+)\.(?P[a-z0-9]+)/?$',
'users/(?P[0-9]+)/$']
During handling of the above exception, another exception occurred:
django.core.exceptions.ImproperlyConfigured: Could not resolve URL for
hyperlinked relationship using view name "user-detail". You may have
failed to include the related model in your API, or incorrectly
configured the 'lookup_field' attribute on this field.
What I find interesting is that the kwargs for the first exception is {'pk': 1}, not {'id':1}. After some help from chat, someone pointed me to this piece of information:
Note that when using hyperlinked APIs you'll need to ensure that both the API views and the serializer classes set the lookup fields if you need to use a custom value.
This is useful as User serializer extends HyperlinkedModelSerializer:
from rest_framework import serializers
from django.contrib.auth.models import User
from snippets.models import Snippet
class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)
class Meta:
model = User
fields = ('url', 'id', 'username', 'snippets')
the User model and serializer has a reverse relationship with Snippet. Now, when I add lookup_field='id' to snippets attribute of UserSerializer (snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True, lookup_field='id')), like it asks me to do so here, the error is persistent.
What am I doing incorrectly? What can I do to fix this? Is it not having anything to do with lookup_id?
I understand that I could replace <id> with <pk> in my urlpatterns, but I would like to understand why this is happening.
For anyone else coming across this question after following the the tutorial, here is the answer.
Change these lines
router.register(r'snippets', views.SnippetViewSet,basename="snippets")
router.register(r'users', views.UserViewSet,basename="users")
To these (note the singular basenames)
router.register(r'snippets', views.SnippetViewSet,basename="snippet")
router.register(r'users', views.UserViewSet,basename="user")
If you follow it exactly as the tutorial exactly as written, it generates these router URLs (notice snippets-details). This is what causes the error
<URLPattern '^snippets\.(?P<format>[a-z0-9]+)/?$' [name='snippets-list']>
<URLPattern '^snippets/(?P<pk>[^/.]+)/$' [name='snippets-detail']>
<URLPattern '^snippets/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='snippets-detail']>
<URLPattern '^snippets/(?P<pk>[^/.]+)/highlight/$' [name='snippets-highlight']>
<URLPattern '^snippets/(?P<pk>[^/.]+)/highlight\.(?P<format>[a-z0-9]+)/?$' [name='snippets-highlight']>
I eventually fixed my second exception by printing the serializer in the Django shell/python interactive console. The result I got was this:
>>> from snippets.serializers import UserSerializer
>>> print(UserSerializer())
UserSerializer():
url = HyperlinkedIdentityField(view_name='user-detail')
id = IntegerField(label='ID', read_only=True)
username = CharField(help_text='Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.', max_length=150, validators=[<django.contrib.auth.validators.UnicodeUsernameValidator object>, <UniqueValidator(queryset=User.objects.all())>])
snippets = HyperlinkedRelatedField(lookup_field='id', many=True, read_only=True, view_name='snippet-detail')
It turns out that to change <pk> to <id> in urlspatterns, you need to add url = HyperlinkedIdentityField(view_name='user-detail', lookup_field='id') in the UserSerializer class.
I just had the same problem and I found, that the HyperlinkedIdentityField wants to insert some placeholder into your URL. But I had used URLs which did not require any placeholders to be set. A ListCreateAPIView to be precise:
url(
r'^users/$', #<-- takes no params
views.UserListView.as_view(), #<-- just prints a list
name="user-list" #<-- HyperlinkedIdentityField pointing
), # here complained bitterly.

Django Rest Framework: Find URL for hyperlinked model

I'm using using Django Rest Framework's HyperlinkedModelSerializer, ModelViewset, and DefaultRouter, and end up with good URLs like this: http://localhost:8000/api/users/1.
I would like to, given a user id, find the fully-qualified url for that user without hard-coding anything. Given 53, I want http://localhost:8000/api/users/1, and the host should change when I move to production.
# from urls.py
router = routers.DefaultRouter()
router.register(r'users', shared_views.UserViewSet)
# from models.py
class UserViewSet(viewsets.ModelViewSet):
'''
endpoint for viewing/editing users
'''
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
# from serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
How can I do this? I tried: reverse(UserViewSet.as_view({'get': 'retrieve'}), args=[request.user.id]) but got this error:
Reverse for 'shared.views.UserViewSet' with arguments '(1,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
As #Anush said, I can use the serializer to get the URL. The request needs to be passed in a particular way as a keyword argument (see below):
class OwnedViewSet(viewsets.ModelViewSet):
''' ModelViewSets that use hyperlinked model serializers
can inherit this to automatically
set `owner` = current user.
'''
def create(self, request, *args, **kwargs):
serialized_owner = UserSerializer(request.user, context={'request': request})
request.data['owner'] = serialized_owner.data['url']
return super(OwnedViewSet, self).create(request, *args, **kwargs)

Categories

Resources