很抱歉,如果这是一个新手问题,但有关many-to-one relationship的文档似乎并未涉及此问题。我一直在寻找类似于this的东西(在“如何插入/向表中添加数据”部分下),但是在所示的示例中,这始终是唯一的插入。
基本上,我想用我本地计算机上的数据填充数据库。为了简单起见,我将下面显示的示例构建到一个 MWE 中,以说明问题。该问题由两个名为Price
和Currency
的表组成,实现以声明方式完成。
的 model.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
目前,我使用以下代码填充数据库:
script.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映射是正确的?我应该让模型类在初始化之前寻找唯一性吗?我是否与其他表关联?
答案 0 :(得分:2)
最简单的解决方案是使用货币代码作为货币的主键,使用Price中的外键。那么你可以拥有
price.currency_id = "EUR"
这也使你的数据库表更具可读性 - 就像你不会有28342而是'GBP'。
答案 1 :(得分:2)
我认为Antti建议的是因为货币有标准代码,如'INR','USD'等,你可以将currency_code作为主键。
或者如果您想保留数字主键,则其中一个选项是: http://www.sqlalchemy.org/trac/wiki/UsageRecipes/UniqueObject
编辑(根据上面链接中的配方添加示例,带有decoartor类的配方)
database.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))
model.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
script.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()