1

抱歉,如果这是一个新手问题,但有关多对一关系的文档似乎并未涵盖这一点。我一直在寻找与此类似的内容(在“如何向表中插入/添加数据”部分),但是在所示示例中,这始终是唯一的插入。

基本上,我想用本地机器上的数据填充我的数据库。为简单起见,我将下面显示的示例构建到说明问题的MWE中。该问题由两个名为Priceand的表组成,Currency并且以声明式的方式完成了实现。

模型.py

from sqlalchemy import Column, Integer, String
from sqlalchemy import Float, BigInteger, ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Currency(Base):
    __tablename__ = 'Currency'

    id = Column(Integer, primary_key=True)
    unit = Column(String(16), unique=True)

    def __init__(self, unit):
        self.unit = unit

class Price(Base):
    __tablename__ = 'Price'

    id = Column(BigInteger, primary_key=True)

    currency_id = Column(Integer, ForeignKey("Currency.id"), nullable=False)
    currency = relationship("Currency", backref="Currency.id")

    hour1 = Column(Float)
    hour2 = Column(Float)

    def __init__(self, hour1, hour2):
        self.hour1 = hour1
        self.hour2 = hour2

目前,我正在使用以下代码填充数据库:

脚本.py

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker

from model import *

engine = create_engine('sqlite:///example.db', echo=True)

db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
session = db_session()

Base.metadata.create_all(engine)

oPrice = Price(2.5, 2.5)
oPrice.currency = Currency("EUR")
session.add(oPrice)

tPrice = Price(5.5, 1.5)
tPrice.currency = Currency("EUR")
session.add(tPrice)

session.commit()

这会产生错误

sqlalchemy.exc.IntegrityError: (IntegrityError) column unit is not unique u'INSERT INTO "Currency" (unit) VALUES (?)' ('EUR',)

填充我的数据库的最佳策略是什么,以确保我的 Currency.id 和 Price.currency_id 映射正确?我是否应该让模型类在初始化之前寻找唯一性,我是否应该与另一个表相关联?

4

2 回答 2

2

最简单的解决方案是在 Currency 中使用货币代码作为主键,在 Price 中使用外键。然后你可以拥有

price.currency_id = "EUR"

这也使您的数据库表更具可读性 - 因为您不会有 28342 而是“GBP”。

于 2013-08-02T17:56:08.117 回答
2

我会支持 Antti 的建议,因为货币具有“INR”、“USD”等标准代码,您可以将 currency_code 作为主键。

或者,如果您想保留数字主键,那么其中一个选项是: http ://www.sqlalchemy.org/trac/wiki/UsageRecipes/UniqueObject

编辑(根据上面链接中的配方添加示例,带有类 decoartor 的示例)

数据库.py

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker

engine = create_engine('sqlite:///example.db', echo=True)
db_session = scoped_session(sessionmaker(autocommit=False,
                                     autoflush=False,
                                     bind=engine))

模型.py

from sqlalchemy import Column, Integer, String
from sqlalchemy import Float, BigInteger, ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base

from database import db_session

Base = declarative_base()

def _unique(session, cls, hashfunc, queryfunc, constructor, arg, kw):
    cache = getattr(session, '_unique_cache', None)
    if cache is None:
        session._unique_cache = cache = {}

    key = (cls, hashfunc(*arg, **kw))
    if key in cache:
        return cache[key]
    else:
        with session.no_autoflush:
            q = session.query(cls)
            q = queryfunc(q, *arg, **kw)
            obj = q.first()
            if not obj:
                obj = constructor(*arg, **kw)
                session.add(obj)
        cache[key] = obj
        return obj

def unique_constructor(scoped_session, hashfunc, queryfunc):
    def decorate(cls):
        def _null_init(self, *arg, **kw):
            pass
        def __new__(cls, bases, *arg, **kw):
            # no-op __new__(), called
            # by the loading procedure
            if not arg and not kw:
                return object.__new__(cls)

            session = scoped_session()

            def constructor(*arg, **kw):
                obj = object.__new__(cls)
                obj._init(*arg, **kw)
                return obj

            return _unique(
                    session,
                    cls,
                    hashfunc,
                    queryfunc,
                    constructor,
                    arg, kw
               )

      # note: cls must be already mapped for this part to work
        cls._init = cls.__init__
        cls.__init__ = _null_init
        cls.__new__ = classmethod(__new__)
        return cls

    return decorate




@unique_constructor(
db_session,
lambda unit: unit,
lambda query, unit: query.filter(Currency.unit == unit)
)
class Currency(Base):
    __tablename__ = 'Currency'

    id = Column(Integer, primary_key=True)
    unit = Column(String(16), unique=True)

    def __init__(self, unit):
        self.unit = unit

class Price(Base):
    __tablename__ = 'Price'

    id = Column(BigInteger, primary_key=True)

    currency_id = Column(Integer, ForeignKey("Currency.id"), nullable=False)
    currency = relationship("Currency", backref="Currency.id")

    hour1 = Column(Float)
    hour2 = Column(Float)

    def __init__(self, hour1, hour2):
        self.hour1 = hour1
        self.hour2 = hour2

脚本.py:

from model import *
from database import engine, db_session as session

Base.metadata.create_all(engine)

oPrice = Price(2.5, 2.5)
oPrice.currency = Currency("EUR")
session.add(oPrice)

tPrice = Price(5.5, 1.5)
tPrice.currency = Currency("EUR")
session.add(tPrice)

session.commit()
于 2013-08-02T19:08:39.160 回答