I am using Django rest framework for writing some rest API in which I have two primary resources as Store and Product.
Now the URIs for these resources are like
List all the stores GET /stores/
List store with ID GET /stores/:id/
Add a new store POST /stores/
Update some attributes in a store PUT /stores/:id/
Same is applicable to products
List all the products GET /products/
List product with ID GET /products/:id/
Add a new product POST /products/
Update some attributes in a product PUT /products/:id/
Now i want to map some products to some stores like if store A is selling products with ID 1 to 100 and store B is selling products with ID 70 to 200.
With this i have got another resource as store_product_mappings
Now i can treat it as above and make URIs for this resource as
List all the store_product_mappings GET /store_product_mappings/
List store_product_mapping with ID GET /store_product_mappings/:id/
Add a new store_product_mapping POST /store_product_mappings/
Update some attributes in a store_product_mapping PUT /store_product_mappings/:id/
But now this mapping id isn't exposed to the consumers of this API. So in order to avoid this complexity i want to make a easilty understandable URI like
To list all the products inside a store GET /stores/:id/products/
To list a product inside a store GET /stores/:id/products/:product_id
To update a product inside a store PUT /stores/:id/products/:product_id
But according to this approach if i do post on this URI
POST /stores/:id/products/
It should create a new mapping resource and return the ID for that mapping resource, in the similar way it will expect the mapping id when i want to retrieve that resource back like
GET /stores/:id/products/:id
But i don't want to expose mapping ids as it's part of my internal system and consumers of API shouldn't need to worry about it. I am still looking for solutions for this kind of use case and resources in Django rest framework.
Let me know if someone has already faced the similar issue and with what approach they were able to implement this without violating the Rest API conventions.
This request:
POST /stores/:storeid/products/
can expose the product_id as part of the URI returned:
Location: /stores/{storeid}/products/{productid}
It does not need to tell you the technical id of the mapping row in your database. Remember, you don't have to expose database rows one-to-one in your REST API.
As a sidenote, if your clients are Hypertext driven (as they should be), they don't have to parse or construct URIs manually, therefore it would be not that important what you expose in the URIs.
Related
I would like to know if there is a way to update a ressource (product in this case), but only update fields that are specified in my request.
For example, let's say I want to update only the required fields for the product, but not change the others, how could I proceed via the webservice ?
As far as I know, when you update a ressource, it will update it as whole : This means if you don't "re-send" the original value back when updating (if you do not specify ALL the fields), it is considered as empty.
From my tests, I tried the following :
Log in my PrestaShop, get a random product, set the "width" to a random value (let's say 30 cm)
Retrieve the blank schema of a product, and fill only the required fields of the schema (width is not part of it)
Send the schema via the webservice to update the original product using a PUT request
Get back in my PrestaShop and notice in despair and sadness that the width value has been set to 0
My use case is that we have a system that is synching products with PrestaShop. When a product is edited in our system, a specific set of fields is sent back to PrestaShop so the product in the shop is also updated.
But for some of our users, they want to be able to add information on the shop and keep them even if the product is updated afterwards. For example, they add dimensions to the product (fields that are not managed / persisted in our system) and they want to keep those information.
The constraint we have is that the set of fields sent for updates is "hardcoded" : We can't get the ressource schema and update it to send it back afterwards.
Is there any parameter / configuration that can be set so values of fields that are not specified are not erased ?
Due to the nature of how the PrestaShop API works, you can perform the following:
For each product you want to update, you can pull the complete object (with all fields), then update that object with only the fields you need updated, and then push the whole object as n update for that product via the API.
Doing this for multiple products will require twice the API calls.. but this is one workaround.
One way is to use a flag variable for the properties which you want to update
I am looking to have additional data added onto my models after the JSON data is returned from the service. The service I talk to returns information as a code, but I want to also include a more readable name to display to users. This would be done almost everywhere the model is used.
Example:
Fetch from the service
{schedule: {code:'MONTHLY'}}
Have access to
{schedule: {code:'MONTHLY', name: 'Monthly'}}
This would be for things which have a map of code to name, where name only ever lives on the front end, and code is what is persisted. I see there is a concept of custom transforms, would this be the way to go?
I also plan to keep a mapping of all possible codes/names in another file, to iterate over or compare to the model's attributes. For instance I would want to present users with a choice of schedules to choose from, MONTHLY, QUARTERLY, or ANNUALLY.
You should create a Computed Property on a model. You can call it: "name" or "displayName". It should depend on "code" attribute on model. Inside computed property, you should access a service. Service should have a method to map code -> name. A method you call from the model on service should return you a name. A code-name map should be separated from the model.
Whenever you want to access displayName Computed Property from model use model.displayName.
I don't fully understand how to make complex queries with REST API on the backend, and a backbone web app on the frontend. Say I have a user table that has a relation with a user_group table:
user.group_ref => user_group.id
If I do a GET on /api/v1/user/1/?format=json it will do something like SELECT * FROM users WHERE id = 1. Right, ok, now... how about if I wanted to JOIN with my user_group where user.group_ref = user_group.id to instantly have access to the data on user_group. I don't want to do an extra query to go and fetch that data.
Maybe I got the whole idea wrong ... Is there a simpler way?
REST is a set of conventions. It doesn't provide for automatic query mapping, so you need to define a service endpoint, and then implement it to return whatever you want it to.
In your case a typical way of composing the URL would be something like:
GET /groups/(groupid)/users
Which is to say "give me all users belonging to this group". Alternatively:
GET /users?group=(groupid)
Which in style is less "RESTful", but doesn't unnecessarily promote group as a top-level resource.
Either way, Backbone doesn't provide an OOTB way for populating collections from more complex resources. For anything beyond simple CRUD you'll have to implement the service call yourself, or create a separate read-only collection with url that maps to your service. Something like:
var UserGroupCollection = Backbone.Collection.extend({
url: function() { return "groups/" + this.options.groupId + "/users"; }
});
var group = new UserGroupCollection({groupId:1});
group.fetch();
The REST API can be implemented separately, without any relationship to the front end, it can be designed as you need.
In your case, if you are always going to get the user_group.id with the query for a user, then you should change your SQL to JOIN statements permanently: SELECT * FROM users WHERE id = 1 JOIN user_group WHERE user.group_ref=user_group.id.
If you need both queries with and without user_group info. You can design two REST methods, such as (GET /api/v1/user/1?format=json) for without group info, and (GET /api/v1/userwithgroup/1?format=json) for ones with group info.
And on the Backbone, you could have two different Models representing the two.
If you don't have millions of users in the table, the SQL with group info should be quite fast and it would be easier to just always have group info with it.
There is a python library called slubmber (http://slumber.in/) which is built on top of requests for the explicit purpose of REST APIs.
I'm using Django-Tastypie to provide a REST-API for my web application.
In this case I have a PhotoGallery object, which references to a couple of Photos using a intermediate table (using through on the ManyToMany field).
Unfortunately I have some trouble saving the PhotoGallery object through the REST interface, as it would require me to create the respective links in the intermediate table (which contain a bit of additional information like a sort index, etc.).
To simplify the API, I decided to expose the intermediate table as well, and now users can create and modify the relation between photos and the galleries.
To prevent the user of the REST-API having to delete each link with a single HTTP request, I'm wondering if it is possible to use a single HTTP DELETE request to delete a selected number of objects. Filtering does not seem to be respected with DELETE requests.
Have you considered the PATCH method? Take a look the Bulk Operations section in the docs.
Pay attention to the "deleted_objects": ... part.
I want to implement restful web-services to query my database.
In Netbeans I did this:
- I created entity classes from my db
- I generated web-services from these entity classes
GET methods work fine when testing but I have some additional requirements. I dont want to query only by tables' id-s. Data needs to be retrieved also when some other parametres are entered.
For example I have a table:
Customer: id, name, address, country
Now I want to display all customers from a specific country.
Where in code can I achieve that?
You can do this with slightly different urls.
So for a single customer you'd present a URL such as:
GET /customer/123.html
But for multiple customers, you'd figure out a way to specify groups. If you wanted all customers, you'd go for:
GET /customers.html
But say you grouped by country, you could try:
GET /customers/Australia.html
Using the singular or plural form would separate the two types of get-requests.