3

I have something like this:

{
  "id": 1, 
  "username": "plasmy",
  "userdetails": [
    {
      "detail": "A Name", 
      "detail_name": "Full Name", 
      "id": 1, 
      "user_id": 1
    }, 
    {
      "detail": "an@email.com", 
      "detail_name": "Email", 
      "id": 2, 
      "user_id": 1
    }, 
    {
      "detail": "An Address", 
      "detail_name": "Address", 
      "id": 3, 
      "user_id": 1
    }, 
    {
      "detail": "999-999-9999", 
      "detail_name": "Phone Number", 
      "id": 4, 
      "user_id": 1
    }
  ]
}

This comes as a result from using Flask_Restless and SQLAlchemy. There is a table for users and a table for userdetails, which are put in the userdetails part of that JSON. What I want to do is, find a way in which the data can look like this:

{
  "id": 1, 
  "username": "plasmy",
  "userdetails": {
      "Full Name": "A Name",
      "Email": "an@email.com",
      "Address": "An Address",
      "Phone Number": "A Phone Number"
    }
}

See how I removed the ids and I used the field "detail_name" as the key and "detail" as value. I tried using preprocessors but they didn't work or maybe I'm using them wrong. I put the preprocessor in the "child" table.

This is what I tried doing (but didn't work):

def detail_sort(results):
    return {'user_details': results['userdetails']}


manager.create_api(User, methods=['GET', 'POST'])
manager.create_api(UserDetails, methods=['GET', 'POST'],
                   preprocessors={
                       'GET_COLLECTION': [detail_sort]
                   })

I tried GET_COLLECTION, GET_SINGLE and GET_MANY. Any help on this will be greatly appreciated.

UPDATE: Here is the new code I tried based on the answer

from flask import Blueprint
from medinv import manager
from medinv.User.models import User, UserDetails

blueprint = Blueprint('blueprint', __name__)


@blueprint.route('/')
@blueprint.route('/home')
def home():
    return "Welcome."


def detail_sort(results):
    print(results)
    results['userdetails'] = {item['detail_name']: item['detail'] for item in results['userdetails']}
    return results['userdetails']


manager.create_api(User, methods=['GET', 'POST'])
manager.create_api(UserDetails, methods=['GET', 'POST'],
                   postprocessors={
                       'GET_COLLECTION': [detail_sort]
                   })
plasmy
  • 99
  • 3
  • 9
  • 32

1 Answers1

3

I think you need to use postproccessors since you need to modify the json response before sending it back to the client.

OK, I reproduced your problem. Now it's working. Here is my code:

import flask
import flask_sqlalchemy
import flask_restless

# Create the Flask application and the Flask-SQLAlchemy object.
app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask_sqlalchemy.SQLAlchemy(app)


# Create your Flask-SQLALchemy models as usual but with the following
# restriction: they must have an __init__ method that accepts keyword
# arguments for all columns (the constructor in
# flask_sqlalchemy.SQLAlchemy.Model supplies such a method, so you
# don't need to declare a new one).

class User(db.Model):

    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String)
    userdetails = db.relationship('UserDetails', backref='User', lazy='dynamic')


class UserDetails(db.Model):

    __tablename__ = 'user_details'

    id = db.Column(db.Integer, primary_key=True)
    detail = db.Column(db.String)
    detail_name = db.Column(db.String)
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)

# Create the database tables.
db.create_all()

# Create the Flask-Restless API manager.
manager = flask_restless.APIManager(app, flask_sqlalchemy_db=db)

user = User(username='plasmy')
userdetail_0 = UserDetails(detail='A name', detail_name='Full Name' )
userdetail_1 = UserDetails(detail='an@email.com', detail_name='Email')
userdetail_2 = UserDetails(detail='An Address', detail_name='Address')
userdetail_3 = UserDetails(detail='999-999-9999', detail_name='Phone Number')


user.userdetails.append(userdetail_0)
user.userdetails.append(userdetail_1)
user.userdetails.append(userdetail_2)
user.userdetails.append(userdetail_3)

db.session.add(user)
db.session.commit()

print('USER CREATED')

