I'm trying to create a pydantic BaseModel that will be able to map some data from the request body and also from the request.state.
How can this be accomplished?
Request metadata shouldn't be part of your model - the model should only be concerned with the actual request (i.e. the data submitted by the user).
To access metadata about the request, add a Request object to your view definition:
def my_method(item: Item, request: Request):
This will magically give you all the metadata (including the .state entry) under the request variable in your view function.
Related
Client might send multiple query params like:
# method = POST
http://api.com?x=foo&y=bar
I need to get all the query params and extract it as a string x=foo&y=bar from the POST request.
Is there a way to do this on fast api? I could not find it on their docs.
NOTE :
We are not sure of the names of the params before hand.
Depending how you want to use the data, you can either use request.query_params to retrieve a immutable dict with the parameters, or you can use request.url to get an object representing the actual URL. This object has a query property that you can use to get the raw string:
from fastapi import FastAPI, Request
import uvicorn
app = FastAPI()
#app.get('/')
def main(request: Request):
return request.url.query
uvicorn.run(app)
This returns the given query string /?foo=123 as foo=123 from the API. There is no difference between a POST or GET request for this functionality.
I am building on Google App Engine + Python + webapp2. Part of building a modern web app requires a restful API. I know that I can do this with Flask, however I want to explore the possibility of building a REST API on webapp2.
On webapp2, requests are handled like this:
app = webapp2.WSGIApplication([
('/post/new', CreatePost),
('/post/([a-z0-9]+)', ViewPost),
('/post/([a-z0-9]+)/edit', EditPost),
('/post/([a-z0-9]+)/delete', DeletePost)
])
Note: ([a-z0-9]+) is a regex that represents the post_id
The above request handlers do not adhere to a RESTful pattern since the request methods are specified in the path (/delete, /edit, /new) rather than in the request headers.
Is the solution to create a single handler class that receives all request types? For example:
class PostHandler(webapp2.RequestHandler):
def get(self):
# handle GET requests
def post(self):
# handle POST requests
def put(self):
# handle PUT requests
def delete(self):
# handle DELETE requests
app = webapp2.WSGIApplication([
('/post/?', PostHandler)
])
In this case, all /post paths are handled by PostHandler. The post_id is no longer used in this pattern since it would be submitted in the request body instead.
Is this the correct approach to building a REST API with webapp2?
You are on the right path, but you should continue to handle post_id in the url and do it like this:
class PostHandler(webapp2.RequestHandler):
def get(self, post_id=None):
if post_id:
# handle Fetching a single post object
else:
# handle Queries
def post(self, post_id=None):
if post_id:
self.abort(405)
# handle creating a single post object
def put(self, post_id=None):
if post_id:
# handle updating a single post object
else:
self.abort(405)
def delete(self, post_id=None):
if post_id:
# handle deleting a single post object
else:
self.abort(405)
app = webapp2.WSGIApplication([
('/post/<post_id>/', PostHandler),
('/post/', PostHandler),
])
Furthermore putting the HTTP verb within the request payload like voscausa suggested is not inline with RESTful API design.
One should extend webapp2.RequestHandler and use the extension as the new base class for a RESTfulHandler and then maybe also for your specific purpose extend the RESTfulHandler to a RESTfulPostHandler. This way we could also deal with new ways or work such as jwt (JSON web token) in the header, authorization and other properties that you wish to handle in the header.
BTW, "post" might not be the best name for your item/object since it could easily get mixed up with the http verb "post". If I were you, I would rename it into "item" or similar, and use a RESTfulItemHandler which extends the webapp2 request handler. There are many things you would like to share between your handler and like that it is convenient to share the functionality in a base-class.
Whenever we test an API in Django Rest Framework using API Request Factory why do we call the view when we are already passing the url .
Look at the following code for better understanding .
request = self.factory.post("/api/v1/menu/", data)
views = MenuResource.as_view({"post": "post"})
response = views(request)
response.render()
self.assertEqual(201, response.status_code)
In the above code we are calling the url as well as calling the view . View is being called for rendering the view on the url but that is not what my use case is . I just want to test the response code . Is there a way of getting the response code without rendering the view as that is an over kill for my use case .
I have looked for other methods except for using API Request Factory but i just wanted to know why does API Request Factory need to call the view . Is there any advantage in comparison to other API Testing modules present in Django Rest Framework.
That is because you are not making a request to the URL.
request = self.factory.post("/api/v1/menu/", data) simply creates a request object for you to use it does not actually make the request.
So testing the view in your case consists of creating a request object and passing it to the view which mimics the way an actual request would be passed and processed by that view.
If you want to send actual request you need to mock a test server which will handle the actual requests.
I'm constructing a custom Prometheus middleware class to do some monitoring and I'd like to either construct or retrieve the Django Rest Framework Request object for a given request in middleware so that I can capture request.version in my metrics.
However, the request parameter in my code below appears to be a WSGIRequest object. Is it possible to discern the DRF HttpRequest object within middleware? Code Below...
if django.VERSION >= (1, 10, 0):
from django.utils.deprecation import MiddlewareMixin
else:
MiddlewareMixin = object
class CustomPrometheusAfterMiddleware(MiddlewareMixin):
def process_response(self, request, response):
# Do some cool things
return response
If you check the source code, the Django's HttpRequest object is wrapped in DRF's Request object in views.APIView.dispatch so I think you will have to go thru quite a few hoops to get it. Your best bet might be to patch APIView to store the infos you're after on the real request (Django's HttpRequest or subclass of) - or on the response FWIW - so you can get them back in the middleware.
I have a Django application which must have the following behavior: if a request has no Content-Type header, it returns an error response.
In order to test this behavior, I need to make an HTTP request without a Content-Type header.
I am using the Client class in the django.test module. This has many methods, including this one:
post(path, data=None, content_type=MULTIPART_CONTENT, follow=False, secure=False, **extra)
Makes a POST request on the provided path and returns a Response object, which is documented below.
[...]
If you provide content_type (e.g. text/xml for an XML payload), the contents of data will be sent as-is in the POST request, using content_type in the HTTP Content-Type header.
If you don’t provide a value for content_type, the values in data will be transmitted with a content type of multipart/form-data. In this case, the key-value pairs in data will be encoded as a multipart message and used to create the POST data payload.
The documentation says that a Content-Type header is always set on the request, irrespective of whether I pass a content_type argument.
So what other ways do I have to construct a request, such that it does not have a Content-Type header?
You can build a customised request instance through the class RequestFactory.
Once generated, you can modify the request instance before passing it to the view.
Using the example in the RequestFactory documentation page as a starting point, you can do:
from django.test import TestCase, RequestFactory
from .views import my_view
class SimpleTest(TestCase):
def setUp(self):
# Every test needs access to the request factory.
self.factory = RequestFactory()
def test_details(self):
# Create an instance of a POST request.
request = self.factory.post('/your/url', data={'your':'data'})
# NOW you can customise your request instance!
# (i.e. remove the Content-Type header)
request.META.pop('CONTENT_TYPE', None)
# Actually use the request to test my_view()
# as if it were deployed at /customer/details
response = my_view(request)
self.assertEqual(response.status_code, 400)
The request.META is just a standard Python dictionary (as explained here), so you can use
del request.META['CONTENT_TYPE']
instead of pop() to remove it, but only if you are deadly sure that the key will be in the dictionary.
I know this is several years old, but I had the same question and found the real answer, i.e. how to do this with the test client:
client.get(url, content_type=None)
At least on Django 2.0, that makes a request with no content type header.