在Marshallow / SQLAlchemy Schema上展平数据

时间:2019-01-14 01:55:12

标签: python flask sqlalchemy marshmallow

我如何从一个端点(尤其是棉花糖和SQLAlchemy)取回数据时遇到一些问题

https://gist.github.com/martinmckenna/eb5eeee5869663fc8f2e52a5e7ef72c9

我在鸡尾酒和配料之间有着多对多的关系,但是我不仅拥有关系表上的外键ings_in_cocktail的数据还多,例如ounces.,当我得到/cocktails/时,它返回如下内容:

{
  "cocktails": [
    {
      "glass": "rocks",
      "ingredients": [
        {
          "ingredient": {
            "ing_type": "liquor",
            "id": 1,
            "name": "gin"
          },
          "ounces": 20
        }
      ],
      "finish": "stirred",
      "id": 1,
      "name": "gin and tonic"
    }
  ]
}

我想做的是将ounces属性的传播与ingredient字典结合起来。

我希望数据如下所示:

{
  "cocktails": [
    {
      "glass": "rocks",
      "ingredients": [
        {
          "ing_type": "liquor",
          "id": 1,
          "name": "gin",
          "ounces": 20
        }
      ],
      "finish": "stirred",
      "id": 1,
      "name": "gin and tonic"
    }
  ]
}

在网上搜索了几个小时之后,我找不到使用Marshamallow轻松实现此目的的方法。有什么简单的方法我想念吗?

3 个答案:

答案 0 :(得分:1)

您可以使用IngredientSchema

中的方法字段

https://marshmallow.readthedocs.io/en/3.0/custom_fields.html#method-fields

请检查它如何在文档中使用此字段

答案 1 :(得分:0)

我最终解决了这个问题:

class CocktailSchema(ma.ModelSchema):
    # this is responsible for returning all the ingredient data on the cocktail
    ingredients = ma.Nested(CocktailIngredientSchema, many=True, strict=True)
    ingredients = fields.Method('concat_ingredients_dicts')

    """
    at this point the ingredients field on the cocktail object looks something like this

    ingredients: [{
        ingredient: {
            name: 'white russian',
            glass: 'rocks',
            finish: 'stirred'
        },
        ounces: 2,
        action: 'muddle',
        step: 1
    }]

    what we want is to concat this data so "ingredients" just turns
    into an list of dicts
    """
    def concat_ingredients_dicts(self, obj):
        result_ingredients_list = []
        i = 0
        while i < len(list(obj.ingredients)):
            # create a dict from the fields that live in the relational table
            relational_fields_dict = {
                'ounces': obj.ingredients[i].ounces,
                'action': obj.ingredients[i].action,
                'step': obj.ingredients[i].step
            }

            # create a dict from the fields on each ingredient in the cocktail
            ingredients_dict = obj.ingredients[i].ingredient.__dict__
            ingredients_dict_extracted_values = {
                'name': ingredients_dict.get('name'),
                'type': ingredients_dict.get('ing_type'),
                'id': ingredients_dict.get('id')
            }

            # merge the two dicts together
            merged = dict()
            merged.update(ingredients_dict_extracted_values)
            merged.update(relational_fields_dict)

            # append this merged dict a result array
            result_ingredients_list.append(merged)
            i += 1
        # return the array of ingredients
        return result_ingredients_list

    class Meta:
      model = Cocktail

答案 2 :(得分:0)

更新:我在底部放了一个更好的解决方案。

您可以通过使用MethodFunction字段来实现此目的,以允许父级从子级获取数据。这是您需要做的:

  1. 不理会CocktailSchema
  2. 完全摆脱IngredientSchema(除非您还需要其他东西)。
  3. CocktailIngredientSchema内,用几个Nested字段替换Function字段,这些字段将数据直接从内部“成分”对象中拉出。

之前的模式

class IngredientSchema(ma.ModelSchema):
    class Meta:
        model = Ingredient


class CocktailIngredientSchema(ma.ModelSchema):
    ingredient = ma.Nested(IngredientSchema)

    class Meta:
        model = CocktailIngredient


class CocktailSchema(ma.ModelSchema):
    ingredients = ma.Nested(CocktailIngredientSchema, many=True)

    class Meta:
        model = Cocktail

之后的模式

class CocktailIngredientSchema(ma.ModelSchema):
    ing_type = ma.Function(lambda obj: obj.ingredient.ing_type)
    id = ma.Function(lambda obj: obj.ingredient.id)
    name = ma.Function(lambda obj: obj.ingredient.name)

    class Meta:
        model = CocktailIngredient


class CocktailSchema(ma.ModelSchema):
    ingredients = ma.Nested(CocktailIngredientSchema, many=True)

    class Meta:
        model = Cocktail

棉花糖3+版本的更清洁替代品(需要Python 3)

  1. 不理会CocktailSchema
  2. 不理会IngredientSchema(而不是如上所示摆脱它)。
  3. CocktailIngredientSchema内,用几个Nested字段替换Pluck字段,这些字段将数据直接从内部架构中拉出。

之后的模式

class IngredientSchema(ma.ModelSchema):
    class Meta:
        model = Ingredient


class CocktailIngredientSchema(ma.ModelSchema):
    ing_type = ma.Pluck(IngredientSchema, 'ing_type')
    id = ma.Pluck(IngredientSchema, 'id')
    name = ma.Pluck(IngredientSchema, 'name')

    class Meta:
        model = CocktailIngredient


class CocktailSchema(ma.ModelSchema):
    ingredients = ma.Nested(CocktailIngredientSchema, many=True)

    class Meta:
        model = Cocktail