title: Flask-SQLAlchemy Data Model Relationship Problems
body: <p>I'm trying to create a system where a <strong>Person</strong> is part of one <strong>WorkGroup</strong>, and each <strong>WorkGroup</strong> contains many <strong>Person</strong> s. <strong>Person</strong> also contains a list of previously-matched <strong>Person</strong> s. Below is what I have so far, and I'm able to load test names into it successfully. However, when, in the <strong>findPartner</strong> method, I try to assign a new <strong>WorkGroup</strong> to the <strong>Person workGroup</strong> field like this:</p>
  
  <pre><code>self.workGroup = WorkGroup(self, newPartner)
  </code></pre>
  
  <p>I get the error message:</p>
  
  <p><code>AttributeError: 'list' object has no attribute '_sa_instance_state'</code></p>
  
  <p>Which baffles me. (I've included a more complete stack trace after the listing). Hoping someone can point out the error (I've only been using SQLAlchemy for about 24 hours so I suspect it's something basic).</p>
  
  <pre><code>def sanitize(first, last):
      def san(nm):
          return " ".join([part.strip().capitalize().replace(",", "") 
                           for part in nm.split()])
      return san(first) + " " + san(last)
  
  
  class Person(db.Model):
      __tablename__ = 'person'
      id = db.Column(db.Integer, primary_key=True)
      name = db.Column(db.String(80), unique=True)
      workGroup = db.relationship("WorkGroup", uselist=False)
      prevPairings_id = db.Column(db.Integer, db.ForeignKey('person.id'))
      prevPairings = db.relationship('Person')
  
      def __init__(self, first, last):
          self.name = sanitize(first, last)
          self.workGroup = None
          self.prevPairings = []
  
      def __repr__(self):
          return self.name
  
      def findPartner(self, amongPeople):
          if self.workGroup:
              return # You already have a group
          candidates = [p for p in amongPeople if p is not self and 
                        p not in self.prevPairings and not p.workGroup]
          assert candidates
          newPartner = candidates[0] # Just choose the first available
          self.prevPairings.append(newPartner)
          newPartner.prevPairings.append(self)
          self.workGroup = WorkGroup(self, newPartner)
          newPartner.workGroup = self.workGroup
  
      def show(self):
          print "&lt;Person %r %r %r %r&gt;" % (self.id, self.name, self.prevPairings, self.workGroup)
  
  
  class WorkGroup(db.Model):
      __tablename__ = 'workgroup'
      id = db.Column(db.Integer, primary_key=True)
      members_id = db.Column(db.Integer, db.ForeignKey('person.id'))
      members = db.relationship('Person')
  
      def __init__(self, *partners):
          self.members = list(partners)
  
      def include(self, newMember):
          assert isinstance(newMember, Person)
          self.members.append(newMember)
  
      def group(self):
          return set(self.members)
  
      def __nonzero__(self):
          return bool(self.members)
  
      def __iter__(self):
          return iter(self.members)
  
      def clear(self):
          self.members = []
  
      def __str__(self): return str(self.members)
      def __repr__(self): return self.__str__()
  
  
  ############################################################
  
  def generateNewPairings():
      all = Person.query.all()
      if max([len(p.prevPairings) for p in all]) &gt; len(all) - 2:
          print "All Pairings Exhausted"
          for p in all:
              p.prevPairings = []
      for p in all:
          p.workGroup = None
  
      def pairUp(everyone):
          if len(everyone) % 2: # Odd number
              oddOne = random.choice(everyone)
              everyone.remove(oddOne)
              pairUp(everyone) # Recurse with even number
              random.choice(everyone).workGroup.include(oddOne)
              return
          for p in everyone: # Even number
              p.findPartner(everyone)
  
      pairUp(all)
      for p in all: print p.show()
  </code></pre>
  
  <p><strong>Here's the complete stack trace:</strong></p>
  
  <pre><code>File "C:\Python27\lib\site-packages\flask\app.py", line 1701, in __call__
  return self.wsgi_app(environ, start_response)
  File "C:\Python27\lib\site-packages\flask\app.py", line 1689, in wsgi_app
  response = self.make_response(self.handle_exception(e))
  File "C:\Python27\lib\site-packages\flask\app.py", line 1687, in wsgi_app
  response = self.full_dispatch_request()
  File "C:\Python27\lib\site-packages\flask\app.py", line 1360, in full_dispatch_request
  rv = self.handle_user_exception(e)
  File "C:\Python27\lib\site-packages\flask\app.py", line 1358, in full_dispatch_request
  rv = self.dispatch_request()
  File "C:\Python27\lib\site-packages\flask\app.py", line 1344, in dispatch_request
  return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 242, in generate_pairs
  generateNewPairings()
  File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 120, in generateNewPairings
  pairUp(all)
  File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 114, in pairUp
  pairUp(everyone) # Recurse with even number
  File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 118, in pairUp
  p.findPartner(everyone)
  File "C:\Users\Bruce Eckel\Dropbox\AtomicScala\_AtomicScalaSeminarLocalServer\AtomicScalaSeminarSQLAlchemy.py", line 63, in findPartner
  self.workGroup = WorkGroup(self, newPartner)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 238, in __set__
  instance_dict(instance), value, None)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 736, in set
  value = self.fire_replace_event(state, dict_, value, old, initiator)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 756, in fire_replace_event
  value = fn(state, value, previous, initiator or self)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\unitofwork.py", line 89, in set_
  sess._save_or_update_state(newvalue_state)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\session.py", line 1389, in _save_or_update_state
  halt_on=self._contains_state):
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1928, in cascade_iterator
  parent_dict, visited_states, halt_on))
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\properties.py", line 929, in cascade_iterator
  get_all_pending(state, dict_)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 693, in get_all_pending
  ret = [(instance_state(current), current)]
  AttributeError: 'list' object has no attribute '_sa_instance_state'
  </code></pre>