简体   繁体   中英

How to select only some of the columns from the database with Flask-SQLAlchemy?

I'm developing an API with flask_restplus and flask_sqlalchemy , and I have a special case where certain applications should access only certain columns from the API.

I have a model:

class MyModel(db.Model):
    __tablename_ = 'my_table'
    id = db.Column(db.Integer, primary_key=True)
    first_column = db.Column(db.Unicode)
    second_column = db.Column(db.Unicode)

I also have specified a flask_resplus ' model to be returned from the API:

my_model = api.model('MyModel',
                    {'first_column': fields.String(),
                     'second_column': fields.String()})

Where api is a flask_restplus ' Api instance and db a flask_sqlachmey 's instance.

Sometimes I want to select only some of the columns, and for the other column to be null in the api.model that is returned as a JSON repsonse.

After searching on the Internet, I found two methods which neither of them work in my case: load_only() from sqlalchemy.orm , which returns the columns as a list . As a result, I can't return those results, since my model expects a dictionary with same keys as described in my_model . The other method, with_entities() returns an instance of MyModel , which is what I need, but it loads all the columns at the time I pass that instance to the my_model , since it does only a lazy selection, ie it selects the specified columns, but if the other columns are required, it does a query again to get the values of other columns, thus loading, in my case, all the columns, which is not what I want. Does anyone know how to do an SQL SELECT where only some of the columns are returned and the result is an instance of the db.Model ?

Thank you in advance!

If you use flask-marshmallow for serialization, you can load the schema with a list of included ( only() ) or excluded ( load_only() ) fields.

So a rough mock-up with what you've got above:

from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow

db = SQLAlchemy()
ma = Marshmallow()

class MyModel(db.Model):
    __tablename_ = 'my_table'
    id = db.Column(db.Integer, primary_key=True)
    first_column = db.Column(db.Unicode)
    second_column = db.Column(db.Unicode)


class MyModelSchema(ma.ModelSchema):
    class Meta:
        model = MyModel

varlist = ['id','first_column']

def somefunction():
    results = MyModel.query.all()
    mymodelschema = MyModelSchema(many=True, only=varlist)
    output = mymodelschema.dump(results).data
    return jsonify(output) # or whatever you're doing with it

Here are the docs for the Marshmallow Schema API where there are a few options including excluding everything in a list, for example: https://marshmallow.readthedocs.io/en/latest/api_reference.html#marshmallow.Schema

You can use Field Masking in flask-restplus to access only the field you want.

Check Out this link: https://flask-restplus.readthedocs.io/en/stable/mask.html

curl -X GET "http://localhost:5000/mymodel" -H  "accept: application/json" -H  "X-Fields: first_column"
from flask import Flask
from flask_restplus import Resource, Api
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String
import os
from flask_restplus import Resource, fields

basedir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///{0}/app-dev.db'.format(
    basedir)
app.config['SECRET_KEY'] = '\xa4?\xca`\xa4~zG\xdf\xdbh\xba\xc2\xc6\xfc\x88\xc6x"\x11\xe8X8\n'

db = SQLAlchemy(app)
api = Api(app)


class MyModel(db.Model):
    __tablename_ = 'my_table'
    id = db.Column(db.Integer, primary_key=True)
    first_column = db.Column(db.String(255))
    second_column = db.Column(db.String(255))


my_model = api.model('MyModel',
                     {'first_column': fields.String(),
                      'second_column': fields.String()})


@api.route('/mymodel')
class GetModel(Resource):
    @api.marshal_with(my_model, envelope='resource', mask='first_column')
    def get(self, **kwargs):
        return MyModel.query.all()  # Some function that queries the db


@api.route('/hello')
class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    model1 = MyModel(first_column='m1_first', second_column='M1_SECOND')
    model2 = MyModel(first_column='m2_first', second_column='M2_SECOND')
    db.session.add(model1)
    db.session.add(model2)
    db.session.commit()
    app.run(debug=True)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM