Python 在SQLAlchemy中防止多对多关系中的重复表项

Python 在SQLAlchemy中防止多对多关系中的重复表项,python,sqlalchemy,Python,Sqlalchemy,我正在尝试使用SQLAlchemy建立一个具有多对多关系的电影数据库。我有两个表,“电影”和“演员”,以及一个关联表“电影演员”。我希望能够将新电影添加到电影表中,但是如果此新电影中的某些演员已经在演员表中,我希望在将电影id和演员id添加到关联表的同时,防止在演员表中重复这些演员。以下是我的表格设置: from sqlalchemy import Table, Column, Integer, String, ForeignKey, create_engine, and_, or_

我正在尝试使用SQLAlchemy建立一个具有多对多关系的电影数据库。我有两个表,“电影”和“演员”,以及一个关联表“电影演员”。我希望能够将新电影添加到电影表中,但是如果此新电影中的某些演员已经在演员表中,我希望在将电影id和演员id添加到关联表的同时,防止在演员表中重复这些演员。以下是我的表格设置:

   from sqlalchemy import Table, Column, Integer, String, ForeignKey, create_engine, and_, or_
   from sqlalchemy.ext.declarative import declarative_base
   from sqlalchemy.orm import backref, mapper, relationship, Session

   Base = declarative_base()

   formats = {'1': 'DVD', '2': 'Blu-ray', '3': 'Digital', '4': 'VHS'}

   ###########################################################################################
   class Movie(Base):
   """Movie Class"""

        __tablename__ = "movie"

        movie_id = Column(Integer, primary_key=True)
        title = Column(String(20), nullable=False, unique=True)
        year = Column(Integer, nullable=False)
        format = Column(String, nullable=False)
        movie_actor = relationship("MovieActor", cascade="all, delete-orphan", backref="movie")

        def __init__(self, title, year, format):
            self.title = title
            self.year = year
            self.format = format

        def __repr__(self):
            return "%s %s" % (self.movie_id, self.title)

    ###########################################################################################
    class Actor(Base):
    """Actor Class"""

        __tablename__ = "actor"

        actor_id = Column(Integer, primary_key=True)
        full_name = Column(String(30), nullable=False, unique=True)

        def __init__(self, full_name):
            self.full_name = full_name

        def __repr__(self):
            return "%s %s" % (self.actor_id, self.full_name)

    ###########################################################################################
    class MovieActor(Base):
    """MovieActor Association Class"""

        __tablename__ = "movieactor"
        movie_id = Column(Integer, ForeignKey('movie.movie_id'), primary_key=True)
        actor_id = Column(Integer, ForeignKey('actor.actor_id'), primary_key=True)

        def __init__(self, actor):
            self.actor = actor
        actor = relationship(Actor, lazy='joined')

        def __repr__(self):
            return "%s, %s" % (self.movie_id, self.actor_id)
下面是一个类,它将处理插入新条目和查询数据库:

    ###########################################################################################
    class Database(object):

    # A connection to the movie database is established upon instantiation.
        def __init__(self):
            engine = create_engine('sqlite:///bmdb.db')
            Base.metadata.create_all(engine)
            session = Session(engine)
            self.session = session

    # add_new method takes a dictionary of strings containing all the info for a new movie: "title, year, format, actors"
    # and formats the strings, then adds them to the proper tables in the database

        def add_new(self, new_movie):

            #find out what formats exist
            format = ""
            for i in range(1,5):
                    try:
                            format += new_movie[formats[str(i)]]
                            format += ", "
                    except:
                            pass

            format = format[:-1]
            format = format[:-1]

            # capitalize the first letter of each word in the movie title
            title = " ".join(word[0].upper() + word[1:].lower() for word in new_movie['title'].split())
            try:
                    movie = Movie(title, new_movie['year'], format)
                    # add the new movie to the session
                    self.session.add(movie)
                    # commit the new movie to the database
                    self.session.commit()
            except:
                    print "Duplicate Movie"
                    self.session.rollback()
                    return

            # parse the text in the actors entry
            # take the incoming string of all actors in the movie and split it into a list of individual actors

            actors = new_movie['actors'].split(", ")        
            for i in range(len(actors)):
                    # for each actor in the list, capitalize the first letter in their first and last names
                    actors[i] = " ".join(word[0].upper() + word[1:].lower() for word in actors[i].split())
                    # add each formatted actor name to the Actor table
                    actor = Actor(actors[i])
                    try:
                            # add the appropriate association between the movie and the actors to the MovieActor table
                            movie.movie_actor.append(MovieActor(actor))
                            # add the new actor and movieactor association to the session
                            self.session.add(movie)
                            self.session.commit()
                    except:
                            print "Duplicate Actor"
                            self.session.rollback()
正如我现在的代码所示,add_new()方法中的try/except块防止将重复的演员添加到数据库中,因为演员表中的“full_name”列设置为unique=True,但这也防止将条目添加到电影演员关联表中


基本上我想知道的是如何添加一部电影,检查电影中的演员是否已经存在于演员表中,如果已经存在,则不要将演员插入演员表中,但是从演员表中获取他们现有的演员id,并在电影演员关联表中创建适当的关联。

您可能需要在
块中插入
self.session.begin嵌套()
。 然后,如果由于重复关键点需要回滚,仍然可以将演员添加到电影中:

from sqlalchemy.exc import IntegrityError  # only catch the right exception!
           # in for loop:
                try:
                        session.begin_nested()
                        actor = Actor(actors[i])
                except IntegrityError:
                        print "Duplicate Actor"
                        self.session.rollback() # subtransaction
                        actor = self.session.query(Actor).\
                           filter(Actor.name==actors[i]).first()
                else:
                        self.session.commit()  # subtransaction

                # add the appropriate association between the movie and the actors to the MovieActor table
                movie.movie_actor.append(MovieActor(actor))
                # add the new actor and movieactor association to the session
                self.session.add(movie)
                self.session.commit()


编辑:当期望出现重复的键错误时,始终除外
IntegrityError

谢谢!如果使用的是关联表,而不是类,那么行
movie.movie\u actor.append(MovieActor(actor))
会是什么样子?