django rest framework post method not allowed - http

I am creating an api and no idea why post method not allowed on any url.
views
class MessagesView(APIView):
permission_classes = (IsAuthenticated,)
def post(self, request):
serializer = MessageSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
chat.urls
urlpatterns = [
path("<str:pk>/", ChatDetail.as_view()),
path("messages/", MessagesView.as_view()),
]
response
{
"detail": "Method \"POST\" not allowed."
}
I am providing the token for the request, so isAuthenticated does not do anything wrong here.

Your first pattern will fire if you visit messages/. Indeed, its <str:pk> parameter matches any string (with at least one character and without slashes). But messages is thus also matched with this view.
What you can do is swap the places of the two urls, then calling messages/ will fire the correct view:
urlpatterns = [
# &downarrow; messages first
path('messages/', MessagesView.as_view()),
path('<str:pk>/', ChatDetail.as_view()),
]
If pk is an integer, you can further restrict the pk with the <int:…> path converter:
urlpatterns = [
path('messages/', MessagesView.as_view()),
path('<int:pk>/', ChatDetail.as_view()),
]

Related

FastAPI pydantic response_model

FastAPI with uvicorn. So far i have everything working fine, now wanted to add extra validation with pydantic and response_model validation. My challange is, the response is a nested object, example:
{
"name": "always the same",
"value": ["ACL1", "ACL2", "ACL3"]
}
I have tried to:
class ACL(BaseModel):
name: str
#app.post(
"get/acls",
response_model=List[ACL]
)
def get_acls(credentials: HTTPBasicCredentials = Depends(security)):
But obviously it will fail. How to define correctly the response model so that it's correctly validated ? (and also visible in openapi documentation page ?)
Thanks,
You can use nested models in Pydantic and by by extension also in FastAPI. Like so:
class ACL(BaseModel):
name: str
class YourResponseModel(BaseModel):
name: str
value: list[ACL]
#app.post("/get/acls", response_model=YourResponseModel)
def get_acl():
return {"name": "always the same", "value": [{"name":"ACL1"}, {"name":"ACL2"}, {"name":"ACL3"}]}
If you don't want nested objects in your value list, then you have to build some logic to parse it. Below is an example of this:
class ACL(BaseModel):
name: str
class AnotherResponseModel(BaseModel):
name: str
value: list[str]
#app.post("/get/acls2", response_model=AnotherResponseModel)
def get_acl():
acls = [ACL(name="ACL1"), ACL(name="ACL2"), ACL(name="ACL3")]
return {"name": "always the same", "value": [acl.name for acl in acls] }
Please note, it is bad practice to mix up POST and GET requests (you are defining a POST endpoint, that will 'get' some ACLs for the end user). That is however unrelated to this question.

How to test pact with null data

