在 Excel 中打开 OData 服务时出错,EDMX 元数据不正确

Error when opening an OData service in Excel, incorrect EDMX metadata

我正在尝试编写一个 RESTful API 来发布一些数据集。在 Python 中执行此操作并希望遵循 OData 标准。目标是能够从 Excel(数据 > 新查询 > OData)中打开数据。

第一次尝试似乎很有希望:我制作了一个连接到 mysql 数据库和 returns 所需行的 Flask api。 不幸的是,从 Excel 开始还不起作用。它在元数据描述符上出错,说它遇到了意外的 属性 'Article name' 名称(我的数据集的字母第一列),它只期望 'name' 和 'url' 在服务文件。

我似乎无法弄清楚正在生成的元数据有什么问题,因此非常感谢您的帮助。

app.py:

from flask import Flask, jsonify, request, make_response
from flask_restful import Resource, Api, reqparse
import pandas as pd
import mysql.connector as sql
import ast
import xml.etree.ElementTree as ET

app = Flask(__name__)
api = Api(app, default_mediatype='application/json')

config = {
  'host': '127.0.0.1',
  'port': '3306',
  'user': '***',
  'passwd': '***',
  'database': '***',
  'charset': 'utf8mb4',
  'raise_on_warnings': True
}

class FinancieleInstrumenten(Resource):
    def get(self):
        parser = reqparse.RequestParser()
        parser.add_argument('begrotingsjaar', required=False)
        parser.add_argument('begrotingshoofdstuk', required=False)
        args = parser.parse_args()  # parse arguments to dictionary

        try:
            cnx = sql.connect(**config)

            # Build query depending on input variables
            if not args['begrotingsjaar'] and not args['begrotingshoofdstuk']:
                qry = "SELECT * FROM FinancieleInstrumenten LIMIT 25;"
                res = pd.read_sql(qry, cnx)
            elif not args['begrotingshoofdstuk']:
                qry = """SELECT * FROM FinancieleInstrumenten WHERE Begrotingsjaar = {} LIMIT 25;"""
                res = pd.read_sql_query(qry.format(args['begrotingsjaar']), cnx)
            elif not args['begrotingsjaar']:
                qry = """SELECT * FROM FinancieleInstrumenten WHERE Begrotingshoofdstuk = {} LIMIT 25;"""
                res = pd.read_sql_query(qry.format(args['begrotingshoofdstuk']), cnx)
            else:
                qry = """SELECT * FROM FinancieleInstrumenten WHERE Begrotingsjaar = {} AND Begrotingshoofdstuk = {} LIMIT 25;"""
                res = pd.read_sql_query(qry.format(args['begrotingsjaar'], args['begrotingshoofdstuk']), cnx)

            # Build response with OData header
            resp = make_response({
                '@odata.context': 'https://stukkenparser.gitlab-minfin.nl/financiele-instrumenten/$metadata',
                'value': res.to_dict('records')
                })
            resp.headers['OData-Version'] = '4.0'
            return resp
        except Exception as e:
            print(str(e))
        finally:
            if cnx.is_connected():
                cnx.close()

    def put(self):
        return {'message': 'Only method GET allowed.'}, 405
    def post(self):
        return {'message': 'Only method GET allowed.'}, 405
    def patch(self):
        return {'message': 'Only method GET allowed.'}, 405
    def delete(self):
        return {'message': 'Only method GET allowed.'}, 405


api.add_resource(FinancieleInstrumenten, '/financiele-instrumenten')


@app.route("/financiele-instrumenten/$metadata", methods=['GET'])
def index():
    if request.method=='GET':
        root = ET.parse('financiele-instrumenten.metadata.xml').getroot()
        return app.response_class(ET.tostring(root), mimetype='application/xml')


if __name__ == '__main__':
    app.run(debug=True, host='127.0.0.1', port=1337)

金融-instrumenten.metadata.xml:

