Return custom payload from default response - django

In Django 2.0 I am using the rest_auth and currently it returns a response like
{
"token": "foo_token",
"user":{
"pk": 1,
"username": "admin",
"email": "test#test.com",
"first_name": "John",
"last_name": "Doe"
}
}
I would like to change this to return something besides the default response django provides. Something like...
{
"token": "foo_token",
"pk":1,
"username": "admin",
"somefield": "Foo Funk"
}
My urls.py look like this currently
url(r'^rest-auth/registration/', include('rest_auth.registration.urls')),
url(r'^refresh-token/', refresh_jwt_token),
url(r'^api/userlist', users.user_list),
The only place I can find to possibly change the response is in library files which I am sure is not wise to change. Any help would be great.

rest_auth allows you to change the responses by specifying your own serializer implementation in your settings.py.
For example if you wanted to customize the response for JWT authentication, then you might create:
# myapp/serializers.py
class MyCustomJWTSerializer(serializers.Serializer):
token = serializers.CharField()
pk = serializers.IntegerField()
username = serializers.CharField()
...
which you can then configure in your settings.py as:
REST_AUTH_SERIALIZERS = {
'JWT_SERIALIZER': 'myapp.serializers.MyCustomJWTSerializer'
}
More info here: https://django-rest-auth.readthedocs.io/en/latest/configuration.html

Related

FastAPI - return Single element in from Database

I have 2 api endpoints. This one returns a list of all my users.
#app.get("/entity/", response_model=List[schemas.Userentity])
def read_entities(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_entities(db, skip=skip, limit=limit)
return users
def get_entities(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Userentity).offset(skip).limit(limit).all()
This works fine. See the data:
[
{
"id": "89f8a844-a30e-41b2-947d-6eb0fcadb1bf",
"email": "xxx#hotmail.com",
"first_name": "xxx",
"last_name": "xxx",
"username": "schoedeld",
"created_timestamp": "1633711745164"
},
{
"id": "abd6bb6b-ad80-431c-9f64-d9e651638f4c",
"email": "xxxsd#hotmail.com",
"first_name": "xxx",
"last_name": "xxx",
"username": "xxx",
"created_timestamp": "1633711733338"
},
{
"id": "ad0bd5ca-5aed-4d7f-a6ac-2620f2133685",
"email": "xxxx#hotmail.com",
"first_name": "fsdfs",
"last_name": "fsdfs",
"username": "testadmin",
"created_timestamp": "1633710812666"
}
]
Here I have an endpoint to return a single user:
I could return a List with a single element, but I would like to return a single value, so I updated my endpoint to this:
#app.get("/entity/{id}", response_model=schemas.Userentity)
def read_entity(id: str, db: Session = Depends(get_db)):
user = crud.get_entity(db, id=id)
return user
def get_entity(db: Session, id: str):
return db.query(models.Userentity).filter_by(id=id)
This results in the following error:
pydantic.error_wrappers.ValidationError: 6 validation errors for Userentity
response -> id
field required (type=value_error.missing)
response -> email
field required (type=value_error.missing)
response -> first_name
field required (type=value_error.missing)
response -> last_name
field required (type=value_error.missing)
response -> username
field required (type=value_error.missing)
response -> created_timestamp
field required (type=value_error.missing)
I am pretty new to FastAPI and do not understand why this error happens, can anyone help me and explain what I am doing wrong here?
You're never actually running your SQLAlchemy query, so you're returning the query itself:
return db.query(models.Userentity).filter_by(id=id)
Instead you should make SQLAlchemy run your query by either:
# raise an exception if not exactly one row is returned (so more than one or zero)
return db.query(models.Userentity).filter_by(id=id).one()
# Either one or zero rows - return `None` if user wasn't found
return db.query(models.Userentity).filter_by(id=id).one_or_none()
# Return None if user isn't found, otherwise return the first row
# (multiple rows isn't an error)
return db.query(models.Userentity).filter_by(id=id).first()
Next time attach set debugger breakpoint at the return call from your view function (or at least print the value), and you can see exactly what you're trying to return to FastAPI for serialization.

Django RestFramework Elastic Search: Timeline API

I'm using django restframework along with elastic search to develop a backend application for a mobile app. I need to develop a timeline API that will load a timeline of posts from other users the user is following. Along with other related poststhat the people they're following may comment.
What is the best implementation method for this problem?
like this
#some_vf
def someview(req, **kw):
query = {
"_source": ["field1", "field2", "field3"],
"query": {
"must": [
{"term": {"username": "zhangsan"}}
],
"should": [
{"term": {"userid": 1}},
{"term": {"followed": "something"}} # other filter with zhangsan
]
},
"sort": [{"timestamp": {"order": "asc"}}],
"size": size,
"from": from_
}
es = Elasticsearch()
data = elasticsearch.helpers.scan(es, ["user_docs_index", "related_posts_index", "followed_some_index"], "_doc")
# or do your serializers
return Response(data)

Django DRF: Schema for bulk creation api

I'm using django-rest-framework to build my API in which supports bulk create/update.
In these cases, the api will accept a list of object like
[
{"foo":"bar"},
{"foo":"bar"}
]
The code I'm using to allow bulk apis is just a small modification to add option many=True for serializer if the data is a list. It's like:
class FooViewSet(views.ModelViewSet):
def create(self, request, *args, **kwargs):
many = isinstance(request.data, list)
if many:
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
self.perform_bulk_create(serializer)
else:
................
I'm using drf_yasg for api doc generation.
But the problem is the schema generated keep detecting my request body just the single model only. Is there any config to make DRF schema generator knows that it will accept a list type?
Here is the schema which DRF generated
{
"post": {
"operationId": "foos_create",
"description": "",
"parameters": [
{
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Foo"
}
}
],
"responses": {
"201": {
"description": "",
"schema": {
"$ref": "#/definitions/Foo"
}
}
},
"tags": [
"foos"
]
}
}
My expectation is the schema would be the array type of Foo definition
Any help will be appreciated. Thanks for your time.
I know it very old post but I was facing a similar issue, and as a noob in DRF and python took a while to figure this stuff out.
I just had to add a simple decorator.
FYI I have used https://github.com/miki725/django-rest-framework-bulk for the bulk update.
#method_decorator(name='perform_bulk_create', decorator=swagger_auto_schema(
request_body=ContactSerializer(many=True),
operation_description="post list of contacts"
))

