将SQL查询初始化为数据类

3

目标是在SQL中基于一些手动分类的字段,实现所选列的获取,例如:

from dataclasses import dataclass


@dataclass
class Q:
    fruits = ('taste', 'color', 'shine')
    vege = ('is_green', 'color', 'juiceable')
    meat = ('is_white', 'is_dark', 'is_red', 'meat_name')
    carbs = ('is_refined', 'gi')

    @static_method
    def get_fields(self, add_fruits=False, add_vege=False, add_meat=False, add_carbs=False):
         fields = set()
         fields.update(self.fruits) if add_fruits else None
         fields.update(self.vege) if add_vege else None
         fields.update(self.meat) if add_meat else None
         fields.update(self.carbs) if add_carbs else None
         return sorted(",".join(sorted(fields)))


print(f"SELECT {Q.get_fields(add_fruits=True, add_vege=True)} from food_features")

给定一个Q对象中的一大类字段列表,我们如何避免在.get_fields函数中硬编码?

fields.update(self._field_name_) if add_field_name else None 
4个回答

2
这是您想要的输出/功能吗?
class Q :
    def __init__(self):
        self.categories = {
            'fruits': ('taste', 'color', 'shine'),
            'vege': ('is_green', 'color', 'juiceable'),
            'meat': ('is_white', 'is_dark', 'is_red', 'meat_name'),
            'carbs': ('is_refined', 'gi')
        }
    
    def get_fields(self, groups):
        fields = []
        for group in groups:
            fields.extend(self.categories[group])
        return", ".join(sorted(set(fields)))

value = Q()
print(f"SELECT {value.get_fields(['fruits', 'vege'])} FROM food_features")

输出:

SELECT color, is_green, juiceable, shine, taste FROM food_features

1
这个程序可能具有正确的功能,但并没有像问题标题所示使用数据类(在这里选择使用数据类可能是有争议的,但这是另一回事)。 - optimus_prime

1
您可以使用 dict 存储类别,然后使用 set 推导式:
>>> @dataclass
class Q:
    fields = {
              'fruits' : ('taste', 'color', 'shine'),
              'vege' : ('is_green', 'color', 'juiceable'),
              'meat' : ('is_white', 'is_dark', 'is_red', 'meat_name'),
              'carbs' : ('is_refined', 'gi')
             }

    @staticmethod
    def get_fields(to_add):
        return ', '.join({f for x in to_add for f in Q.fields[x]})

print(f"SELECT {Q.get_fields(('fruits','vege'))} from food_features")

SELECT taste, juiceable, shine, color, is_green from food_features

这需要对原始代码进行轻微的改动,但我认为这绝对是最好的答案 - 易于阅读,并且能够解决问题。 - optimus_prime
@optimus_prime,你觉得我的改进方案怎么样? - PleSo
1
@PleSo它能工作...但对我来说仍然是实现字典的一种变通方法 - 这里的重点是我们有一个键值情况,涉及到“水果”/“蔬菜”等及其相关集合 - 那么为什么要重新发明轮子呢?我猜当你回到代码时,这最终只会让你浪费更多时间。此外,这里的集合推导式非常优雅。 - optimus_prime
1
@optimus_prime 感谢您的反馈。确实,使用字典解决方案更容易思考和实现。关于重新发明轮子,我想为他提供一个完全向后兼容的解决方案,其中包括在参数中添加_*前缀以及保留类中字段的选项,但在我看来,在这种情况下这是值得怀疑的。 - PleSo

0

您可以使用kwargs__ getattribute __方法来实现此目的。

编辑:

一种更易读且更高效的方法:

from dataclasses import dataclass
from functools import reduce

@dataclass
class Q:
    fruits = ('taste', 'color', 'shine')
    vege = ('is_green', 'color', 'juiceable')
    meat = ('is_white', 'is_dark', 'is_red', 'meat_name')
    carbs = ('is_refined', 'gi')

    @classmethod
    def get_fields(cls, **kwargs):
        fields = set()

        for key, is_key_true in kwargs.items():
          if is_key_true:
            fields.update(getattr(cls, key.replace('add_', '')))

        return ", ".join(sorted(fields))

print(f"SELECT {Q.get_fields(add_fruits=True, add_vege=True, add_meat=False)} from food_features")

这将输出

SELECT color, is_green, juiceable, shine, taste from food_features

旧代码和一些解释:

使用kwargs,您将收到一个函数参数字典,其中可以添加任何想要添加的内容。例如add_fruits=True, add_vege=True等。我们将使用该字典将参数映射到元组(parameter, value)中,但删除add_前缀。我们将所有这些都添加到列表中,您最终将拥有属性值列表和一个布尔值,告诉我们是否要将它们添加到查询中。 [('fruits', True), ('vege', True)]

我们在values列表中进行迭代,并针对每个具有True的属性,使用__getattribute__方法从类属性获取数据。

我们将拥有属性元组的列表,我们只需将它们转换为列表,以便将我们想要的所有值放入单个列表中。

现在,我们拥有值列表,我们只需使用“,”将列表与“,”连接起来即可。

就是这样。

请查看下面的代码。

from dataclasses import dataclass


@dataclass
class Q:
    fruits = ('taste', 'color', 'shine')
    vege = ('is_green', 'color', 'juiceable')
    meat = ('is_white', 'is_dark', 'is_red', 'meat_name')
    carbs = ('is_refined', 'gi')

    
    def get_fields(self, **kwargs):
        values = [(key.replace('add_', ''), value) for key, value in kwargs.items()]
        tuples_query = [self.__getattribute__(key) for key, value in values if value]
        query = [field for single_tuple in tuples_query for field in single_tuple]
        
        return ", ".join(query)

q = Q()

print(f"SELECT {q.get_fields(add_fruits=True, add_vege=True, add_meat=False)} from food_features")

这将输出

SELECT taste, color, shine, is_green, color, juiceable from food_features

0

我个人建议使用枚举来模拟SQL查询中字段或分组的分类。

例如:

from dataclasses import dataclass
from enum import Enum
from typing import Sequence


@dataclass
class Q:

    class Group(Enum):
        FRUIT = ('taste', 'color', 'shine')
        VEG = ('is_green', 'color', 'juiceable')
        MEAT = ('is_white', 'is_dark', 'is_red', 'meat_name')
        CARBS = ('is_refined', 'gi')

    @staticmethod
    def get_fields(groups: Sequence['Q.Group'] = None):
        fields = set()

        if not groups:  # if no groups specified, just select everything
            return '*'

        for g in groups:
            fields.update(g.value)

        return ','.join(sorted(fields))

    @classmethod
    def get_query(cls, *groups: 'Q.Group'):
        return f'SELECT {cls.get_fields(groups)} from food_features'


print(Q.get_query())
print(Q.get_query(Q.Group.FRUIT, Q.Group.VEG))

输出:

SELECT * from food_features
SELECT color,is_green,juiceable,shine,taste from food_features

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接