【问题标题】:can I store a Dictionary as the property of an object?我可以将字典存储为对象的属性吗?
【发布时间】:2012-11-29 14:04:12
【问题描述】:

使用 Python/Flask/SQLAlchemy/Heroku。

想要将对象的字典存储为对象的属性:

澄清

class SoccerPlayer(db.Model):
    name = db.Column(db.String(80))
    goals_scored = db.Column(db.Integer())

^如何将姓名和进球数设置为一本字典?


更新:如果有任何区别,用户将输入名称和goals_scored。

另外,我正在网上搜索合适的答案,但作为菜鸟,我无法理解/实现我在 Google 上为我的 Flask 网络应用找到的内容。

【问题讨论】:

  • 您想做什么以使数据结构(理论上)为soccer_player.data["name"], soccer_player.data["goals_scored"] 而不是soccer_player.name, soccer_player.goals_scored? (知道这将有助于改进答案)。
  • 我想将一支球队的 SoccerPlayer 列表与另一支球队的 SoccerPlayer 列表进行比较——就进球数而言。每队进球最多的球员 - 将被输出到网页。
  • 我只是觉得字典会很有用。我还计划稍后添加助攻、犯规等 - 所以我认为如果没有字典就太混乱了。
  • 我认为直接使用数据库的字段比将数据打包到 Python(或 JSON)字典中要好得多。无论您想进行何种比较,使用 DB 查询肯定会更快(并且可能更容易),而不是使用您自己的字典处理代码。

标签: python web-applications heroku sqlalchemy flask


【解决方案1】:

我会支持 Sean 提供的方法,遵循它你会得到正确的 标准化的 DB 模式,可以更轻松地利用 RDBMS 为您完成繁重的工作。如果, 但是,您坚持在数据库中使用类似字典的结构,我会 建议试试hstore 允许您将键/值对存储为单个值的数据类型 邮递员。我不确定在 Postgres 中是否默认创建 hstore 扩展名 Heroku 提供的数据库,你可以通过在里面输入\dx 命令来检查 psql。如果其中没有带有hstore 的行,您可以通过以下方式创建它 输入CREATE EXTENSION hstore;

由于 SQLAlchemy 中的 hstore 支持在 0.8 版中可用,但在 尚未发布(但希望会在未来几周内发布),您需要安装它 来自其 Mercurial 存储库:

pip install -e hg+https://bitbucket.org/sqlalchemy/sqlalchemy#egg=SQLAlchemy

然后像这样定义你的模型:

from sqlalchemy.dialects.postgresql import HSTORE
from sqlalchemy.ext.mutable import MutableDict

class SoccerPlayer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), nullable=False, unique=True)
    stats = db.Column(MutableDict.as_mutable(HSTORE))

# Note that hstore only allows text for both keys and values (and None for
# values only).
p1 = SoccerPlayer(name='foo', stats={'goals_scored': '42'})
db.session.add(p1)
db.session.commit()

之后,您可以在查询中执行常规操作:

from sqlalchemy import func, cast

q = db.session.query(
    SoccerPlayer.name,
    func.max(cast(SoccerPlayer.stats['goals_scored'], db.Integer))
).group_by(SoccerPlayer.name).first()

查看HSTORE docs 更多示例。

【讨论】:

    【解决方案2】:

    如果您将此类信息存储在数据库中,我会推荐另一种方法:

    class SoccerPlayer(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(80))
        team_id = db.Column(db.Integer, db.ForeignKey('Team.id'))
        stats = db.relationship("Stats", uselist=False, backref="player")
    
    
    class Team(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(80))
        players = db.relationship("SoccerPlayer")
    
    
    class Stats(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        player_id = db.Column(db.Integer, db.ForeignKey('SoccerPlayer.id'))
        goals_scored = db.Column(db.Integer)
        assists = db.Column(db.Integer)
        # Add more stats as you see fit
    

    使用此模型设置,您可以做如下疯狂的事情:

    from sqlalchemy.sql import func
    
    max_goals_by_team = db.session.query(Team.id,
                        func.max(Stats.goals_scored).label("goals_scored")
                    ). \
                join(SoccerPlayer, Stats). \
                group_by(Team.id).subquery()
    
    players = SoccerPlayer.query(Team.name.label("Team Name"),
                                    SoccerPlayer.name.label("Player Name"),
                                    max_goals_by_team.c.goals_scored). \
                    join(max_goals_by_team,
                            SoccerPlayer.team_id == max_goals_by_team.c.id,
                            SoccerPlayer.stats.goals_scored == max_goals_by_team.c.goals_scored). 
                    join(Team)
    

    因此,让数据库完成了从每支球队中选出目标最高的球员的艰苦工作,而不是全部用 Python 完成。

    【讨论】:

    • 你将如何初始化 player_id 以获取统计信息(即如何将其添加到 def init 方法中)
    【解决方案3】:

    默认情况下,甚至 django(比flask 更大的 python web 框架)也不支持这个。但是在django你可以安装它,它叫做jsonfield(https://github.com/bradjasper/django-jsonfield)。

    我想告诉你的是,并非所有数据库都知道如何存储二进制文件,但它们确实知道如何存储字符串,而 django 的 jsonfield 实际上是一个包含字典的 json 转储的字符串。

    所以,简而言之,你可以在烧瓶中做

    import simplejson
    
    class SoccerPlayer(db.Model):
      _data = db.Column(db.String(1024))
    
      @property
      def data(self):
          return simplejson.loads(self._data)
    
      @data.setter
      def data(self, value):
          self._data = simplejson.dumps(value)
    

    但请注意,这样您一次只能分配整个字典:

    player = SoccerPlayer()
    player.data = {'name': 'Popey'}
    print player.data # Will work as expected
    {'name': 'Popey'}
    player.data['score'] = '3'
    print player.data
    # Will not show the score becuase the setter doesn't know how to input by key
    {'name': 'Popey'} 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-04
      • 2015-12-23
      • 1970-01-01
      相关资源
      最近更新 更多