Django Path To Resource Failure? - python

When I try a GET request on one of my API endpoints it can't find the endpoint.
urls.py file looks like this
from django.urls import path, include
from django.contrib import admin
from api.resources import NoteResource
note_resource = NoteResource()
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(note_resource.urls)),
]
api.resources looks like this
from tastypie.resources import ModelResource
from api.models import Note
class NoteResource(ModelResource):
class Meta:
queryset = Note.objects.all()
resource_name = 'note'
Any idea why this is happening?
Solution: It appears that http://127.0.0.1:8000/api/note/ works properly.. why would this be?

You should also have one url entry in note_resource.urls for only /api request. Something similar to
path('api/', APIClass).
But, you never need that endpoint. Because, /api does not represent any actual request in your system.
I rather suggest to have following endpoints :
path('api/notes/',include(note_resource.urls))
in your main urls.py.
So that you can have multiple urls in main urls.py file representing each app.
path('api/<APP_NAME>(s)/',include(<APP_NAME>.urls))
And, you will manage other endpoints in your app urls.py file:
# Create a new Note
path(
'create/',
NoteCreate.as_view()
),

Related

DRF: API root view doesn't list my APIs for some reasons

The bounty expires in 4 days. Answers to this question are eligible for a +50 reputation bounty.
varnie wants to draw more attention to this question.
I have the following urls.py for an app named service where I register API endpoints:
from .views import AccessViewSet, CheckViewSet
app_name = "api"
router = DefaultRouter()
router.register(r"access/(?P<endpoint>.+)", AccessViewSet, basename="access")
router.register(r"check/(?P<endpoint>.+)", CheckViewSet, basename="check")
urlpatterns = [
path("", include(router.urls)),
]
Below is my project's urls.py where I use it:
from django.conf import settings
from django.contrib import admin
from django.urls import include, path
import service.urls as service_urls
urlpatterns = [
# ...
path("service/", include('service.urls')),
]
The APIs themselves are functioning properly, but I am having trouble making them work with DRF's default API root view. The view is displaying an empty list of available endpoints. I'm not sure, but this issue may be related to the regular expressions I'm using when registering endpoints, such as r"access/(?P<endpoint>.+). If this is indeed the problem, how can I resolve it?"
DefaultRouter needs to run reverse(viewname) on your views to generate urls for the default view. It won't know about your dynamic parameter endpoint when it runs.
If it's acceptable to change your url structure to {endpoint}/access/... and {endpoint}/check/... you can:
router.register(r"access", AccessViewSet, basename="access")
router.register(r"check", CheckViewSet, basename="check")
urlpatterns = [
re_path(r"(?P<endpoint>.+)/", include(router.urls)),
]
After which, each {endpoint}/ view should have a working default API view.
If you need to preserve your url structure, you'll have to manually generate a list of available routes like this:
from collections import OrderedDict
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.reverse import reverse
class ApiRoot(APIView):
"""My API root view"""
def get(self, request, format=None):
"""List of available API endpoints"""
api_methods = [
"access-list",
"check-list",
]
endpoints = [
"endpoint-a",
"endpoint-b",
]
routes = OrderedDict()
for endpoint in endpoints:
for method in api_methods:
routes[endpoint + method] = reverse(
method, endpoint=endpoint, request=request, format=format
)
return Response(routes)
Notice the endpoint=endpoint in the reverse call.
If the code above doesn't help, please, expand your question with more details of what you're trying to archive in the first place. I have a feeling you might want to replace your viewsets with an EndpointViewSet with custom ViewSet actions for check and access.
Finally, if you're looking for routes like {endpoint_id}/access/{access_id}/... and {endpoint_id}/check/{check_id}/... check out drf-nested-routers - a drop-in replacement for DRF's Routers that supports nesting.

Django generateschema ignores URLs

