Установите модель sqlalchemy и Зефир с большим вложенным JSON - PullRequest
0 голосов
/ 24 апреля 2020

Я создаю API с Flask и получаю несколько ошибок при построении модели для вложенной структуры и Marshmallow. Неважно, что я делаю, кажется, я не могу никуда добраться.

Входные данные являются вложенными JSON. Мне нужно хранить информацию в базе данных (sqlite) и запрашивать ее по мере необходимости. Кажется, модели, которые я создал, вроде бы нормальные. У меня есть некоторые сомнения в схемах. Не уверен, что они правильные или используют правильно. Информации об использовании таких вложенных структур не так много. Я вижу много базовых c вещей, включая платные уроки, но не так много с более сложными структурами:

{
    "bessAssets": [{
        "designation": "bess3",
        "status": true,
        "vNom": 0,
        "eNom": 0,
        "maxSoc": 90,
        "minSoc": 10,       
        "testData": {
            "vNomD": [{
                "cRate": 0,
                "vAvg": 0,
                "trial": 1
            }],
            "vNomC": [{
                "cRate": 0,
                "vAvg": 0,
                "trial": 1
            }]
        }
    }]
}

Работа, которую я сделал в последней попытке, была примерно такой:

from marshmallow import Schema, fields
from marshmallow_sqlalchemy import SQLAlchemySchema, auto_field, SQLAlchemyAutoSchema
from marshmallow import EXCLUDE
from flask_sqlalchemy import SQLAlchemy
from flask import Flask

import os
import json


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)


class BessAsset(db.Model):

    __tablename__ = 'bess_asset'

    designation = db.Column(db.String(70), primary_key=True)
    status = db.Column(db.Boolean)
    vNom = db.Column(db.Float)
    eNom = db.Column(db.Float)
    maxSoc = db.Column(db.Float)
    minSoc = db.Column(db.Float)


class TestData(db.Model):

    __tablename__ = 'test_data'

    test_data_id = db.Column(db.Integer, primary_key=True)
    bess_asset_designation = db.Column(db.String, db.ForeignKey('bess_asset.designation'))
    bess_asset = db.relationship('BessAsset', backref=db.backref("testData"), cascade='all')


class VnomD(db.Model):

    __tablename__ = 'vnom_d'

    vnom_id = db.Column(db.Integer, primary_key=True)
    cRate = db.Column(db.Float)
    vAvg = db.Column(db.Float)
    trial = db.Column(db.Integer)
    test_data_id = db.Column(db.Integer, db.ForeignKey('test_data.test_data_id'))
    test_data = db.relationship('TestData', backref=db.backref("vNomD"), cascade='all')


class VnomC(db.Model):

    __tablename__ = 'vnom_c'

    vnom_id = db.Column(db.Integer, primary_key=True)
    cRate = db.Column(db.Float)
    vAvg = db.Column(db.Float)
    trial = db.Column(db.Integer)
    test_data_id = db.Column(db.Integer, db.ForeignKey('test_data.test_data_id'))
    test_data = db.relationship('TestData', backref=db.backref("vNomC"), cascade='all')


class VnomDSchema(SQLAlchemyAutoSchema):

    class Meta:
        model = VnomD
        exclude = ('vnom_id',)
        include_fk = True
        # load_instance = True


class VnomCSchema(SQLAlchemyAutoSchema):

    class Meta:
        model = VnomC
        exclude = ('vnom_id',)
        include_fk = True
        # load_instance = True


class TestDataSchema(SQLAlchemyAutoSchema):

    vnom_c = fields.Nested(VnomCSchema, many=True)
    vnom_d = fields.Nested(VnomDSchema, many=True)

    class Meta:
        model = TestData
        unknown = EXCLUDE
        include_relationships = True


class BessAssetSchema(SQLAlchemyAutoSchema):

    testData = fields.Nested(TestDataSchema, )

    class Meta:
        model = BessAsset
        unknown = EXCLUDE
        include_relationships = True
        # load_instance = True


db.create_all()

