Can't get Django REST Swagger to work with routers - django

I have a Django REST Framework project and I'm trying to generate Swagger documentation. In my URL's file, however, I use a router and that doesn't seem to work very well with Swagger. I followed the instructions in this discussion and here's what I got:
This works perfectly:
router = CustomRouter(
schema_title='My API',
schema_renderers=[renderers.CoreJSONRenderer, OpenAPIRenderer],
trailing_slash=False
)
router.register(r'users', UserViewSet, base_name='user')
router.register(r'foos', FooFieldViewSet)
router.register(r'bars', BarViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^admin/', include(admin.site.urls)),
url(r'^session-auth/', include('rest_framework.urls')),
url(r'^rest-auth/', include('rest_auth.urls')),
]
And I get the expected page:
If I add SwaggerUIRenderer to the schema_renderers list, however, it doesn't work anymore:
router = CustomRouter(
schema_title='My API',
schema_renderers=[renderers.CoreJSONRenderer, OpenAPIRenderer, SwaggerUIRenderer],
trailing_slash=False
)
# ...
I get this page:
What am I doing wrong??
UPDATE
If I add ?format=swagger, I get:
/home/vagrant/ve/lib/python3.4/importlib/_bootstrap.py:321: RemovedInDjango110Warning: django.core.context_processors is deprecated in favor of django.template.context_processors.
return f(*args, **kwds)
[22/Jul/2016 09:00:10] "GET /?format=swagger HTTP/1.1" 403 3688
Internal Server Error: /
Traceback (most recent call last):
File "/home/vagrant/ve/lib/python3.4/site-packages/django/core/handlers/base.py", line 174, in get_response
response = self.process_exception_by_middleware(e, request)
File "/home/vagrant/ve/lib/python3.4/site-packages/django/core/handlers/base.py", line 172, in get_response
response = response.render()
File "/home/vagrant/ve/lib/python3.4/site-packages/django/template/response.py", line 160, in render
self.content = self.rendered_content
File "/home/vagrant/ve/lib/python3.4/site-packages/rest_framework/response.py", line 70, in rendered_content
ret = renderer.render(self.data, media_type, context)
File "/home/vagrant/ve/lib/python3.4/site-packages/rest_framework_swagger/renderers.py", line 18, in render
data = json.loads(codec.dump(data))
File "/home/vagrant/ve/lib/python3.4/site-packages/openapi_codec/__init__.py", line 34, in dump
data = generate_swagger_object(document)
File "/home/vagrant/ve/lib/python3.4/site-packages/openapi_codec/encode.py", line 8, in generate_swagger_object
parsed_url = urlparse.urlparse(document.url)
AttributeError: 'list' object has no attribute 'url'
[22/Jul/2016 09:00:10] "GET /?format=openapi HTTP/1.1" 500 81912

At this point, the SwaggerUIRenderer does not override the default browsable API of the root view (html), so you have to specify the format query parameter to switch between renderers.
For example:
http://localhost:8000/?format=swagger -> Swagger UI interface
http://localhost:8000/?format=openapi-> Swagger/OpenAPI JSON spec
http://localhost:8000/?format=corejson -> CoreAPI CoreJSON
As for the 403, sounds like either an authentication config or you are not logged in.
Alternatively, remove the schema attributes of the router, and instead create a separate view that returns the schema.

Related

Django_hosts integration produces 'NoReverseMatch at /en/api/' when attempting to access api