I am trying to learn to create a Backend with Django, together with an Angular frontend.
In order to make the api a little more consistent I tried to create a API schema to use the OpenAPI Generator.
I have run the command ./manage.py generateschema --file schema.yml. But: The yml file does not have any information about the users.url. I have added the get_schema_view from the rest_framework, with the same result.
The (main) app urls.py looks like this:
from django.conf.urls import include
from django.contrib import admin
from django.urls import path
from rest_framework.schemas import get_schema_view
urlpatterns = [
path('admin/', admin.site.urls),
path('api/users/', include('users.urls'), name="users"),
path('api/network/', include('networkController.urls'), name="network"),
path('api/files/', include('fileController.urls'), name="files"),
path('api/', get_schema_view(
title="API Documentation",
description="API for all things"
), name='openapi-schema')
]
The networkController.urls looks like this:
from django.urls import path
from . import views
urlpatterns = [
path('', views.startNetwork)
]
which is found by the schema generator.
The users.urls looks like this:
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.login),
path('register/', views.registerUser)
]
I have tried to move all urls to the (main) backend.urls and include the views directly, I have tried "layering" them all.
# backend.urls:
path('api/', include('api.urls'))
# api.urls:
path('users/', include('users.urls'))
without any changes.
I tried looking up, why - but without success. If I run the server and make a GET-Request via curl to localhost:8000/api/users/login directly, it works perfectly fine.
Could you please help me figure out, what I did wrong or guide me to a tutorial, which would cover the topic a little more detailled?
(And yes. Maybe I should just switch to something like FastAPI, but I really love Djangos Auth.Users and the easy constant, connection to a database)
Thanks in advance!
(EDIT: You can find the whole code in my GitHub)
I just figure out why my code didn't work. I use Class Based Views but I think this will also work for Method Based ones.
It looks like generateschema does not like uppercase method names:
class ...(UpdateAPIView):
serializer_class = ...
permission_classes = [IsAuthenticated]
http_method_names = ['PUT']
...
Instead of ['PUT'] I used ['put']
class ...(UpdateAPIView):
serializer_class = ...
permission_classes = [IsAuthenticated]
http_method_names = ['put']
...
So, replace all the uppercase letters with lowercase letters.

django_tastypie can't recognize url

I am starting a new project with Django and neo4j, and the most promising library for developing REST services is TastyPie (Django Rest Framework is to tied to ORM, and neo4j is not a relational database).
I am following this tutorial in order to make TastyPîe to work with not relational databases, and I already override the get methods, but I get this error when trying to access the endpoint:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/api/items_manager/items/
Using the URLconf defined in AttractoraGraph.urls, Django tried these URL patterns, in this order:
admin/
api/ general/
api/ items_manager/ items/ ^(?P<resource_name>item)/$ [name='api_dispatch_list']
api/ items_manager/ items/ ^(?P<resource_name>item)/schema/$ [name='api_get_schema']
api/ items_manager/ items/ ^(?P<resource_name>item)/set/(?P<pk_list>.*?)/$ [name='api_get_multiple']
api/ items_manager/ items/ ^(?P<resource_name>item)/(?P<pk>.*?)/$ [name='api_dispatch_detail']
The current path, api/items_manager/items/, didn't match any of these.
This is my general urls.py:
from django.contrib import admin
from django.urls import path
from django.urls import path, include
api_url_patterns = [
path('general/', include('GeneralApp.urls')),
path('items_manager/', include('ItemsManagerApp.urls')),
]
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(api_url_patterns)),
This is my application's urls.py:
from django.urls import path, include
from ItemsManagerApp import resources
urlpatterns = [
path('items/', include(resources.ItemResource().urls)),
]
this is my resources.py:
from tastypie import fields, utils
from tastypie.authorization import Authorization
from tastypie.resources import Resource
from ItemsManagerApp import models
class ItemResource(Resource):
uid = fields.CharField(attribute='uid')
name = fields.CharField(attribute='name')
description = fields.CharField(attribute='description')
created = fields.DateTimeField(default=utils.now, attribute='created')
def get_object_list(self, request):
return list(Item.nodes.all())
def obj_get_list(self, bundle, **kwargs):
return self.get_object_list(bundle.request)
def obj_get(self, bundle, **kwargs):
item = Item.nodes.filter(uid=kwargs['pk'])
return item
I can see that the endpoint url api/items_manager/items/ is in the list, but with something else that is not referenced in the documentation.
I'll appreciate any idea of how to solve this.
In this situation, you can try to access this endpoint http://127.0.0.1:8000/api/items_manager/items/item/.
Maybe this tutorial can help you.

