如何在sqlalchemy中使用多对多关系
我想在我的项目中使用sqlalchemy的关系功能。我在简单的代码上测试了多对多的关系:
from sqlalchemy import Table, Column, Integer, String, Text, DateTime, ForeignKey, create_engine
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
engine = create_engine('sqlite:///m2m.sqlite', echo=True)
Base = declarative_base(engine)
post_tags = Table('post_tags', Base.metadata,
Column('post_id', Integer, ForeignKey('blog_posts.id')),
Column('tag_name', String, ForeignKey('blog_tags.name'))
)
class BlogPost(Base):
__tablename__ = 'blog_posts'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String)
content = Column(Text)
created = Column(DateTime, default=datetime.now)
tags = relationship('BlogTag', secondary=post_tags, backref='blog_posts')
def __init__(self, title, content, tags=None):
self.title = title
self.content = content
if tags:
self.tags = tags
def __repr__(self):
return '%d %s %s' % (self.id, self.title, self.content)
class BlogTag(Base):
__tablename__ = 'blog_tags'
name = Column(String, primary_key=True)
def __init__(self, name):
self.name = name
def __repr__(self):
return '%s' % self.name
Base.metadata.create_all(engine)
Session = sessionmaker(engine)
dbs = Session()
post = BlogPost('1', '1111', [BlogTag('one'), BlogTag('two')])
dbs.add(post)
dbs.commit()
post = BlogPost('2', '222', [BlogTag('one'), BlogTag('newtag')])
dbs.add(post)
dbs.commit()
for x in dbs.query(BlogTag).all():
print x
但是在这段代码上我遇到了异常:
post = BlogPost('2', '222', [BlogTag('one'), BlogTag('newtag')])
dbs.add(post)
dbs.commit()
如果标签已经存在于表中,我该如何将新的博客文章插入到数据库里呢?
感谢大家。我为BlogTag写了以下代码:
class BlogTag(Base):
__tablename__ = 'blog_tags'
name = Column(String, primary_key=True)
@staticmethod
def get(dbsession, name):
obj = dbsession.query(BlogTag).filter(BlogTag.name == name).first()
if not obj:
obj = BlogTag(name)
return obj
def __init__(self, name):
self.name = name
def __repr__(self):
return '%s' % self.name
我像下面这样使用它们:
tags = [BlogTag.get(dbs, 'one'), BlogTag.get(dbs, 'two')]
post = BlogPost('1', '1111', tags)
dbs.add(post)
dbs.commit()
tags = [BlogTag.get(dbs, 'one'), BlogTag.get(dbs, 'newtag')]
post = BlogPost('2', '222', tags)
dbs.add(post)
dbs.commit()
我不确定这是否是最好的方法,但它确实有效 :)
我可以从Base获取会话吗?
2 个回答
3
这里的问题是,你创建了两个主键为 one
的 BlogTag
对象。
SQLAlchemy 的会话对象并不知道你不想为第二个对象创建一个新的数据库记录。
下面的代码可以正常工作:
tag_one = BlogTag('one')
post = BlogPost('1', '1111', [tag_one, BlogTag('two')])
dbs.add(post)
dbs.commit()
post = BlogPost('2', '222', [tag_one, BlogTag('newtag')])
dbs.add(post)
dbs.commit()
你也可以这样做(但要注意,dbs.query(BlogTag).get('one')
会额外向数据库执行一次查询,这在第一个例子中是不需要的):
post = BlogPost('1', '1111', [BlogTag('one'), BlogTag('two')])
dbs.add(post)
dbs.commit()
post = BlogPost('2', '222', [dbs.query(BlogTag).get("one"), BlogTag('newtag')])
dbs.add(post)
dbs.commit()
2
BlogPost('one')
这个代码会创建一个全新的对象。你需要从数据库中获取已经存在的标签。
另外,在上面的例子中,你忘记把一个新标签添加到数据库的会话中了。