Thank you Mike for this detailled response !

The UniqueObject recipe is very interesting, but not very suitable to my 
case because my webservice don't use the same session on each Post 
insertion.

Maybe a session.merge_all() method can help to improve performance in such 
cases by grouping the underlying SQL queries (only one SELECT and one 
INSERT) for all instances.


Le mardi 23 mai 2017 19:48:11 UTC+3, Mike Bayer a écrit :
>
> we don't have ON DUPLICATE KEY UPDATE for MySQL as of yet however there 
> is a PR that I will attend to at some point for possible 1.2 inclusion. 
>
> However, note you can just as well just emit the SQL string for ON 
> DUPLCIATE KEY UPDATE if you're only targeting MySQL.   Then just create 
> the object you need that corresponds to this row, then use 
> make_transient_to_detached() -> session.add() to make it happen (see 
> example below). 
>
> Theoretically, the functionality of session.merge() could provide an 
> alternate form that makes use of ON DUPLICATE KEY UPDATE automatically 
> however for the foreseeable future, you'd need to roll this yourself 
> once you make use of the SQL statement. 
>
> The traditional way to handle this is largely like what you have except 
> that client-side caching is used to make it more efficient.   One 
> example is 
> https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/UniqueObject, 
> which caches per single object.   Other ways include just selecting a 
> whole batch of objects based on keys you know you will be working with 
> up front. 
>
>
> from sqlalchemy import * 
> from sqlalchemy.orm import * 
> from sqlalchemy.ext.declarative import declarative_base 
>
> Base = declarative_base() 
>
> post_tags = Table( 
>      "post_tags", Base.metadata, 
>      Column("postid", ForeignKey('post.id'), primary_key=True), 
>      Column("tagid", ForeignKey('tag.key'), primary_key=True) 
> ) 
>
>
> class Post(Base): 
>      __tablename__ = 'post' 
>
>      id = Column(Integer, primary_key=True) 
>      _tags = relationship('Tag', secondary='post_tags') 
>
>      @property 
>      def tags(self): 
>          return ';'.join(tag.key for tag in self._tags) 
>
>      @tags.setter 
>      def tags(self, s): 
>          lst = [Tag(key=tag) for tag in s.split(';')] 
>          self._tags = lst 
>
>
> class Tag(Base): 
>      __tablename__ = 'tag' 
>
>      key = Column(String(40), primary_key=True) 
>      count = Column(Integer) 
>
> e = create_engine("mysql://scott:tiger@localhost/test", echo=True) 
> Base.metadata.drop_all(e) 
> Base.metadata.create_all(e) 
> s = Session(e) 
>
>
> def make_tag(name): 
>      s.execute( 
>          "INSERT INTO tag (`key`, `count`) VALUES (:name, 1) " 
>          "ON DUPLICATE KEY UPDATE count = count + 1", 
>          {"name": name} 
>      ) 
>      tag_obj = Tag(key=name) 
>      make_transient_to_detached(tag_obj) 
>      return s.merge(tag_obj, load=False) 
>
>
> s.add(Post(_tags=[make_tag("tag_a")])) 
> s.commit() 
>
> s.add(Post(_tags=[make_tag("tag_a")])) 
> s.commit() 
>
> assert s.query(Tag).count() == 1 
>
>
>
> On 05/22/2017 09:59 AM, yoch....@gmail.com <javascript:> wrote: 
> > Hi, 
> > 
> > I'm trying to create a tag system with a many-to-many relationship 
> > approach, and I have problems with the updating phase. 
> > 
> > | 
> > classPost(Base): 
> >      __tablename__ ='post' 
> > 
> >      id =Column(Integer,primary_key=True) 
> >      _tags =relationship('Tag',secondary='post_tags') 
> > 
> > @property 
> > deftags(self): 
> > return';'.join(tag.key fortag inself._tags) 
> > 
> > @tags.setter 
> > deftags(self,s): 
> >          lst =[Tag(key=tag)fortag ins.split(';')] 
> > self._tags =lst 
> > 
> > classTag(Base): 
> >      __tablename__ ='tag' 
> > 
> >      key =Column(String(40),primary_key=True) 
> >      count =Column(Integer) 
> > | 
> > 
> > 
> > And the `post_tags` is defined as : 
> > 
> > | 
> > CREATE TABLE `post_tags`( 
> > `idpost`INT(10)UNSIGNED NOT NULL, 
> > `tag`VARCHAR(40)NOT NULL, 
> >   PRIMARY KEY (`idpost`,`tag`), 
> >   INDEX `FK_post_tags`(`tag`), 
> >   FOREIGN KEY (`tag`)REFERENCES `tag`(`key`), 
> >   FOREIGN KEY (`idpost`)REFERENCES `post`(`id`)ON DELETE CASCADE 
> > ); 
> > | 
> > 
> > 
> > The problem is that when adding tags that already exists in the `tag` 
> table 
> > 
> > | 
> > post.tags ='a'# tag 'a' already created 
> > | 
> > 
> > it produces this exception : sqlalchemy.orm.exc.FlushError: New instance 
> > <Tag at 0x7f7b56a3a940> with identity key (<class 'test_mm.Tag'>, 
> > ('a',)) conflicts with persistent instance <Tag at 0x7f7b567af7f0> 
> > 
> > 
> > Ideally, I'd like to be able to produce a query like (MySQL) : 
> > 
> > | 
> > INSERT INTO tag (`key`,`count`)VALUES (%s,1)ON DUPLICATE KEY UPDATE 
> > count =count +1 
> > | 
> > 
> > Instead that, the way I found to do seems much less efficient : 
> > 
> > | 
> > @tags.setter 
> > deftags(self,s): 
> >      sess =object_session(self) 
> >      lst =[sess.merge(Tag(key=tag))fortag ins.split(';')] 
> > self._tags =lst 
> > | 
> > 
> > (and the counter is managed by a trigger on `post_tags` INSERT) 
> > 
> > Is there another way to do that properly and efficiently ? 
> > 
> > -- 
> > SQLAlchemy - 
> > The Python SQL Toolkit and Object Relational Mapper 
> > 
> > http://www.sqlalchemy.org/ 
> > 
> > To post example code, please provide an MCVE: Minimal, Complete, and 
> > Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
> > description. 
> > --- 
> > You received this message because you are subscribed to the Google 
> > Groups "sqlalchemy" group. 
> > To unsubscribe from this group and stop receiving emails from it, send 
> > an email to sqlalchemy+...@googlegroups.com <javascript:> 
> > <mailto:sqlalchemy+unsubscr...@googlegroups.com <javascript:>>. 
> > To post to this group, send email to sqlal...@googlegroups.com 
> <javascript:> 
> > <mailto:sqlal...@googlegroups.com <javascript:>>. 
> > Visit this group at https://groups.google.com/group/sqlalchemy. 
> > For more options, visit https://groups.google.com/d/optout. 
>

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to