【问题标题】:Sqlalchemy: building a class that contains a collection of items from another classSqlalchemy:构建一个包含另一个类的项目集合的类
【发布时间】:2015-02-01 04:33:24
【问题描述】:

我有以下三个类:

  1. A
  2. AItem
  3. ACollection

用户可以添加无限数量的对象作为class A 的一部分。从 A 继承的每个对象都可以添加无限数量的对象作为class AItem 的一部分。然后,用户可以根据需要对这些项目进行集合或组合。这些集合仅适用于属于AItem 的项目,它是A 的一部分。请注意,并非 A 中的每个对象都必须有一个集合,并且项目可以出现在多个集合中。

例子:

colors = A
red = AI(colors)
green = AI(colors)
blue = AI(colors)
yellow = AI(colors)
black = AI(colors)
brown = AI(colors)
white = AI(colors)

Collection_1 = [red, green, blue]
Collection_2 = [brown, black]
Collection_3 = [red, brown, yellow]

到目前为止我的代码:

class A(db.model):
    __tablename__ = 'a'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    a_items = db.relationship('AItem', backref='a')

class AItem(db.model):
    __tablename__ = 'a_items'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    A_id = db.Column(db.Integer, db.ForeignKey('a.id'))

class ACollection(db.model):
    __tablename__ = 'a_collections'
    id = db.Column(db.Integer, primary_key=True)

我的问题:

如何构建ACollection 类?另外,请随时“重新设计”或重构我的任何代码。