I'm in the process of integrating Django_hosts. I have the front end URLs and the admin URLs successfully integrated and working, but my REST API URLs are still inaccessible due to this error:
'NoReverseMatch at /en/api/'
The error is traced to Django/urls/base.py, but I'm not sure what is triggering it:
Traceback (most recent call last):
File "/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/lib/python3.6/site-packages/django/core/handlers/base.py", line 124, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/lib/python3.6/site-packages/django/contrib/auth/decorators.py", line 23, in _wrapped_view
resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
File "/lib/python3.6/site-packages/django/shortcuts.py", line 148, in resolve_url
return reverse(to, args=args, kwargs=kwargs)
File "/lib/python3.6/site-packages/django/urls/base.py", line 86, in reverse
raise NoReverseMatch("%s is not a registered namespace" % key)
django.urls.exceptions.NoReverseMatch: 'admin' is not a registered namespace
These are my hosts.py and api_urls.py files:
# hosts.py
host_patterns = patterns(
'',
host(r'www', settings.ROOT_URLCONF, name='www'),
host(r'admin', 'Project.admin_urls', name='admin'),
host(r'api', 'Project.api_urls', name='api'),
)
# api_urls.py
urlpatterns += i18n_patterns (
path('session_security/', include('session_security.urls')),
path('', include('Project.apps.api.urls'), name='api'),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Also, to kill two birds with one stone, when attempting to convert this url:
{% url 'profile' request.user.id %}
to this url:
{% host_url 'profile' host 'www' request.user.id %}
It fails throwing a NoReverseMatch error with no arguments, as if the arguments are ignored.
Any help from someone knowledgeable about this package or either of these issues would be appreciated.
Thanks.
Update
I resolved this error by including admin.site urls within the api_urls.py file:
# api_urls.py
urlpatterns += i18n_patterns (
path('session_security/', include('session_security.urls')),
path('', include('Project.apps.api.urls'), name='api'),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
path('', admin.site.urls, name='admin'), <--This is key, but not sure exactly why.
However, now I've run into something strange, which isn't exactly an error, but effectively acts as one for my use case. The API seems to no longer recognize request.user, so that even if a user is logged in, the API thinks he is AnonymousUser.
*Both API and Admin subdomains not recognizing logged in user. Seems to be something to do with SESSION cookie...

Django sending users to the wrong template location?

I'm working on Python Crash Course Chapter 19, where we are setting up a 'learning log' application. I'm stuck (on page 440) with a template reference error.
I have reworked this section of the book, in addition to taking all of the updates listed here: https://ehmatthes.github.io/pcc/chapter_19/README.html
but seem to keep getting what looks to be a template reference error. I noticed the template is referencing the 'learning_logs' directory when looking for the login.html, which is incorrect -- it should be looking in the 'learning_log' directory.
learning_log\users\urls.py
from django.contrib.auth import views as auth_views
from . import views
app_name = 'users'
urlpatterns = [
# Login page.
path('login/',
auth_views.LoginView.as_view(template_name='users/login.html'),
name='login'),
]
learning_logs\urls.py
"""learning_logs URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
"""Defines URL patterns for learning_logs."""
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [
# Home page.
path('', views.index, name='index'),
#Show all topics.
path('topics/', views.topics, name='topics'),
#Detail page for a single topic.
path('topics/<int:topic_id>/', views.topic, name='topic'),
#Page for adding a new topic.
path('new_topic/', views.new_topic, name='new_topic'),
#Page for adding a new entry.
path('new_entry/<int:topic_id>/', views.new_entry, name='new_entry'),
#Page for editing an entry.
path('edit_entry/<int:entry_id>/', views.edit_entry, name='edit_entry'),
]
learning_log\urls.py
"""learning_log URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.urls import path, include
from django.contrib import admin
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('users.urls')),
path('', include('learning_logs.urls')),
]
Here is the browser error:
TemplateDoesNotExist at /users/login/
users/login.html
Request Method:
GET
Request URL:
http://localhost:8000/users/login/
Django Version:
2.2.1
Exception Type:
TemplateDoesNotExist
Exception Value:
users/login.html
Exception Location:
C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\loader.py in select_template, line 47
Python Executable:
C:\Users\kevin\Documents\webapps\11_env\Scripts\python.exe
Python Version:
3.7.3
Python Path:
['C:\\Users\\kevin\\Documents\\webapps',
'C:\\Users\\kevin\\AppData\\Local\\Programs\\Python\\Python37-32\\python37.zip',
'C:\\Users\\kevin\\AppData\\Local\\Programs\\Python\\Python37-32\\DLLs',
'C:\\Users\\kevin\\AppData\\Local\\Programs\\Python\\Python37-32\\lib',
'C:\\Users\\kevin\\AppData\\Local\\Programs\\Python\\Python37-32',
'C:\\Users\\kevin\\Documents\\webapps\\11_env',
'C:\\Users\\kevin\\Documents\\webapps\\11_env\\lib\\site-packages']
Server time:
Sun, 26 May 2019 22:14:26 +0000
Template-loader postmortem
Django tried loading these templates, in this order:
Using engine django:
django.template.loaders.app_directories.Loader: C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\contrib\admin\templates\users\login.html (Source does not exist)
django.template.loaders.app_directories.Loader: C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\contrib\auth\templates\users\login.html (Source does not exist)
django.template.loaders.app_directories.Loader: C:\Users\kevin\Documents\webapps\learning_logs\templates\users\login.html (Source does not exist)
Here is the server error:
Traceback (most recent call last):
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\core\handlers\base.py", line 145, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\core\handlers\base.py", line 143, in _get_response
response = response.render()
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\response.py", line 106, in render
self.content = self.rendered_content
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\response.py", line 81, in rendered_content
template = self.resolve_template(self.template_name)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\response.py", line 63, in resolve_template
return select_template(template, using=self.using)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\loader.py", line 47, in select_template
raise TemplateDoesNotExist(', '.join(template_name_list), chain=chain)
django.template.exceptions.TemplateDoesNotExist: users/login.html
[26/May/2019 15:12:27] "GET /users/login/ HTTP/1.1" 500 79384
Not Found: /users/
[26/May/2019 15:12:39] "GET /users/ HTTP/1.1" 404 3224
Internal Server Error: /users/login/
Traceback (most recent call last):
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\core\handlers\base.py", line 145, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\core\handlers\base.py", line 143, in _get_response
response = response.render()
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\response.py", line 106, in render
self.content = self.rendered_content
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\response.py", line 81, in rendered_content
template = self.resolve_template(self.template_name)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\response.py", line 63, in resolve_template
return select_template(template, using=self.using)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\loader.py", line 47, in select_template
raise TemplateDoesNotExist(', '.join(template_name_list), chain=chain)
django.template.exceptions.TemplateDoesNotExist: users/login.html
[26/May/2019 15:12:43] "GET /users/login/ HTTP/1.1" 500 79384
Internal Server Error: /users/login/
Traceback (most recent call last):
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\core\handlers\base.py", line 145, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\core\handlers\base.py", line 143, in _get_response
response = response.render()
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\response.py", line 106, in render
self.content = self.rendered_content
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\response.py", line 81, in rendered_content
template = self.resolve_template(self.template_name)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\response.py", line 63, in resolve_template
return select_template(template, using=self.using)
File "C:\Users\kevin\Documents\webapps\11_env\lib\site-packages\django\template\loader.py", line 47, in select_template
raise TemplateDoesNotExist(', '.join(template_name_list), chain=chain)
django.template.exceptions.TemplateDoesNotExist: users/login.html
double check your templates directory in settings and make sure slashes are forward slashes / not \
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [''],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
Are you sure you haven't mistyped anything? I do that heaps and it always messes up my programs. Try checking where you've typed learning_logs or learning_log and see if you need to add or remove an s.
It appears that the book suggests you save the login.html file to the learning_log/users/templates folder, but it should be in the learning_logs folder. Once I moved the templates, that fixed my issue. Thank you both Nurhun and Lumiobyte!