with open(os.path.join('examples', 'structures', 'assets.json')) as f:
    json_file = json.load(f)

bess_asset_schema = BessAssetSchema()
vnomd_schema = VnomDSchema()
vnomc_schema = VnomCSchema()

for b_asset in json_file['bessAssets']:

    bess_structure = (bess_asset_schema.load(b_asset))

    bess_asset = BessAsset(**bess_structure)
    test_data = TestData(bess_asset=bess_asset)

    db.session.add(bess_asset)

    for value in b_asset['testData']['vNomD']:
        vnom_d_structure = (vnomd_schema.load(value))
        vnom_d = VnomD(**vnom_d_structure, test_data=test_data)
        db.session.add(vnom_d)

    for value in b_asset['testData']['vNomC']:
        vnom_c_structure = (vnomc_schema.load(value))
        vnom_c = VnomC(**vnom_c_structure, test_data=test_data)
        db.session.add(vnom_c)

    db.session.commit()

bess_assets = BessAsset.query.all()
dump_data = bess_asset_schema.dump(bess_assets)

print(dump_data)

Похоже, из этой строки произошла ошибка (AttributeError: 'NoneType' object has no attribute 'query'):

for b_asset in json_file['bessAssets']:

    bess_structure = (bess_asset_schema.load(b_asset))

Не могли бы вы помочь мне разобраться, как правильно сохранить эту структуру и вывести ее пользователю?

Спасибо!

1 Ответ

0 голосов
/ 26 апреля 2020

Хорошо, я понял это. Не уверен, что этот вопрос привлечет внимание других людей, но я уверен, что это может быть полезно для кого-то. Вот мое решение (SQLAlchemy + Marshmallow):

from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, ForeignKey, String, Float, Boolean
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
from marshmallow import fields
import json

from sqlalchemy import create_engine

engine = create_engine('sqlite:///test.db', echo=True)
Session = sessionmaker(bind=engine)
session = Session()

Base = declarative_base()


# parent
class BessAsset(Base):

    __tablename__ = 'bess_asset'

    designation = Column(String, primary_key=True)
    status = Column(Boolean)
    vNom = Column(Float)
    eNom = Column(Float)
    maxSoc = Column(Float)
    minSoc = Column(Float)
    maxCCh = Column(Float)
    maxCDch = Column(Float)
    minPCh = Column(Float)
    minPDch = Column(Float)
    chEff = Column(Float)
    dischEff = Column(Float)
    eolCriterion = Column(Float)
    lifetime = Column(Integer)
    invPNom = Column(Float)
    invVNom = Column(Float)
    # one to one mapping
    testData = relationship('TestData', backref='bessAsset', lazy='select', uselist=False)


# add other testData elements relationships here
# one to one relationship
# one bessAsset will have one testdata
class TestData(Base):

    __tablename__ = 'test_data'

    test_data_id = Column(Integer, primary_key=True)
    designation = Column(String, ForeignKey('bess_asset.designation'))
    vNomD = relationship('Vnomd', backref='testData', lazy='select')
    vNomC = relationship('Vnomc', backref='testData', lazy='select')
    dLim = relationship('Dlim', backref='testData', lazy='select')
    cLim = relationship('Clim', backref='testData', lazy='select')
    effD = relationship('Effd', backref='testData', lazy='select')
    effC = relationship('Effc', backref='testData', lazy='select')
    roundEff = relationship('RoundEff', backref='testData', lazy='select')


class RoundEff(Base):

    __tablename__ = 'round_eff'
    roundeff_id = Column(Integer, primary_key=True)
    cRate = Column(Float)
    roundEffAvg = Column(Float)
    trial = Column(Integer)
    test_data_id = Column(Integer, ForeignKey('test_data.test_data_id'))


class Effc(Base):

    __tablename__ = 'eff_c'
    effc_id = Column(Integer, primary_key=True)
    cRate = Column(Float)
    effChAvg = Column(Float)
    trial = Column(Integer)
    test_data_id = Column(Integer, ForeignKey('test_data.test_data_id'))