【问题讨论】:

    标签: oop python-2.7 sqlalchemy flask-sqlalchemy


    【解决方案1】:

    您想获得有关如何构建数据或类的建议吗?由于我不确定,我会给你两个。

    数据:

    从数据的角度来看,我建议您在表 a_itemsa_collections 之间创建一个表 a_items_collections,因为您有 N 到 N 的关系(一个集合可以有很多项目和一个item 可以属于多个集合)。因此,a_items_collections 的每一行都会在项目和集合之间建立链接。

    面向对象:

    从 OOP 的角度来看,您有很多可能性来定义您的数据类。您可以在ACollection 中设置AItem 的数组,您可以为表a_items_collections 使用另一个集合,...您当然应该使用ORM 来帮助您做到这一点。我不是 Python 方面的专家,所以在这里我帮不了你太多。此外,我强烈建议您使用 SOA(面向服务的架构)模式将业务类与数据类分开。更清楚地说,数据库的数据类镜像应该只包含属性,并且操作这些数据的智能应该在其他类中实现(通常实现为singleton)。如果您必须以与您的项目相同的方式操作您的集合,您可以查看composite 模式。

    【讨论】:

    • 这不是一个或另一个。如果你想做一个算法或/和一个程序,你应该使用 OOP。如果您想在程序中保留项目和集合对象的状态(例如,在您关闭计算机或网络服务器时检索它们),您应该使用数据库。您可以使用 ORM(对象关系映射器)来帮助您将数据从数据库转换为程序中的对象(来自我所谓的数据类)。
    • 感谢您的解释。我正在做的应用程序是一个 Web 应用程序,可以让我更深入地了解 Web 开发、数据库设计和 ORM 构建。这是我计划构建的另一个现实世界应用程序中将面临的问题,因此了解问题和解决方案对我来说至关重要。到周末我可能还有一些问题,如果你有什么要补充的,请随时这样做。我的目标是正确构建它并理解为什么要这样做
    • 我给了你一些有趣的链接。可能很难理解所有内容,但您应该快速浏览一下以了解这些概念的存在。如果你能做 Python 以外的其他语言,PHP 更适合做 OOP。我建议您将 PHP 与框架 Symfony2 一起使用。它可以帮助您以一种简单的方式开始您的 Web 开发,默认情况下提供了一个称为 Doctrine 的 ORM,并且可以在您更熟悉它时在专家级别使用(帮助您使用我告诉您的所有模式)。
    • 当然,你可以在这里问更多问题,或者如果你认为其他人不感兴趣,可以直接问我(你可以在我的github上找到我的电子邮件)。
    【解决方案2】:

    我建议您从 SQLAlchemy 文档的Object Relational Tutorial 部分开始,这将使您更好地理解实现的 OO 概念以及所表示的数据结构。它解释了工作单元(会话)、领域模型和各种类型的关系。如果您只需要阅读一页(尽管相当大),那就是它。

    但为了让您入门,请参阅下面的独立运行代码,其中包含您的模型,包括多对多关系、插入、更新和删除数据。根据您的代码示例代码使用 Flask 和 Flask-SQLAlchemy。

    另外,我真的会为您的模型对象和关系赋予其他名称:

    A           -> Category
    AItem       -> [Category]Item
    ACollection -> [Item]Collection
    A.a_items   -> items (with backref *category*)
    

    下面是代码,用SQLALCHEMY_ECHO 注释[out] 行,以便隐藏/查看已执行的SQL 语句。只需将其保存到某个 python 文件并运行它。

    from flask import Flask
    from flask.ext.sqlalchemy import SQLAlchemy
    
    app = Flask(__name__)
    _db_uri = 'sqlite:///:memory:'
    app.config['SQLALCHEMY_DATABASE_URI'] = _db_uri
    app.config['SQLALCHEMY_ECHO'] = True  # @TODO: uncomment to see SQL statements
    db = SQLAlchemy(app)
    
    
    # utility to make string representation of objects human-readable
    def _repr(self):
        from sqlalchemy.orm import class_mapper
        attrs = class_mapper(self.__class__).column_attrs  # only columns
        # attrs = class_mapper(self.__class__).attrs  # show also relationships
        return u"<{}({})>".format(
            self.__class__.__name__,
            ', '.join(
                '%s=%r' % (k.key, getattr(self, k.key))
                for k in sorted(attrs)
            )
        )
    db.Model.__repr__ = _repr
    
    
    # DEFINE THE MODEL
    class A(db.Model):
        __tablename__ = 'a'
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(64), unique=True)
    
        a_items = db.relationship('AItem', backref='a')
    
        def get_item_by_name(self, item_name):
            """ In memory search for the item of this class of given name. """
            for item in self.a_items:
                if item.name == item_name:
                    return item
    
    
    class AItem(db.Model):
        __tablename__ = 'a_items'
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(64), unique=True)
        A_id = db.Column(db.Integer, db.ForeignKey('a.id'))
    
    
    # this table contains many-to-many relationship
    t_collection_item = db.Table(
        'a_collection_item', db.Model.metadata,
        db.Column('a_items_id', db.Integer, db.ForeignKey('a_items.id'),
                primary_key=True),
        db.Column('a_collections_id', db.Integer, db.ForeignKey('a_collections.id'),
                primary_key=True)
        )
    
    
    class ACollection(db.Model):
        __tablename__ = 'a_collections'
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(64))
    
        items = db.relationship(AItem,
                                secondary=t_collection_item,
                                backref='collections')
    
    
    def test_model():
        with app.app_context():
            db.create_all()
    
            # CREATE SOME TEST DATA
            # create instance of *A*
            colors = A(name='colors')
            smells = A(name='smells')
    
            # add some items to them
            colors.a_items = [
                AItem(name='red'),
                AItem(name='green'),
                AItem(name='blue'),
                AItem(name='yellow'),
                AItem(name='black'),
            ]
            smells.a_items = [
                AItem(name='superb'),
                AItem(name='awful')
            ]
    
            # this will insert into DB instance of *A* and all their dependents
            db.session.add(colors)
            db.session.add(smells)
            db.session.commit()
    
            # create some collections, but first find colors
            red = colors.get_item_by_name('red')
            green = colors.get_item_by_name('green')
            blue = colors.get_item_by_name('blue')
            black = colors.get_item_by_name('black')
            yellow = colors.get_item_by_name('yellow')
            # create a new one
            white = AItem(name='white')
            colors.a_items.append(white)
    
            coll1 = ACollection(name='my favorites', items=[red, green, blue])
            coll2 = ACollection(name='i hate these', items=[black, yellow])
    
            db.session.add_all([white, coll1, coll2])
            db.session.commit()
    
            # SOME QUERIES
            # get collections which has blue color
            q = (db.session.query(ACollection)
                .join(AItem, ACollection.items)
                .filter(AItem.name == 'blue')
                )
            for coll in q.all():
                print(coll)
                for item in coll.items:
                    print("  {}".format(item))
    
            # Now I hate blue, so lets remove it from my collection and rename it
            mycol = db.session.query(ACollection)\
                .filter(ACollection.name == 'my favorites').one()
            mycol.name = 'my new favorites'
            mycol.items.remove(blue)
            db.session.commit()
            assert 2 == len(mycol.items)
    
    
    if __name__ == '__main__':
        test_model()
    

    【讨论】:

      猜你喜欢
      • 2016-05-16
      • 1970-01-01
      • 1970-01-01
      • 2020-12-03
      • 1970-01-01
      • 2019-07-12
      • 2014-12-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多