I am aware of that PACT expects provider data need to be in our control, but I am facing following situation
I have pact contract for multiple consumers, all have some mandatory attribute and some are optional attribute, but business logic suppress all the attribute which are having null value, but according to contract I would still be needing that value as null,
what should I do for it?
Edit 1:
i.e let's say below my contract looks
consumer sent request with below params:
{ "method": "GET", "path" : "/pathOfApi", "headers":{ "accept": "json" } }
Provider responds with below data:
{ "Status": 200,
"body" :[
{"country" : "string",
"countryId" :"string",
"postalcode": "string",
"addressLine1" :"string",
"addressLine2" : "string"
"customerName" : "string",
"customerId" : "string"
}
]
now not all customer has address line 2, now in production if addressLine 2 is null it won't be present in output of api, but for our contract field should be present with null
If your provider does not return a field, but the consumer is expecting null, then either the consumer needs to change their expectation (because it's incorrect) or the provider should update its implementation to return null values.
Just because a consumer asks for something doesn't mean you need to do it!
If in some instances the field is present and other it is not, you need to write two tests to cover each case. I'd suggest covering one case with all of the fields, and another with the minimum set of fields (see https://docs.pact.io/faq/#why-is-there-no-support-for-specifying-optional-attributes).

No route matches {:action=>"show", :controller=>"products"}, missing required keys: [:id]

I have a rSpec Controller test which fails
the index route will have an :id which does not exist in an index route.
I've clean routes:
resources :products
This is the controller,
class ProductsController < ApplicationController
before_action :set_product, only: [:show]
skip_before_action :authorize_request, only: [:show, :index]
# GET /products
def index
# params here {"controller"=>"products", "action"=>"index", "product"=>{}}
#products = Product.all
render json: #products
end
ActionController::UrlGenerationError: No route matches {
:action=>"show",
:controller=>"products"},
missing required keys: [:id]
Why is "show" called? I did not passed any params to the controller:
This is the spec:
RSpec.describe "/products", type: :request do
describe " GET /index" do
it " renders a successful response " do
Fabricate.times(3, :product)
get "/products", headers: valid_headers
expect(response).to be_successful
end
end
end
When I change the routes from get "/products", to: 'products#index' and comment out resources :products then it passes the test
EDIT / PROBLEM FOUND :
I use include Rails.application.routes.url_helpers in my Product model, which caused this issue. I need it to generate URLs to retrieve my attachments. How else can I get the URLs of ActiveStorage ?
I solved the problem: I had include Rails.application.routes.url_helpers in my Product model which caused the problem:
class Product < ApplicationRecord
# include Rails.application.routes.url_helpers
end
commented out, all specs are passing now
But I still don't understand why I can't use it in my Model.
I wanna have it included to retrieve urls of my attachments:
def main_image_thumb_url
rails_blob_url(main_image, host: "localhost:3000")
end

Extjs 4 - Retrieve data in json format and load a Store. It sends OPTION request

I'm developing an app with Spring MVC and the view in extjs 4. At this point, i have to create a Grid which shows a list of users.
In my Spring MVC controller i have a Get method which returns the list of users in a jsonformat with "items" as a root.
#RequestMapping(method=RequestMethod.GET, value="/getUsers")
public #ResponseBody Users getUsersInJSON(){
Users users = new Users();
users.setItems(userService.getUsers());
return users;
}
If i try to access it with the browser i can see the jsondata correctly.
{"items":[{"username":"name1",".....
But my problem is relative to request of the Ext.data.Store
My Script is the following:
Ext.onReady(function(){
Ext.define('UsersList', {
extend: 'Ext.data.Model',
fields: [
{name:'username', type:'string'},
{name:'firstname', type:'string'}
]
});
var store = Ext.create('Ext.data.Store', {
storeId: 'users',
model: 'UsersList',
autoLoad: 'true',
proxy: {
type: 'ajax',
url : 'http://localhost:8080/MyApp/getUsers.html',
reader: {type: 'json', root: 'items'}
}
});
Ext.create('Ext.grid.Panel',{
store :store,
id : 'user',
title: 'Users',
columns : [
{header : 'Username', dataIndex : 'username'},
{header : 'Firstname', dataIndex: 'firstname'}
],
height :300,
width: 400,
renderTo:'center'
});
});
When the store tries to retrieve the data and launchs the http request, in my firebug console appears OPTIONS getUsers.html while the request in the browser launchs GET getUsers.html
As a result, Ext.data.Store has not elements and the grid appears with the columnames but without data. Maybe i've missed something
Thank you
You can change the HTTP methods that are used by the proxy for the different CRUD operations using actionMethods.
But, as you can see in the doc (and as should obviously be the case), GET is the default for read operations. So the OPTIONS request you are observing is quite puzzling. Are you sure that there's not another part of your code that overrides the default application-wide? Maybe do a search for 'OPTIONS' in all your project's JS files, to try and find a possible suspect. Apparently there's no match in the whole Ext code, so that probably doesn't come from the framework.
Edit:
Ok, I think I've got it. If your page is not accessed from the same domain (i.e. localhost:8080, the port is taken into account), the XHR object seems to resort to an OPTIONS request.
So, to fix your problem, either omit the domain name completely, using:
url: '/MyApp/getUsers.html'
Or double check that your using the same domain and port to access the page and make the requests.

Specifying a root node of FormPanel.form.load for dynamic forms

I'm building a dynamic ExtJS form based on JSON data loaded from an ASP.NET web service. The problem I find is that ExtJS expects the JSON in a specific format, ie.
{ "metaData": { "title": "Testing" }, "data": [], "success": true }
When using an ASP.NET web service to return an object as JSON it returns with the first element "d", ie.
{ "d": { "metaData": { "title": "Testing" }, "data": [], "success": true } }
Is it possible to tell the ExtJS form to use "d" as the root node?
After some more testing I've found that the ExtJS form does load the JSON from my web service but because the response text doesn't have "success": true in the root it is handled by the 'failed' handler. Fortunately this handler accepts the same parameters as the 'success' handler so can be manipulated the same.
Here's an example of my form load handler:
this.form.load({
url: "myservice.asmx/getUser",
headers: {'Content-Type': 'application/json'},
success: function(form, action) {
//not fired
},
failure: function(form, action){
if (action.result.d){
//process data
}
}
});
You don't have to call form.load() of course. I bypass it, and simply call my ASMX web method directly by calling the AJAX function that links to my webmethod, as provided by the ScriptManager. MS AJAX does all the JSON decoding, and factors out the 'd' property, etc.
Your web method doesn't even have to return an object with the 'success' and 'data' objects, as required by form.load(), although it's a useful format and I stick to it.
With the 'data' object returned by the web method (with name/value pairs, where name == field name) you can now call ExtJs's form.setValues(data); to write the values into the fields.
This is a perfectly valid case for bypassing ExtJS's code.
--
As with loading, so with submitting. To get around the 'd' property problem in the object that has to be returned by the submission web method, handle the click event of the Submit button, and push the data to the server by directly calling your web method. Your web method can/should return an object in the same format as is required by ExtJs. But you get back this object, and if not successful, call form.markInvalid() yourself, passing in the 'errors' property. Peasy easy and works well.
Again, since ExtJs doesn't play nice with the 'd' property it's perfectly valid to bypass it and do things yourself.
--
I tend to use the ScriptManager-provided functions for calling my web methods more and more, and bypass ExtJs's AJAX method invoking code. The former are much simpler to use, know about the 'd' property and also know how to deserialize Microsoft's JSON format for serialized DateTime objects.

Resources