Django url not finding a template using CreateView in Class-Based Views

I have created a page where a user can add a new item (notes in this case) and I am making use of CBV which I have recently started learning.
This is my model form
class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ('title', 'note', 'tags')
This is the view in views.py
class NoteCreate(CreateView):
model = Note
form_class = NoteForm
template_name = "add_note.html"
Then this is the url as I used in the urls.py of the app
from django.conf.urls import patterns, url
from . import views
from madNotes.views import NoteCreate, NoteIndex,
urlpatterns = patterns(
'',
url(r'^notes/add/$', NoteCreate.as_view(), name="new_note"),
url(r'^$', NoteIndex.as_view()),
url(r'^(?P<slug>\S+)/$', views.NoteDetail.as_view(), name="entry_detail"),
)
NB: I used the same url as the main page at 127.0.0.1:8000 in the projects urls.py file and it worked.
I have seen several tutorials and even the docs and can't seem to find what I am doing wrong. Will I also need to add a function in order for it to be saved in the db or the CBV will do it all?
EDit: The error I get is this
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/notes/add/
Here is the project's urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
from MadNotez import settings
from registration.backends.default.views import RegistrationView
from madNotes.forms import ExRegistrationForm
if settings.DEBUG:
import debug_toolbar
urlpatterns = patterns('',
url(r'^__debug__/', include(debug_toolbar.urls)),
url(r'accounts/register/$', RegistrationView.as_view(form_class = ExRegistrationForm), name='registration_register'),
url(r'^accounts/', include('registration.backends.simple.urls')),
url(r'^admin/', include(admin.site.urls)),
url('^markdown/', include('django_markdown.urls')),
url('^notes/', include('madNotes.urls')),
#url(r'^$', views.NoteCreate.as_view(), name="new note"), when I used it here it worked
)
you say that is the urls.py of the app, which means it is included by the project's urls.py.
As you show now, all the app's URIs go under the notes prefix:
url('^notes/', include('madNotes.urls')),
so as things stand at present the correct URI for the page is
http://127.0.0.1:8000/notes/notes/add/
In order to clean things up a bit, I'd suggest to modify the app's urls.py to
url(r'^add/$', NoteCreate.as_view(), name="new_note"),
so that the page can be reached at
http://127.0.0.1:8000/notes/add/
This way all the app's pages/services are available under the notes prefix and with a simple name that is consistent with their action (i.e. add, delete, etc)

Django urlresolvers reverse for dynamically generated ids

I've got to test URLs that look like this:
http://127.0.0.1:8000/payment/pay/513623c9/
but I'm not sure how to say this to the reverse() function. This is my urls.py
from django.conf.urls import patterns, url
from payment import views
from payment.msn import *
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^msnstats/', MsnStats.finish_transaction, name='finish_transaction'),
url(r'^pay/(?P<payment_id>\w+)/$', views.pay, name='pay')
)
And this is what the relevant part of my views.py looks like:
def pay(request, payment_id):
try:
plan=PaymentPlan.objects.get(payment_id=payment_id)
The payment_id is generated for each plan, so I first create a plan, get its payment_id from the database, and somehow call it. I'm just not sure how to use reverse.
from django.core.urlresolvers import reverse
url = reverse('pay', args=[plan.payment_id])
or
url = reverse('pay', kwargs={'payment_id': plan.payment_id})
Both versions are valid.
UPDATE: If you include the payment.urls with namespace argument then you have to add this namespace to the url name in the reverse() call:
project/urls.py:
url(r'^payment/', include('payment.urls', namespace='payment')),
payment/tests.py:
response = self.client.get(reverse('payment:pay', args=[plan.payment_id]))

Categories

Resources