I need to rewrite an existing WebService as part of a Django application, the goal is to integrate the Django application to a legacy system that can just call this particular WebService.
So I do have a very complex WSDL file with several methods and a huge data structure. Is there any way to generate a stub for an application in Django using that WSDL file, or do I have to create all the necessary data structures and method signatures myself?
Check out Zeep
Zeep inspects the WSDL document and generates the corresponding code to use the services and types in the document.
views.py (Server)
from django.http import HttpResponse
from django.views import View
from zeep import Client
class MyConvertKilometer2MilesView(View):
def get(self, request):
# <view logic>
client = Client('http://www.webservicex.net/ConvertSpeed.asmx?WSDL')
result = client.service.ConvertSpeed(
100, 'kilometersPerhour', 'milesPerhour')
return HttpResponse('result')
If you are not sure how fast you get returns (due to the nature of SOAP), use a
dango-channels tasks or
celery tasks
Command Line tests with Zeep
If you like to test something inbetween, you may us the command line interface of Zeep to make this easy.
python -mzeep http://www.soapclient.com/xml/soapresponder.wsdl
The "Client" class from the "zeep" library is used to create a client object that will connect to the specified WSDL endpoint, "http://www.webservicex.net/ConvertSpeed.asmx?WSDL". This client object is then used to call the "ConvertSpeed" method of the web service, passing in the parameters "100", "kilometersPerhour", and "milesPerhour".
views.py (Client)
A Django example that generates a client view would be:
from django.http import HttpResponse
from django.views import View
from zeep import Client
class MyConvertKilometer2MilesView(View):
def get(self, request, speed):
client = Client('http://www.webservicex.net/ConvertSpeed.asmx?WSDL')
result = client.service.ConvertSpeed(speed, 'kilometersPerhour', 'milesPerhour')
return HttpResponse(result)
You also need to update the routing configuration in urls.py file to include this parameter in the url path.
from django.urls import path
from .views import MyConvertKilometer2MilesView
urlpatterns = [
path('convert/<int:speed>/', MyConvertKilometer2MilesView.as_view()),
]
Related
I have some Django DRF tests that look like this:
from django.conf import settings
from rest_framework.test import APILiveServerTestCase, RequestsClient
class APITests(APILiveServerTestCase):
def setUp(self):
self.client = RequestsClient()
def test_endpoints(self):
with self.settings(MEDIA_ROOT=f"{settings.BASE_DIR}/test_media/"):
# Upload file
self.client.post(
URL,
files={"my_file": ("filename", b"abc")},
)
# Download the file we just uploaded
response = self.client.get(
URL_to_FILE,
)
The upload works fine, but the download fails with a 404, because the test webserver isn't serving media files, and more specifically, not serving them from my custom MEDIA_ROOT=f{settings.BASE_DIR}/test_media/ folder.
Adding entries to urls.py to have Django serve media files didn't work.
Django appears to have a TestCase subclass specifically designed for this: django.contrib.staticfiles.testing.StaticLiveServerTestCase. DRF doesn't have anything like this, but I figured I could make one like this:
from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from rest_framework.test import APIClient
class APIStaticLiveServerTestCase(StaticLiveServerTestCase):
client_class = APIClient
class APITests(APIStaticLiveServerTestCase):
....
This didn't work either.
Is there a way to make this work?
I am testing loginradius for authentication in a fastapi app but cant seem to get the redirect or modal form to work correctly. I have tried using the OAuth2PasswordBearer and OAuth2AuthorizationCodeBearer classes but neither worked as desired. I would like to be able to click the "Authorize" button and either get redirected to the loginradius login page https://<app_name>.hub.loginradius.com (and returned to the api page with the token from loginradius) or render the loginradius login/registration form instead of the fastapi OAuth2PasswordBearer form. example:
Instead of
Desired outcome
Small Code Snippet
"""Fastapi routes for all User related endpoints"""
from fastapi import APIRouter, Depends
from fastapi.security import OAuth2PasswordBearer
router = APIRouter()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# oauth2_scheme = OAuth2AuthorizationCodeBearer(
# tokenUrl="https://<app_name>.hub.loginradius.com",
# authorizationUrl="https://<app_name>.hub.loginradius.com")
#router.post("/me", response_model=None)
def create_me(token: str = Depends(oauth2_scheme)):
return {"token": token}
Is that possible?
Thanks
I got some REST API endpoints in Django and I'd like to use the same authentication for Graphene. The documentation does not provide any guidance.
For example, if you are using authentication_classes = (TokenAuthentication,) in your API views, you could add an endpoint to a GraphQLView decorated in this way:
urls.py:
# ...
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import authentication_classes, permission_classes, api_view
def graphql_token_view():
view = GraphQLView.as_view(schema=schema)
view = permission_classes((IsAuthenticated,))(view)
view = authentication_classes((TokenAuthentication,))(view)
view = api_view(['GET', 'POST'])(view)
return view
urlpatterns = [
# ...
url(r'^graphql_token', graphql_token_view()),
url(r'^graphql', csrf_exempt(GraphQLView.as_view(schema=schema))),
url(r'^graphiql', include('django_graphiql.urls')),
# ...
Note that we added a new ^graphql_token endpoint and kept the original ^graphql which is used by the GraphiQL tool.
Then, you should set the Authorization header in your GraphQL client and point to the graphql_token endpoint.
UPDATE: See this GitHub issue where people have suggested alternative solutions and full working examples.
Adding some additional steps that I had to take when following this integration:
class RTGraphQLView(GraphQLView):
def parse_body(self, request):
if type(request) is rest_framework.request.Request:
return request.data
return super().parse_body(request)
Graphene was expecting the .body attr but DRF reads it and attaches it to .data before being passed to GraphQLView.
I am using the django rest framework to perform API calls via IOS
and I get the following error
"CSRF Failed: CSRF cookie not set."
Here's my django API code:
class LoginView(APIView):
"""
List all snippets, or create a new snippet.
"""
#csrf_exempt
def get(self, request, format=None):
startups = Startup.objects.all()
serializer = StartupSerializer(startups, many=True)
return Response(serializer.data)
#csrf_exempt
def post(self, request, format=None):
profile = request.POST
....
What can I do?
If anyone is still following this question, the direct answer is that you need to use the decorator on the view method itself. The get and post methods defined on the APIView class just tell DRF how the actual view should behave, but the view method that the django router expects is not actually instantiated until you call LoginView.as_view().
Thus, the solution is to add the csrf_exempt decorator to urls.py. It might look as follows:
#file: urls.py
from django.conf.urls import patterns, url
from django.views.decorators.csrf import csrf_exempt
import views
urlpatterns = patterns('',
url('^login/$', csrf_exempt(views.LoginView.as_view())),
...
)
However, as Mark points out above, csrf protection is important to prevent your sessions from being hijacked. I haven't worked with iOS myself, but I would look into using django's cookie-based csrf tokens. You can use the ensure_csrf_cookie decorator to make django send a csrftoken cookie with a response, and your POST requests will validate as long as you include that token as an X-CSRFToken header.
I've had the same issue. My problem was that I forgot to put .as_view() in urls.py on MyAPIView. So it have to be like:
url(r'$', GetLikesAPI.as_view(), name='list')
not:
url(r'$', GetLikesAPI, name='list')
The problem you encounter here is that django for processing your view is using whatever as_view() method will return, not directly method get() or post().
Therefore you should decorate your class-based view in one of following ways:
In urls.py
urlpatterns = patterns('',
url('^login/$', csrf_exempt(views.LoginView.as_view())),
...
)
or on dispatch() method (pre django 1.9)
from django.utils.decorators import method_decorator
class LoginView(APIView):
#method_decorator(csrf_exempt)
def dispatch(self, *args, **kwargs):
...
or on class view itself (from django 1.9)
from django.utils.decorators import method_decorator
#method_decorator(csrf_exempt, name='dispatch')
class LoginView(APIView):
...
For GETs, you shouldn't be modifying data, so a CSRF isn't required.
If you are modifying data with a POST, then you SHOULD have a CSRF if you are using session based authentication. Otherwise, you're opening up a security hole. Even though you think your Django server is going to be servicing iPhone apps, there's nothing to stop someone with your app from sniffing the packets on the traffic to your server, and then reverse engineering access to the server with other kinds of web clients. For this reason, Django Rest Framework requires a CSRF in some cases. This is mentioned in the Django rest framework documentation.
The path around this requirement for POSTs is to not use session authentication. For example, you could use BasicAuthentication over HTTPS. With this authentication mechanism, you should use HTTPS to prevent credentials from being passed in the clear with every request.
This is an old question but something we ran into recently.
DRF disables CSRF by default, unless using session authentication. By default NSURLconnection is set up to handle cookies. You need to explicitly tell the iOS app to not use cookies. Then you can keep using session auth if needed and not have to csrf exempt your views.
urlpatterns = patterns('',
url('^login/$', csrf_exempt(views.LoginView.as_view())),
...
)
Guys. I had the same error and spent a lot of time just for finding that:
1) I had another router with 'login' and there i missed '$'. I mean sometimes you can forget something in routing and get this error.
In my case it happend because I sent put request to url='http://example.com/list/5' without slash at the end. When I changed url to url='http://example.com/list/5/' all started to work.
Currently I have a Django project, let's call it Backend. There is a folder api and there I have this resource declared using Django-Tastypie:
from django.contrib.auth.models import User
from tastypie.resources import ModelResource, ALL
from tastypie.authentication import BasicAuthentication
from tastypie.authorization import DjangoAuthorization
from tastypie.cache import SimpleCache
from tastypie.throttle import CacheDBThrottle
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
excludes = ['email', 'password', 'is_staff', 'is_superuser']
authentication = BasicAuthentication()
authorization = DjangoAuthorization()
cache = SimpleCache()
throttle = CacheDBThrottle()
filtering = {
'username' : ALL,
'date_joined' : ['gt','lt','gte','lte','range']
}
With proper routing rules in place, if I access an URL like http://127.0.0.1:8000/api/v1/user?format=json, I am supposed to get back some info on users in json format, but from my local database. I do not want to use any local database, except maybe for testing with some dummy data. I want such a get request to result in a SOAP request to a certain remote server with my logged in session's username and password.
I already have a standalone Python application, where I can perform a SOAP request and get a SOAP response back using SUDS and pre-downloaded WSDL files. Now I want to include this functionality in my Dhango project in such a way, that I change the settings in settings.py in the project, but I do not have to modify the applications inside the project.
You can define your own Custom managers that use your standalone Python application.
From tastypie documentation. Adding this code to your urls.py should work:
from tastypie.api import Api
from my_app.api.resources import UserResource
v1_api = Api(api_name='v1')
v1_api.register(UserResource())
#old urlpatterns here
urlpatterns += patterns('',
(r'^api/', include(v1_api.urls)),
)