How to make a reverse in DefaultRouter() - django

I'm setting up a new tests, and i want to make an reverse.
router = DefaultRouter()
router.register('profile', views.UserProfileViewSet, base_name='profile')
urlpatterns = [
url(r'', include(router.urls))
]
UserProfileViewSet
class UserProfileViewSet(viewsets.ModelViewSet):
"""Handles creating, creating and updating profiles."""
serializer_class = serializers.UserProfileSerializer
permission_classes = (permissions.UpdateOwnProfile,)
authentication_classes = (TokenAuthentication,)
queryset = get_user_model().objects.all()
So , i want make a reverse in tests.py. my shot is:
CREAT_USER_URL = reverse('profile-create')
And I simply get:
Reverse for 'profile-create' not found. 'profile-create' is not a valid view function or pattern name.
How should I set up a reverse in this case.

You should use profile-list instead of profile-create
CREAT_USER_URL = reverse('profile-list')
There is no URL as {base_name}-create, If you wanna use create endpoint, use {base_name}-list.
For more information, refer to this table

Related

Django Rest Framework routing with primary key prefix

I'm using DRF DefaultRouter as follows.
# urls.py
router = DefaultRouter()
router.register('book', BookingViewSet, basename='booking')
router.register('book/<int:bk>/reservation', ReservationViewSet, basename='reservation')
urlpatterns = [
path('', include(router.urls)),
]
# view
class ReservationViewSet(viewsets.ModelViewSet):
serializer_class = ReservationSerializer
queryset = Reservation.objects.all() # for testing only
But when I visit the URL /book/1/reservation/ it says no url pattern found.
lending/ ^book/<int:bk>/reservation/$ [name='reservation-list']
lending/ ^book/<int:bk>/reservation\.(?P<format>[a-z0-9]+)/?$ [name='reservation-list']
lending/ ^book/<int:bk>/reservation/(?P<pk>[^/.]+)/$ [name='reservation-detail']
lending/ ^book/<int:bk>/reservation/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='reservation-detail']
The current path, lending/book/1/reservation/, didn’t match any of these.
I'm using bk to capture book id.
That's because it implements the <int:bk> as regex, so without any interpretation. Probably the simplest way to do this is with two routers:
router1 = DefaultRouter()
router1.register('book', BookingViewSet, basename='booking')
router2 = DefaultRouter()
router2.register('reservation', ReservationViewSet, basename='reservation')
urlpatterns = [
path('', include(router1.urls)),
path('book/<int:bk>/', include(router2.urls)),
]
In the ReservationViewSet you can then for example filter with bk:
class ReservationViewSet(viewsets.ModelViewSet):
serializer_class = ReservationSerializer
def get_queryset(self):
return Reservation.objects.filter(book_id=self.kwargs['bk'])
Just wanted to add this to the accepted answer ... You can also do this with the same router, just embed the pk in the URL in regex form as the error message suggests:
# urls.py
router = DefaultRouter()
router.register('book', BookingViewSet, basename='booking')
router.register('book/(?P<bk>[^/.]+)/reservation', ReservationViewSet, basename='reservation')
urlpatterns = [
path('', include(router.urls)),
]
You can then access the bk argument in your view using self.kwargs['bk'].

Django optional URL captured value for rest APIView

For following Django code using django_rest_framework:
class PollMessageView(generics.ListAPIView):
serializer_class = MessageSerializer
lookup_url_kwarg = 'count'
def get_queryset(self):
count = self.kwargs.get(self.lookup_url_kwarg)
queryset = Message.objects.all()[:count]
return queryset
urlpatterns = [
path('poll_message/<int:count>', PollMessageView.as_view(), name="poll_message"),
]
How do I make the count parameter optional in the URL pattern? For example, if I visit /poll_message/ without a count, it would still call the PollMessageView (and I can set a default number for count if it's missing)?
create a new path as ,
urlpatterns = [
path('poll_message/', PollMessageView.as_view(), name="poll_message-wo-count"),
path('poll_message/<int:count>', PollMessageView.as_view(), name="poll_message"),
]
# URLconf
from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.page),
path('blog/page<int:num>/', views.page),
]
# View (in blog/views.py)
def page(request, num=1):
# Output the appropriate page of blog entries, according to num.
...
In detail reference - https://docs.djangoproject.com/en/3.0/topics/http/urls/#specifying-defaults-for-view-arguments
You can specify defaults for view arguments, but the example on the website is for functional view, not sure how would that work for class based view or in this case, the rest framework class based view.
However, you can add another path with extra options to achieve the same thing
path('poll_message/', PollMessageView.as_view(), {
"count": 5
}, name="poll_message"),

Register your Django router

When I use route.register without base_name like;route.register(r'codes', SmsCodeViewset)
An error occurred;
AssertionError: basename argument not specified, and could not automatically determine the name from the viewset, as it does not have a .queryset attribute.
When I use route.register(r'codes', SmsCodeViewset, bose_name="") there is no error, may I ask why?
I was checking my endpoints and I don't have base name in any of them.
from app import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'states', views.StateSet)
urlpatterns = [
url(r'^', include(router.urls)),
]
class StateSet(viewsets.ModelViewSet):
queryset = State.objects.all()
serializer_class = StateSerializer
we should provide base name to the route when we add it like below
router.register(
r'codes',
SmsCodeViewset,
base_name='sms-code',
)

Django REST framework RetrieveAPIView gets empty "id" parameter and returns 404 error