Django 1.9.2 test Client issue

I'm using Django==1.9.2 and djangorestframework==3.3.2, and django.test.Client to make some tests. The problem is that when I execute my tests I'm gettting this error:
ERROR: test_view (main.tests.test_http.TestMainViewSet)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/vladir/work/all/project-django1.9/saxo-publish/publish/main/tests/test_http.py", line 111, in test_view
content_type='application/json'
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 515, in post
secure=secure, **extra)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 314, in post
secure=secure, **extra)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 380, in generic
return self.request(**r)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 449, in request
response = self.handler(environ)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/test/client.py", line 123, in __call__
response = self.get_response(request)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 245, in get_response
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 296, in handle_uncaught_exception
return callback(request, **param_dict)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/utils/decorators.py", line 166, in _wrapped_view
return middleware.process_response(request, response)
File "/home/vladir/work/all/project-django1.9/venv/local/lib/python2.7/site-packages/django/middleware/csrf.py", line 230, in process_response
request.META["CSRF_COOKIE"],
KeyError: u'CSRF_COOKIE'
My test code looks like this:
import json
from django.test import Client
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
class TestMainViewSet(TestCase):
def setUp(self):
self.client = Client(
HTTP_HOST='example.com' # I have also tried removing this
)
self.create_read_url = reverse('books-list')
User.objects.create_user(
username="username",
email="username#zunzun.se",
password="123"
)
def test_create(self):
self.client.login(username='username', password="123")
# In this case I'm doing a POST, but it is the same with a GET
response = self.client.post(
self.create_read_url,
data=json.dumps({'title': "Create"}), # I have also tried without the json.dumps
content_type='application/json'
)
data = json.loads(response.content)
print data
self.assertEqual(response.status_code, 201)
self.assertEquals(data['title'], "Create")
my view code is:
from django.contrib.auth.mixins import LoginRequiredMixin
from rest_framework import viewsets
from .serialiazers import (
BookSerializerRead,
BookSerializerWrite,
)
class MainViewSet(LoginRequiredMixin, viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class_read = BookSerializerRead
serializer_class_write = BookSerializerWrite
on the urls.py:
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'books', MainViewSet, 'books')
urlpatterns = [
url(r'^api/', include(router.urls)),
]
According with the Django doc about it, I should not need anything additional to avoid the CSRF checks,
because as textually said there: "By default, the test client will disable any CSRF checks performed by your site.", and I also know that enforce_csrf_checks=False by default
on the Client.
I have found one detail though, if I create an instance of the client that way self.client = Client(HTTP_HOST='example.com', CSRF_COOKIE='xxxxxx') then it works, but is that
actually needed? It is not what the documentation says, so I suppose I'm doing something wrong. Could someone help me with that please? I will appreciate any help about.
Thanks in advance
Try to use DRF's APITestCase as base class for test cases:
from rest_framework.testing import APITestCase
class TestMainViewSet(APITestCase):
...

