How to change DRF to use a Response from a schema? - django

Is it possible to change the Django Rest Framework to use a Response from a schema?
For example, a GET using the standard DRF will output:
{
"count": 0,
"next": null,
"previous": null,
"results": []
}
Where I would like it to output
{
"meta_stuff": {
"License": "MIT"
},
"data": []
}
Where results=data and an extra ). Would I need to customise DRF or can a swagger.JSON schema be used to achieve this?

I suppose you're talking about pagination style. If so you should look at this link: http://www.django-rest-framework.org/api-guide/pagination/#custom-pagination-styles.
Basically you're going to write your own custom Pagination class, customizing DRF's behavior. Documentation's example is exactly what you've described.

Related

How do I add the `fields` array parameter when making requests to Patreon API using Postman?

I'm currently learning how to use the Patreon API. Before I integrate it into my site, I want to test the endpoints using POSTMAN. For example, I want to test the /campaign endpoint based on this documentation.
However, I'm confused how to set the parameter
fields[campaign]=created_at,creation_name
I put it in the body > x-www-form-urlencoded but it's not getting displayed in the atributes.
What is the correct way to set it?
Here is my screenshot of Postman:
Based on the documentation, the attributes in the response should have this information:
{
"data":
{
"attributes": {
"created_at": "2018-04-01T15:27:11+00:00",
"creation_name": "online communities",
"discord_server_id": "1234567890",
"image_small_url": "https://example.url",
"image_url": "https://example.url",
"is_charged_immediately": false,
"is_monthly": true,
"main_video_embed": null,
"main_video_url": null,
"one_liner": null,
"patron_count": 1000,
"pay_per_name": "month",
"pledge_url": "/bePatron?c=12345",
"published_at": "2018-04-01T18:15:34+00:00",
"summary": "The most creator-first API",
"thanks_embed": "",
"thanks_msg": null,
"thanks_video_url": null,
},
"id": "12345",
"type": "campaign"
},
From the API documentation
GET /api/oauth2/v2/campaigns/{campaign_id}
[ and ] needs to URL encode
Fields for each include must be explicitly requested i.e. fields[campaign]=created_at,creation_name but url encode the brackets i.e.fields%5Bcampaign%5D=created_at,creation_name
So you needs to change the Query Params KEY but
VALUE keep the same format field , field
From
fields[Bcampaign]
To
fields%5Bcampaign%5D

How the users can access my Elasticsearch database in my Django SaaS?

Let's say that I have a SaaS based on Django backend that processes the data of the users and write everything to the Elasticsearch. Now I would like to give users access to search and request their data stored in ES using all possible search requests available in ES. Obviously the user should have only access to his data, not to other user's data. I am aware that it can be done in a lot of different ways but I wonder what is safe and the best solution? At this point I store everything in one index and type in the way shown below but I can do this in any way.
"_index": "example_index",
"_type": "example_type",
"_id": "H2s-lGsdshEzmewdKtL",
"_score": 1,
"_source": {
"user_id": 1,
"field1": "example1",
"field2": "example2",
"field3": "example3"
}
I think that the best way would be to associate every document with the user_id. The user would send for example GET request with body and authorization header with Token. I would use Token to extract id of the user for example in this way
key = request.META.get('HTTP_AUTHORIZATION').split()[1]
user_id = Token.objects.get(key=key).user_id
After this I would redirect his request to ES and only data that meet requirements and belongs to this user would be returned. Of course I could do this like shown above where I also add field user_id. For example I could use post_filter in this way:
To every request I would add something like this:
,
"post_filter": {
"match": {
"user_id": 1
}
}
For example the user sends GET with body
{
"query": {
"regexp": {
"tag": ".*example.*"
}
}
}
and I change this in my backend and redirect request to ES with body:
{
"query": {
"regexp": {
"tag": ".*example.*"
}
},
"post_filter": {
"match": {
"user_id": 1
}
}
}
but it doesn't seem to me that including this field in _source is a good idea. I am almost sure that it can be solved in a more optimal way than post_filtering. I see a lot of information about authorization in ES however I can’t find how can I associate document with user_id and then search only his documents without post_filtering. Any ideas?
UPDATE
My current solution looks in they way shown below however as I mentioned I believe that it is not optimal way. If anyone has an idea how can I solve this in the way described above I will be grateful for help.
I send for example
{
"query": {
"regexp": {
"tag": ".*test.*"
}
}
}
In Django backend I just do
key = request.META.get('HTTP_AUTHORIZATION').split()[1]
user_id = Token.objects.get(key=key).user_id
body = json.loads(request.body)
body['post_filter'] = {"match": {"user_id": user_id}}
res = es.search(index="pictures", doc_type="picture", body=body)
output = []
for hit in res['hits']['hits']:
output.append(hit["_source"])
return Response(
{'output': output},
status=status.HTTP_200_OK)
In elasticsearch 7.1, you have now basic security in the free version of elasticsearch. Thanks to that, you can control per indice thé Access of your user.

How to set variable Request format in Amazon Api Gateway?

I want to make a Model for request where some part of the request structure may change.
As i don't have uniform structure here. How can i define json model for Amazon Api Gateway?
Request:
Here data inside items.{index}.data is changing according to type_id. Also we are not sure about which item with perticular type_id come at which {index}. even the type of items.{index}.data may change.
{
"name":"Jon Doe",
"items": [
{
"type_id":2,
"data": {
"km": 10,
"fuel": 20
}
},
{
"type_id": 5,
"data": [
[
"id":1,
"value":2
],
.....
]
},{
"type_id": 3,
"data": "data goes here"
},
....
]
}
How should i do this?
API Gateway uses JSON schema for model definitions. You can use a union datatype to represent your data object. See this question for an example of such a datatype.
Please note that a data model such as this will pose problems for generating SDKs. If you need SDK support for strictly typed languages, you may want to reconsider this data model.

Can I define a custom validation with options for Loopback?

Is there a prescribed way to create a custom validator in loopback? As an example, assume that I want to create something like:
Validatable.validatesRange('aProperty', {min: 0, max: 1000})
Please note that I am aware of:
Validatable.validates(propertyName, validFn, options)
The problem I have with validates() is that validFn does not have access to the options. So, I'm forced to hard code this logic; and create a custom method for every property that needs this type of validation. This is undesirable.
Similarly, I am familiar with:
Model.observes('before save', hookFn)
Unfortunately, I see no way to even declare options for the hookFn(). I don't have this specific need (at least, not yet). It was just an avenue I explored as a possible alternative to solve my problem.
Any advice is appreciated. Thanks in advance!
There is a mention of how to do this over at https://docs.strongloop.com/display/public/LB/Validating+model+data
You can also call validate() or validateAsync() with custom validation
functions.
That leads you to this page https://apidocs.strongloop.com/loopback-datasource-juggler/#validatable-validate
Which provides an example.
I tried it out on my own ...
Question.validate('points', customValidator, {message: 'Negative Points'});
function customValidator(err) {
if (this.points <0) err();
}
And since that function name isn't really used anywhere else and (in this case) the function is short, I also tried it out with anonymous function:
Question.validate('points',
function (err) { if (this.points <0) err(); },
{message: 'Question has a negative value'})
When points are less than zero, it throws the validation error shown below.
{
"error": {
"name": "ValidationError",
"status": 422,
"message": "The `Question` instance is not valid. Details: `points` Negative Points (value: -100).",
"statusCode": 422,
"details": {
"context": "Question",
"codes": {
"points": [
"custom"
]
},
"messages": {
"points": [
"Negative Points"
]
}
What you are looking for is validatesLengthOf(). For example:
Validatable.validatesLengthOf('aProperty', {min: 0, max: 1000});
Here is the documentation links:
All the methods of Validatable class and
Model-wise validation.

Tastypie Adding Custom Values (conditionally)

I have a Complex Django model with (1) alot of fields and (2) dynamically generated data for this model that is not stored on the model.
Comlex Resource:
class ComplexResource(resource):
class Meta:
allowed_methods = ('get','put','post','delete','patch')
queryset = Complex.objects.all()
serializer = CustomSerializer()
authorization = Authorization()
authentication = Authentication()
always_return_data = True
filtering = { "field_1" : ALL, "field_2" : ALL, "field_N" : ALL }
ordering = ["field_1", "field_2", "field_n"]
Currently, my Complex endpoint /m/api/v1/complex/ successfully returns STORED data on the model.
Complex Response:
{ "meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 1
},
"objects": [
{"field_1": "foo", "field_2": "bar", ... , "field_n": "foobar"}
]
}
Problem:
What I would like to do is to use prepend_urls to add a custom endpoint which will return all of the data. This way I wouldn't have to use dehydrate wastefully adding data every time I called my current endpoint which can be seen here:
http://django-tastypie.readthedocs.org/en/latest/cookbook.html#adding-custom-values
Question
How do I go about creating a prepend_url which injects additional data into a resource? or is there a better design pattern for solving this altogether?
Complex Response w/all data: /m/api/v1/complex/all/
{ "meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 1
},
"objects": [
{"field_1": "foo",
"field_2": "bar",
.
.
.
"field_n": "foobar",
"advanced_field_1 : "a1",
"advanced_field_2 : "a2",
.
.
.
"advanced_field_n : "an",
}
]
}
I think dehydrate was made for cases like yours. Add query set argument to determine that dehydrate should compute addition fields or not.
def dehydrate(self, bundle):
if bundle.request.GET.get('attach_dynamic_fields'):
bundle.data['my_dynamic_field_1'] = make_dynamic_field(1)
bundle.data['my_dynamic_field_2'] = make_dynamic_field(2)
return bundle
Then just use when needed:
/api/v1/complex/ # Gives all, no dynamic fields attached
/api/v1/complex/1/ # Gives one, no dynamic fields attached
/api/v1/complex/?attach_dynamic_fields=1 # Gives all, with dynamic fields
/api/v1/complex/1/?attach_dynamic_fields=1 # Gives one, with dynamic fields
Creating prepend_url doesn't make sense for me. Because it means rewriting Tastypie again. Which is sign that something here is not RESTful.
If you want to do it anyway you should take a look on dispatch method and its flow:
Flow through request / response cycle
You will have to rewrite this: dispatch method in your prepend_url somehow.