I use Django 1.11 + Django REST Framework. I'm trying to get detailed data about distinct record using generic RetrieveAPIView from Django REST Framework and server returns "HTTP 404 Not Found" result, i.e. no data.
models.py file:
class TestPage( models.Model):
title = models.CharField( size = 120)
counter = models.IntegerField( default = 0)
def __unicode__(self):
return u'' + self.title
serializers.py file:
class TestPageSerializer(serializers.ModelSerializer):
class Meta:
fields = ('id', 'title', 'counter',)
model = models.TestPage
urls.py file:
urlpatterns = [
url( r'^admin/', admin.site.urls),
url( r'^api/', include( 'rest_framework.urls')),
url( r'^api/', include( 'testpages.urls')),
]
testpages/urls.py file:
urlpatterns = [
url( r'^$', views.TestPageList.as_view()),
url( r'^(?P<id>)\d+/$', views.TestPageDetail.as_view()),
]
and at last views.py file:
class TestPageList(generics.ListAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
class TestPageDetail(generics.RetrieveAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
# def get_object( self):
# print self.kwargs
If I request "http://127.0.0.1:8000/api/" for all records in the list, server returns all records. But if I want to get record by id using "http://127.0.0.1:8000/api/1/" for example, I get the empty result like in the photo below.
[![enter image description here][1]][1]
If I uncomment get_object()-function in last 2 lines, I can see {'id': u''} in terminal i.e. server gets empty parameter 'id'.
What wrong with this code?
The issue is your regular expression matching for id, you're putting \d+ outside of the matching group when it's actually what you want to match:
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view())
By the way, to be good REST citizen, you shouldn't add the / at the end of a URL for a specific object.
Note: As mentioned by JPG, adding a name for the resource you're fetching (plural) would be proper RESTful:
url(r'^pages/(?P<id>\d+)$', views.TestPageDetail.as_view())
This way, you fetch page nr. 1 at /api/pages/1
The original problem was the closing parathesis (the regex expression), you were misplaced that.
urlpatterns = [
url(r'^$', views.TestPageList.as_view()),
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view()),
^^^ here
]
Apart from that, You should change the urls patterns to a sensible form, as
#testpages/urls.py
urlpatterns = [
url(r'^test/$', views.TestPageList.as_view()),
url(r'^test/(?P<id>\d+)/$', views.TestPageDetail.as_view()),
]
then the list-api will be available on /api/test/ endpoint and the detail-api will be available on /api/test/{id}/

tastypie return empty resource_uri in get

This is the resource
class TagResource(ModelResource):
user = tastypie.fields.ForeignKey(UserResource,'user')
class Meta:
queryset = Tag.objects.all()
resource_name = 'tag'
authorization= Authorization()
object_class = Tag
filtering = {
'name' : ALL,
}
simple get request
http://localhost:8000/api/v1/tag/1/?format=json
returns with empty resource_uri
{"created": "2014-03-26T15:14:11.928068",
"id": 1, "name": "test",
"resource_uri": "", "user": ""}
Why is that ?
I tried
def hydrate_resource_uri(self, bundle):
return bundle.get_resource_uri()
It didn't work and i'm pretty sure it's not supposed to require special care.
What am i missing ?
I know this is old, but I know your problem, I just had it on mine, you have a "namespace" on the API URL include or on any URL includes further up in your URL tree.
I had the same problem, and it was because I forgot to register my resource in the urls.py.
Ensure you have something like this in your urls.py file:
myapi.register(TagResource())
I have created a blog for highlighting the same problem. link to my blog
Tastypie give couple of options to create a Model Resource.
ModelResource
NamespacedModelResource
When namespace is included in urls.py then Tastypie logic of generating resource_uri fails as it also expects the same namespace in the api urls. To overcome this problem either one has to remove the namespace from module level urls.py or implement namespace with Tastypie. First solution looks easy but it may break your application. The below code will help you use second approach.
Api.py
from tastypie.resources import NamespacedModelResource
class EntityResource(NamespacedModelResource):
class Meta:
queryset = Entity.objects.all()
allowed_methods = ['get']
resource_name = 'entity'
authentication = SessionAuthentication()
authorization = Authorization()
mymodule/url.py
from tastypie.api import NamespacedApi
from mymodule.api import EntityResource
v1_api = NamespacedApi(api_name='v1', urlconf_namespace='mymodule')
v1_api.register(EntityResource())
urlpatterns = patterns('',
url(r'^api/', include(v1_api.urls)),
)
Make sure you use same namespace for module and its api urls. The above code will surely generate proper resource_uri.
Try to remove the object_class, I think that if this is ModelResource, you don't need this.
This may be because, for some reason, the api_name is missing.
Try to add it in the resource meta.
For instance, if your resource uri is /api/v1/YourResourceName, try to add in your resource Meta:
api_name = 'v1'
Hope it helps.
Only if someone else get this problem caused by a namespace in urls.py. You have to use NamespacedModelResource instead of ModelResource:
from tastypie.resources import NamespacedModelResource
class TagResource(NamespacedModelResource):
user = tastypie.fields.ForeignKey(UserResource,'user')
class Meta:
queryset = Tag.objects.all()
resource_name = 'tag'
authorization= Authorization()
object_class = Tag
filtering = {
'name' : ALL,
}
and then into your module's urls.py
from tastypie.api import NamespacedApi
v1_api = NamespacedApi(api_name='v1', urlconf_namespace='your_module')
v1_api.register(TagResource())
urlpatterns = patterns(
'',
url(r'^api/', include(v1_api.urls)),
)
Check this post.