class Effd(Base):

    __tablename__ = 'eff_d'
    effd_id = Column(Integer, primary_key=True)
    cRate = Column(Float)
    effDchAvg = Column(Float)
    trial = Column(Integer)
    test_data_id = Column(Integer, ForeignKey('test_data.test_data_id'))


class Clim(Base):

    __tablename__ = 'clim'

    clim_id = Column(Integer, primary_key=True)
    cRate = Column(Float)
    eRemain = Column(Float)
    trial = Column(Integer)
    test_data_id = Column(Integer, ForeignKey('test_data.test_data_id'))


class Dlim(Base):

    __tablename__ = 'dlim'

    dlim_id = Column(Integer, primary_key=True)
    cRate = Column(Float)
    eRemain = Column(Float)
    trial = Column(Integer)
    test_data_id = Column(Integer, ForeignKey('test_data.test_data_id'))


class Vnomd(Base):

    __tablename__ = 'vnomd'

    vnomd_id = Column(Integer, primary_key=True)
    cRate = Column(Float)
    vAvg = Column(Float)
    trial = Column(Integer)
    test_data_id = Column(Integer, ForeignKey('test_data.test_data_id'))


class Vnomc(Base):

    __tablename__ = 'vnomc'

    vnomc_id = Column(Integer, primary_key=True)
    cRate = Column(Float)
    vAvg = Column(Float)
    trial = Column(Integer)
    test_data_id = Column(Integer, ForeignKey('test_data.test_data_id'))


class VnomDSchema(SQLAlchemyAutoSchema):

    class Meta:
        model = Vnomd
        exclude = ['vnomd_id']


class VnomCSchema(SQLAlchemyAutoSchema):

    class Meta:
        model = Vnomc
        exclude = ['vnomc_id']


class RoundEffSchema(SQLAlchemyAutoSchema):

    class Meta:
        model = RoundEff
        exclude = ['roundeff_id']


class DlimSchema(SQLAlchemyAutoSchema):

    class Meta:
        model = Dlim
        exclude = ['dlim_id']


class ClimSchema(SQLAlchemyAutoSchema):

    class Meta:
        model = Clim
        exclude = ['clim_id']


# include test_data nested objects here
# will not be a list
class TestDataSchema(SQLAlchemyAutoSchema):

    vNomD = fields.Nested(VnomDSchema, many=True)
    vNomC = fields.Nested(VnomCSchema, many=True)
    dLim = fields.Nested(DlimSchema, many=True)
    cLim = fields.Nested(ClimSchema, many=True)
    roundEff = fields.Nested(RoundEffSchema, many=True)

    class Meta:
        model = TestData
        exclude = ['test_data_id', 'bessAsset']
        include_relationships = True


class BessAssetSchema(SQLAlchemyAutoSchema):

    testData = fields.Nested(TestDataSchema)

    class Meta:
        model = BessAsset
        ordered = True  # returns a ordered dict
        # include_fk = True
        # exclude = ['id', 'director_id']


Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)

vnomc = Vnomc(cRate=0.2, vAvg=0.34, trial=3)
vnomd = Vnomd(cRate=0.2, vAvg=0.34, trial=3)
roundEff = RoundEff(cRate=0.3, roundEffAvg=2.1, trial=2)
dlim = Dlim(cRate=2.5, eRemain=3.4, trial=100)
clim = Clim(cRate=2.5, eRemain=3.4, trial=200)

test_data = TestData(vNomC=[vnomc],
                     vNomD=[vnomd],
                     roundEff=[roundEff],
                     dLim=[dlim],
                     cLim=[clim])

bess_asset = BessAsset(designation='Teste 1',
                       status=True,
                       vNom=0.2,
                       eNom=0.3,
                       minPCh=2.1,
                       maxCCh=20.3,
                       minSoc=20.1,
                       maxSoc=1.3,
                       testData=test_data)

session.add(bess_asset)
session.commit()

bess_assets = session.query(BessAsset).all()
my_schema = BessAssetSchema()
my_data = my_schema.dump(bess_assets, many=True)
print(my_data)
...