testing django web app that uses cookies/session

In views.py:
get_dict = Site.objects.getDictionary(request.COOKIES['siteid'])
{gets a dictionary with site information based on id from cookie}
In tests.py:
from django.test import TestCase
class WebAppTest(TestCase):
def test_status(self):
response = self.client.get('/main/',{})
response.status_code # --->passed with code 200
response = self.client.get('/webpage/',{'blog':1})
response.status_code # ----> this is failing
In order to present blog page it goes to a view where it gets a dictionary using existing cookie, process it, renders templates, which works fine when running the app. But the tests are failing.Having never tested Django webapps I'm not sure how to test it right. Here is the traceback.
Traceback (most recent call last):
File "<console>", line 2, in <module>
File "/usr/lib/pymodules/python2.6/django/test/client.py", line 313, in post
response = self.request(**r)
File "/usr/lib/pymodules/python2.6/django/core/handlers/base.py", line 92, in get_response
response = callback(request, *callback_args, **callback_kwargs)
File "/var/lib/django/data/../webpage/views.py", line 237, in getCostInfo
get_dict = Site.objects.getDictionary(request.COOKIES['siteid'])
KeyError: 'siteid'
Went through some online samples but couldn't find something that deals in depth with cookies/sessions. Any ideas or directs to useful links are highly appreciated.
Take a look at the Persistent State section of the Django Testing docs.
In your case, I would expect your test to be something more like:
from django.test import TestCase
from django.test.client import Client
class WebAppTest(TestCase):
def setUp(self):
self.client = Client()
session = self.client.session
session['siteid'] = 69 ## Or any valid siteid.
session.save()
def test_status(self):
response = self.client.get('/main/',{})
self.assertEqual(response.status_code, 200)
response = self.client.get('/webpage/',{'blog':1})
self.assertEqual(response.status_code, 200)

how to activate DJANGO.CORE.CONTEXT_PROCESSORS.REQUEST

I read this
DJANGO.CORE.CONTEXT_PROCESSORS.REQUEST
If TEMPLATE_CONTEXT_PROCESSORS contains this processor, every
RequestContext will contain a variable request, which is the current
HttpRequest. Note that this processor is not enabled by default;
you'll have to activate it.
from this page
But it seems there is no information how to activate this processor.
Here is my original question
Access request in django custom template tags
After i followed the answer
i still got errors
TemplateSyntaxError at / Caught an exception while rendering: 'request' Original Traceback (most recent call last):
File "C:\Python25\lib\site-packages\django\template\debug.py", line 71, in render_node result = node.render(context)
File "C:\Python25\lib\site-packages\django\template__init__.py", line 936, in render dict = func(*args)
File "c:\...\myapp_extras.py", line 7, in login request = context['request']
File "C:\Python25\lib\site-packages\django\template\context.py", line 44, in getitem raise KeyError(key) KeyError: 'request'
the code causing problem is
request = context['request'] in
from django import template
register = template.Library()
#register.inclusion_tag('userinfo.html',takes_context = True)
def userinfo(context):
request = context['request']
address = request.session['address']
return {'address':address}
I answered this here: How can I pass data to any template from any view in Django?
Also see the comments on my answer... you might want that bit of info too.
in settings.py
from django.conf import global_settings
TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'django.core.context_processors.request',
)