def detail_sort(result, **kw):
    print('detail_sort called')
    print(result)

    for entry in result['objects']:
        entry['userdetails'] = {item['detail_name']: item['detail'] for item in
                                entry['userdetails']}
    print('MODIFIED JSON: ', result)

# Create API endpoints, which will be available at /api/<tablename> by
# default. Allowed HTTP methods can be specified as well.
# manager.create_api(Person, methods=['GET', 'POST', 'DELETE'])
# manager.create_api(Article, methods=['GET'])

manager.create_api(User, methods=['GET', 'POST', 'DELETE'],
                   postprocessors={
                       'GET_MANY': [detail_sort]
                   })
manager.create_api(UserDetails, methods=['GET'], )

# start the flask loop
app.run(use_reloader=False)

Note that you need to use GET_MANY and look how detail_sort is implemented.

Without using postprocessor the response is like this:

{
  "num_results": 1, 
  "objects": [
    {
      "id": 1, 
      "userdetails": [
        {
          "detail": "A name", 
          "detail_name": "Full Name", 
          "id": 1, 
          "user_id": 1
        }, 
        {
          "detail": "an@email.com", 
          "detail_name": "Email", 
          "id": 2, 
          "user_id": 1
        }, 
        {
          "detail": "An Address", 
          "detail_name": "Address", 
          "id": 3, 
          "user_id": 1
        }, 
        {
          "detail": "999-999-9999", 
          "detail_name": "Phone Number", 
          "id": 4, 
          "user_id": 1
        }
      ], 
      "username": "plasmy"
    }
  ], 
  "page": 1, 
  "total_pages": 1
}

With postprocessor the response looks like this:

{
  "num_results": 1, 
  "objects": [
    {
      "id": 1, 
      "userdetails": {
        "Address": "An Address", 
        "Email": "an@email.com", 
        "Full Name": "A name", 
        "Phone Number": "999-999-9999"
      }, 
      "username": "plasmy"
    }
  ], 
  "page": 1, 
  "total_pages": 1
}

Hope this helps.

Nurjan
  • 5,889
  • 5
  • 34
  • 54
  • Thank you for your answer. I tried that and it didn't work. I'm going to edit my views.py code in the original post to see if you can see anything wrong. Also, the part that says results['userdetails'] = .... is giving me an error, saying that's not possible. I tried changing it a bit to returning only the results['userdetails'] and the error goes away but it still doesn't work. In fact I tried doing a print and it is not getting there (to detail_sort). – plasmy Jun 13 '17 at 15:29
  • @plasmy What do you get as error? Try first set the value for key userdetails in results and then return it. I haven't tested my code. – Nurjan Jun 13 '17 at 15:33
  • Yes I did try this and the error does not appear. The problem is that it's still not doing what I want. – plasmy Jun 13 '17 at 15:34
  • @plasmy Could you paste the stacktrace of the error? – Nurjan Jun 13 '17 at 15:36
  • `Traceback (most recent call last): File "run.py", line 1, in from medinv import app File "/Users/myname/PycharmProjects/MedInvAPI/medinv/__init__.py", line 10, in from medinv.User.views import blueprint File "/Users/myname/PycharmProjects/MedInvAPI/medinv/User/views.py", line 15 return results['userdetails'] = {item['detail_name']: item['detail'] for item in results['userdetails']} ^ SyntaxError: invalid syntax` I've edited the code to show how I fixed this error. With that code I'm not getting the error. – plasmy Jun 13 '17 at 15:45
  • @plasmy If there is no error now, what happens to results? Did you print it? – Nurjan Jun 13 '17 at 15:47
  • It's not modifying it like I want. I even tried doing a print but it's like it never gets there because the print is never executed. – plasmy Jun 13 '17 at 15:50
  • @plasmy Have you tried to add postprocessor to User, but no to UserDetails in `create_api()` call? – Nurjan Jun 13 '17 at 17:00
  • That was perfect! I modified the code a little bit since it was only working for one record. I also added GET_SINGLE to the postprocessors and in detail_sort I made an if checking for 'object' in result. If it existed then it executes your code and if it doesn't it's pretty much the same code but without the first foreach. Thank you so much!!!! – plasmy Jun 13 '17 at 20:33