<slug> path converter not working, but literal <slug> in URL works
Question:
When I try to visit this URL:
I get 404 page. Here’s the message that I get:
Using the URLconf defined in scrap.urls, Django tried these URL patterns, in this order:
admin/
api-auth/
getpage/
^<slug>/$ [name='ArticleInfoViewSet-list']
^<slug>/(?P<slug>[^/.]+)/$ [name='ArticleInfoViewSet-detail']
__debug__/
The current path, slugtest/, didn’t match any of these.
However, if I use
http://127.0.0.1:8000/<slug>/
the page loads perfectly, but that’s not intended behavior.
models.py
class Article(models.Model):
url = models.CharField(max_length=255,validators=[RegexValidator(regex=website_regex)])
slug = models.CharField(max_length=255, null=True)
unique_id = models.CharField(max_length=255, unique=True, null=True)
views.py
class ArticleInfoViewSet(ModelViewSet):
serializer_class = ArticleInfoSerializer
lookup_url_kwarg = 'slug'
lookup_field = 'slug'
def get_queryset(self):
queryset = Article.objects.prefetch_related('data')
return queryset
serializer.py
class ArticleInfoSerializer(serializers.ModelSerializer):
data = ArticleDataSerializer(many=True)
class Meta:
model = Article
fields = ['url', 'data', 'slug']
lookup_field = ['slug']
read_only_fields = ['url', 'data', 'slug']
urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('getpage/', include('getpage.urls')),
path('', include('getpage.urls2')),
path('__debug__/', include('debug_toolbar.urls')),
]
urls2.py
from . import views
from rest_framework_nested import routers
router = routers.SimpleRouter()
router.register('<slug>', views.ArticleInfoViewSet, basename='ArticleInfoViewSet')
urlpatterns = router.urls
What am I doing wrong here?
Answers:
router.register('', views.ArticleInfoViewSet, basename='ArticleInfoViewSet')
Easy mistake.
You have set everything up perfectly from what I can see, but you registered the route as literally /<slug>
hehe 🙂 whereas I’m guessing you wanted a little API at the root of the app which accepts a slug url parameter? To prove this, try accessing /<slug>/slugtest
Django rest framework will do lots of magical things when you register a viewset. Including considering the lookupfield kwarg and url kwarg attributes you have set. DRF will also automagically generate relevant url patterns based on your viewset configuration, hence why you need not specify and url parameters when using viewsets, there are however, ways to configure it and customise the paths.
I would not recommend putting an API with a slug parameter at the root of your app, it might be more appropriate to prefix the path somewhere with "api/" so it would become api/slugtest
and even better still, for the article viewset, api/articles/slugtest
So you would change the url:
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('getpage/', include('getpage.urls')),
path('api', include('getpage.urls2')),
path('__debug__/', include('debug_toolbar.urls')),
]
And finally register it as articles
router.register('articles', views.ArticleInfoViewSet, basename='ArticleInfoViewSet')
When I try to visit this URL:
I get 404 page. Here’s the message that I get:
Using the URLconf defined in scrap.urls, Django tried these URL patterns, in this order:
admin/
api-auth/
getpage/
^<slug>/$ [name='ArticleInfoViewSet-list']
^<slug>/(?P<slug>[^/.]+)/$ [name='ArticleInfoViewSet-detail']
__debug__/
The current path, slugtest/, didn’t match any of these.
However, if I use
http://127.0.0.1:8000/<slug>/
the page loads perfectly, but that’s not intended behavior.
models.py
class Article(models.Model):
url = models.CharField(max_length=255,validators=[RegexValidator(regex=website_regex)])
slug = models.CharField(max_length=255, null=True)
unique_id = models.CharField(max_length=255, unique=True, null=True)
views.py
class ArticleInfoViewSet(ModelViewSet):
serializer_class = ArticleInfoSerializer
lookup_url_kwarg = 'slug'
lookup_field = 'slug'
def get_queryset(self):
queryset = Article.objects.prefetch_related('data')
return queryset
serializer.py
class ArticleInfoSerializer(serializers.ModelSerializer):
data = ArticleDataSerializer(many=True)
class Meta:
model = Article
fields = ['url', 'data', 'slug']
lookup_field = ['slug']
read_only_fields = ['url', 'data', 'slug']
urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('getpage/', include('getpage.urls')),
path('', include('getpage.urls2')),
path('__debug__/', include('debug_toolbar.urls')),
]
urls2.py
from . import views
from rest_framework_nested import routers
router = routers.SimpleRouter()
router.register('<slug>', views.ArticleInfoViewSet, basename='ArticleInfoViewSet')
urlpatterns = router.urls
What am I doing wrong here?
router.register('', views.ArticleInfoViewSet, basename='ArticleInfoViewSet')
Easy mistake.
You have set everything up perfectly from what I can see, but you registered the route as literally /<slug>
hehe 🙂 whereas I’m guessing you wanted a little API at the root of the app which accepts a slug url parameter? To prove this, try accessing /<slug>/slugtest
Django rest framework will do lots of magical things when you register a viewset. Including considering the lookupfield kwarg and url kwarg attributes you have set. DRF will also automagically generate relevant url patterns based on your viewset configuration, hence why you need not specify and url parameters when using viewsets, there are however, ways to configure it and customise the paths.
I would not recommend putting an API with a slug parameter at the root of your app, it might be more appropriate to prefix the path somewhere with "api/" so it would become api/slugtest
and even better still, for the article viewset, api/articles/slugtest
So you would change the url:
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('getpage/', include('getpage.urls')),
path('api', include('getpage.urls2')),
path('__debug__/', include('debug_toolbar.urls')),
]
And finally register it as articles
router.register('articles', views.ArticleInfoViewSet, basename='ArticleInfoViewSet')