我试图解析一些RSS提要并使用URL将URL写入数据库flask-sqlalchemy
.
其中一些Feed重叠(即同一篇文章出现在多个Feed中或多次出现在同一个Feed中),因此我将主键定义为URL的(非随机)哈希值.
我的问题是,当我循环遍历URL并将它们添加到Session时,我尝试将它们提交到数据库时遇到异常.
这是我的代码:
for entry in feed.entries: # Create the database row article = Article(entry) # If the row already exists in the database if db.session.query(Article).filter_by(uuid=article.uuid).first(): print "duplicate" else: db.session.merge(article) db.session.commit()
当文章已存在于数据库中时,将忽略该文章.但是,如果它存在于Session中但尚未提交数据库,那么SQLAlchemy会尝试在同一事务中多次写入它,我得到了sqlalchemy.exc.IntegrityError: (IntegrityError) column hash is not unique
.
我的直觉是我需要检查会话中是否已存在具有该哈希的对象(以及查询数据库),我认为这是合并会做的,但我仍然得到错误.
您应该使用示例中的唯一对象模式.
这将为会话创建内存缓存,并保证一个唯一的结果.如果实例在缓存中,请使用它,否则尝试从数据库中获取它.如果它不在数据库中,请创建它.将实例添加到缓存中.
这是我对模式的看法,它简化了链接的例子.
class UniqueMixin(object): @classmethod def get_unique(cls, **kwargs): session = current_app.extensions['sqlalchemy'].db.session session._unique_cache = cache = getattr(session, '_unique_cache', {}) key = (cls, tuple(kwargs.items())) o = cache.get(key) if o is None: o = session.query(cls).filter_by(**kwargs).first() if o is None: o = cls(**kwargs) session.add(o) cache[key] = o return o class MyModel(UniqueMixin, db.Model): # ... name = db.Column(db.String, nullable=False) # ... m1 = MyModel.get_unique(name='test') # new instance m2 = MyModel.get_unique(name='test') # from cache assert m1 is m2 db.session.commit() # inserts one row m1 = MyModel.get_unique(name='test') # from database m2 = MyModel.get_unique(name='test') # from cache assert m1 is m2
在可能遇到这种情况的任何模型中继承UniqueMixin,并使用get_unique而不是普通的构造函数.