2013-06-24 45 views
3

我已经在flask-sqlalchemy中正确实施多对多关系时遇到了很多麻烦。它从重复的标签开始,现在,很多天以后是映射问题。公平的警告,我的代码在这些问题悄悄出现之前更加漂亮。我还添加了一个addproduct.py文件来加速测试。所以在这里。flask-sqlalchemy插入未映射

models.py

1 from app import app, db                                  
2 
3 product_tags = db.Table('association', 
4   db.Column('product_id', db.Integer, db.ForeignKey('product.id')), 
5   db.Column('tag_name', db.Integer, db.ForeignKey('tag.name')) 
6) 
7 
8 class Product(db.Model):                                  
9  id = db.Column(db.Integer, primary_key=True) 
10  title = db.Column(db.String(128))                              
11  description = db.Column(db.Text) 
12  image = db.Column(db.String(64)) 
13  link = db.Column(db.String(256)) 
14  price = db.Column(db.Float()) 
15  timestamp = db.Column(db.DateTime)                              
16  expiration = db.Column(db.String(6)) 
17  tags = db.relationship('Tag', secondary=product_tags,                         
18    backref=db.backref('product', lazy='dynamic')) 
19 
20  def __init__(self, title, description, image, link, price, timestamp, expiration, tags):                 
21   self.title = title                                 
22   self.description = description                              
23   self.image = image                                 
24   self.link = link                                  
25   self.price = price                                 
26   self.timestamp = timestamp 
27   self.expiration = expiration                               
28   self.tags = tags                                  
29   print self.title                                  
30 
31  def __repr__(self):                                  
32   return '<Title %r, Description %r, Image %r, Link %r, Price %r, Timestamp %r, Expires %r, Tags %r>' % (self.title, self.description, self.image, sel 
33 
34 class Tag(db.Model):                                   
35  name = db.Column(db.String(32), primary_key=True) 
36 
37  def __init__(self, name): 
38   self.name = name                                  
39 
40  def __repr__(self):                                  
41   return '<Tag %r>' % self.name 

addproduct.py

1 from app import db                                   
2 from app.models import Product, Tag, product_tags 
3 from datetime import datetime                                
4 
5 imagefolder = 'static/img/' 
6 
7 title = 'product' 
8 description = 'description' 
9 image = 'image.jpg' 
10 link = 'http://link.com' 
11 price = 2000.00 
12 expiration = '' 
13 tags = ['tag1','tag2']                                
14 
15 newtags = []                                     
16 
17 def create_product(title, description, image, link, price, expiration, tags):                    
18  image = imagefolder + image                                
19  tag_assoc = [] 
20  for tag in tags:                                   
21   tagcheck = Tag.query.filter_by(name=tag).first()                          
22   if tagcheck == None:                                 
23    tag_assoc.append(Tag(tag))                              
24   else:                                    
25    newtags.append(tag)                                
26 
27  product = Product(title, description, image, link, price, datetime.utcnow(), expiration, tag_assoc)              
28  create_assoc(newtags)                                 
29  return product                                   
30 
31 def create_assoc(newtags):                                 
32  title_search = Product.query.filter_by(title=title).first()                        
33  for tag in newtags: 
34   assoc = product_tags.insert().values(product_id=title_search.id, tag_name=tag) 
35   db.session.add(assoc)                                
36  db.session.commit() 
37 
38 if __name__ == '__main__': 
39  product = create_product(title, description, image, link, price, expiration, tags) 
40  db.session.add(product)                                 
41  db.session.commit()                                  
42  create_assoc(newtags) 

该错误消息我得到的是:

11:11 ~/shop $ python addproduct.py 
product 
Traceback (most recent call last): 
File "addproduct.py", line 39, in <module> 
product = create_product(title, description, image, link, price, expiration, tags) 
File "addproduct.py", line 28, in create_product 
create_assoc(newtags) 
File "addproduct.py", line 35, in create_assoc 
db.session.add(assoc) 
File "/home/username/.local/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 114, in do 
return getattr(self.registry(), name)(*args, **kwargs) 
File "/home/username/.local/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1358, in add 
raise exc.UnmappedInstanceError(instance) 
sqlalchemy.orm.exc.UnmappedInstanceError: Class 'sqlalchemy.sql.expression.Insert' is not mapped 

这是我第一次的webapp不是直接从一个教程,和我完全迷失了。请帮忙!

回答

4

我可以看到的第一个错误是否在您的模型中 - 您的关联正确地有两个外键,但标记的外键是Integer,但您的Tag类中的主键是String - 它们应该匹配。除此之外,模型看起来不错。

你也应该能够简化您的create_product功能:

def create_product(title, description, image, link, price, expiration, tags): 
    image = imagefolder + image 
    tag_list = [] 
    for tag in tags: 
     tagcheck = Tag.query.filter_by(name=tag).first() 
     if tagcheck is None: 
      tag_list.append(Tag(tag)) 
     else: 
      tag_list.append(tagcheck) 
    product = Product(title, description, image, link, price, datetime.utcnow(), expiration, tag_list) 
    return product 

这可以简单地将__init___构造内移动。在你的例子中,你直接处理关联表,但是你根本不需要 - 只要相信ORM做正确的事情,那就是SQLAlchemy的美妙之处。

这里是您的产品型号可能是什么样子的例子:

class Product(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    title = db.Column(db.String(128)) 
    description = db.Column(db.Text) 
    image = db.Column(db.String(64)) 
    link = db.Column(db.String(256)) 
    price = db.Column(db.Float()) 
    timestamp = db.Column(db.DateTime) 
    expiration = db.Column(db.String(6)) 
    tags = db.relationship('Tag', secondary=product_tags, 
      backref=db.backref('products', lazy='dynamic')) 

    def __init__(self, title, description, image, link, price, timestamp, expiration, tags): 
     self.title = title 
     self.description = description 
     self.image = image 
     self.link = link 
     self.price = price 
     self.timestamp = timestamp 
     self.expiration = expiration 

     for tag in tags: 
      tagcheck = Tag.query.filter_by(name=tag).first() 
      if tagcheck is None: 
       self.tags.append(Tag(tag)) 
      else: 
       self.tags.append(tagcheck) 

    def __repr__(self): 
     return '<{}, {}>'.format(self.title, ":".join([x.name for x in self.tags])) 

并对其进行测试,首先让我们来添加一对夫妇的标签系统:

ta = Tag('cat') 
tb = Tag('dog') 
db.session.add_all([ta, tb]) 
db.session.commit() 

>>> Tag.query.all() 
[<Tag u'cat'>, <Tag u'dog'>] 

现在让我们来添加一个使用这些标签的产品,以及一个新的标签。

p = Product(
    'title', 
    'description', 
    'image', 
    'link', 
    0.0, 
    datetime.now(), 
    'expiry', 
    ['dog','cat','horse'] 
) 
db.session.add(p) 
db.session.commit() 

当我们创建产品,构造函数接受每个这三个字符串代码,并说:“嘿,这是否名称的标签已经存在?”如果是的话,它会使用它,如果不是,则会使用该名称创建一个新标签。 SQLAlchemy聪明到足以知道将新标签添加到会话中,并在产品提交时提交。

>>> Tag.query.all() 
[<Tag u'cat'>, <Tag u'dog'>, <Tag u'horse'>] 

现在让我们找到带有狗标签的所有产品(假设已添加更多产品)。

>>> tag = Tag.query.get('dog') 
>>> products = tag.products 
>>> [x.title for x in products] 
['title','other','examples'] 

再一次,我根本没有触及关联表,没有必要。 SQLAlchemy正在节省我们的工作量。

+0

如果我把它放在标签__init__中,标签是否会被创建?如果它是一个重复的标签,我不想创建它,我只想创建关联。另外,感谢您指出整数/字符串问题。 – BradleyMoore

+0

我尝试了您所建议的更改,但得到了错误AttributeError:“表”对象没有属性“追加”。它参考了product_tags.append。 – BradleyMoore

+1

我的意思是将它添加到产品init中,您将产品需要的标签作为字符串传入,然后产品init决定是否需要创建它们,或者是否可以重用数据库中的现有产品。另外我相信我已经修复了附加错误,在我的例子中,我有一个可变的冲突。 – Doobeh