django test - how to get response data for future use

I'm running a login test like so:
def test_login_user(self):
client = APIClient()
url = reverse('rest_login')
data = {
'username': 'test',
'password': 'Welcome2'
}
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
client.logout()
If I login to the app normally I see a json return like this:
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJ1c2VybmFtZSI6ImV2YW4iLCJleHAiOjE1MTQ2NzYzNTYsImVtYWlsIjoiZXZhbkAyOGJlYXR0eS5jb20iLCJvcmlnX2lhdCI6MTUxNDY3Mjc1Nn0.8CfhfgtMLkNjEaWBfNXbUWXQMZG4_LIru_y4pdLlmeI",
"user": {
"pk": 2,
"username": "test",
"email": "test#test.com",
"first_name": "",
"last_name": ""
}
}
I want to be able to grab that token value for future use however the response does not seem to have a data value to grab.
What I'm looking for is response.content per the official documentation
https://docs.djangoproject.com/en/2.0/topics/testing/tools/#testing-responses
You can use response.json()['token'] to get data from the token field.
Usage as below:
token = response.json()['token'];
show error response:
response.context["form"].errors

Show Filters and Ordering in Django Rest Framework Options Request

I'm using the Django Rest Framework I noticed on the web browseable part of the API there is a button called 'options' when clicked it shows the following...
HTTP 200 OK Vary: Accept Content-Type: text/html Allow: HEAD, GET, OPTIONS
{
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"renders": [
"application/json",
"text/html"
],
"name": "Products",
"description": "API endpoint."
}
my question is, is there anyway I could list out here all the filter options an other stuff for this url?
You can make OPTIONS return whatever you want, by overriding the .metadata() method on the view.
See here: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/views.py#L340
Update as of 2015: We now have a customizable metadata API that makes this easier: http://www.django-rest-framework.org/api-guide/metadata/
You can totally do this. Here's a custom metadata class that I've been keeping up to date here on StackOverflow. This simply lists all the available filters, their types, and their choices. It also lists the ordering fields that are available on a class:
class SimpleMetadataWithFilters(SimpleMetadata):
def determine_metadata(self, request, view):
metadata = super(SimpleMetadataWithFilters, self).determine_metadata(request, view)
filters = OrderedDict()
if not hasattr(view, 'filter_class'):
# This is the API Root, which is not filtered.
return metadata
for filter_name, filter_type in view.filter_class.base_filters.items():
filter_parts = filter_name.split('__')
filter_name = filter_parts[0]
attrs = OrderedDict()
# Type
attrs['type'] = filter_type.__class__.__name__
# Lookup fields
if len(filter_parts) > 1:
# Has a lookup type (__gt, __lt, etc.)
lookup_type = filter_parts[1]
if filters.get(filter_name) is not None:
# We've done a filter with this name previously, just
# append the value.
attrs['lookup_types'] = filters[filter_name]['lookup_types']
attrs['lookup_types'].append(lookup_type)
else:
attrs['lookup_types'] = [lookup_type]
else:
# Exact match or RelatedFilter
if isinstance(filter_type, RelatedFilter):
model_name = (filter_type.filterset.Meta.model.
_meta.verbose_name_plural.title())
attrs['lookup_types'] = "See available filters for '%s'" % \
model_name
else:
attrs['lookup_types'] = ['exact']
# Do choices
choices = filter_type.extra.get('choices', False)
if choices:
attrs['choices'] = [
{
'value': choice_value,
'display_name': force_text(choice_name, strings_only=True)
}
for choice_value, choice_name in choices
]
# Wrap up.
filters[filter_name] = attrs
metadata['filters'] = filters
if hasattr(view, 'ordering_fields'):
metadata['ordering'] = view.ordering_fields
return metadata
Put that somewhere in your project, then set your DEFAULT_METADATA_CLASS, and you should be all set, with a new key on your OPTIONS requests like so:
"filters": {
"sub_opinions": {
"type": "RelatedFilter"
},
"source": {
"type": "MultipleChoiceFilter",
"choices": [
{
"display_name": "court website",
"value": "C"
},
]
}
...more...
}
This will also display choices, mirroring the way it's handled elsewhere in DRF.