<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
  <edmx:DataServices>
    <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="NL.MinFin.OData.FinancieleInstrumenten">
      <EntityContainer Name="FinancieleInstrumentenEntities">
        <EntitySet Name="FinancieleInstrumentenSet" EntityType="NL.MinFin.OData.FinancieleInstrumenten.FinancieleInstrumentenType" />
      </EntityContainer>
      <EntityType Name="FinancieleInstrumentenType">
        <Property Name="Begrotingsjaar" Type="Edm.String" Nullable="false" />
        <Property Name="Begrotingshoofdstuk" Type="Edm.String" Nullable="false" />
        <Property Name="Begrotingsnaam" Type="Edm.String" Nullable="false" />
        <Property Name="Artikelnummer" Type="Edm.String" Nullable="true" />
        <Property Name="Artikelnaam" Type="Edm.String" Nullable="true" />
        <Property Name="Artikelonderdeel" Type="Edm.String" Nullable="true" />
        <Property Name="Instrument" Type="Edm.String" Nullable="true" />
        <Property Name="Regeling" Type="Edm.String" Nullable="true" />
        <Property Name="Ontvanger" Type="Edm.String" Nullable="true" />
        <Property Name="KvK-nummer" Type="Edm.String" Nullable="true" />
        <Property Name="Bedrag" Type="Edm.Int64" Nullable="false" />
      </EntityType>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

简答:

您的元数据不正确,因为您的实体类型缺少一个键

长答案:

来自:https://docs.microsoft.com/en-us/odata/concepts/data-model:

实体类型 实体类型被命名为带有键的结构化类型。它们定义实体的命名属性和关系。实体类型可以通过从其他实体类型的单一继承派生。

实体类型的键由实体类型的原始属性(例如 CustomerId、OrderId、LineId 等)的子集构成。

--

因此,实体的本质是,您可以唯一地标识它,以便从我们作为导航属性来源的外部访问它。因此,您必须定义一个实体键,这样,例如,当 ODATA 想要导航到其中一个时,它可以做到这一点,例如 /FinancieleInstrumentenEntities(key)/.

你的 edmx 应该看起来像这样(我猜测了密钥,希望它是独一无二的):

<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
  <edmx:DataServices>
    <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="NL.MinFin.OData.FinancieleInstrumenten">
      <EntityContainer Name="FinancieleInstrumentenEntities">
        <EntitySet Name="FinancieleInstrumentenSet" EntityType="NL.MinFin.OData.FinancieleInstrumenten.FinancieleInstrumentenType" />
      </EntityContainer>
      <EntityType Name="FinancieleInstrumentenType">
        <Key>
          <PropertyRef Name='Begrotingsjaar'/>
          <PropertyRef Name='Begrotingshoofdstuk'/>
          <PropertyRef Name='Begrotingsnaam'/>
        </Key>
        <Property Name="Begrotingsjaar" Type="Edm.String" Nullable="false" />
        <Property Name="Begrotingshoofdstuk" Type="Edm.String" Nullable="false" />
        <Property Name="Begrotingsnaam" Type="Edm.String" Nullable="false" />
        <Property Name="Artikelnummer" Type="Edm.String" Nullable="true" />
        <Property Name="Artikelnaam" Type="Edm.String" Nullable="true" />
        <Property Name="Artikelonderdeel" Type="Edm.String" Nullable="true" />
        <Property Name="Instrument" Type="Edm.String" Nullable="true" />
        <Property Name="Regeling" Type="Edm.String" Nullable="true" />
        <Property Name="Ontvanger" Type="Edm.String" Nullable="true" />
        <Property Name="KvK-nummer" Type="Edm.String" Nullable="true" />
        <Property Name="Bedrag" Type="Edm.Int64" Nullable="false" />
      </EntityType>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

如您所见,对于复合键,您可以添加多个 属性 引用。你至少需要一个 .

您用作键的属性必须不能为空,并且组合是唯一的很重要。 (在那种情况下我不确定)

如果您没有可唯一识别的项目,您可以尝试使用复杂类型而不是实体,将其作为 属性 附加到根实体(仍然需要密钥)。