pony icon indicating copy to clipboard operation
pony copied to clipboard

MappingError: Different column names should be specified for attributes User.followers and User.followers

Open nadermx opened this issue 9 years ago • 3 comments

I'm currently trying to do a followers/following twitter set up. I thought I just needed to do a self refrence but seems with postgres I am getting a error

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/gunicorn/arbiter.py", line 473, in spawn_worker
    worker.init_process()
  File "/usr/lib/python2.7/dist-packages/gunicorn/workers/base.py", line 100, in init_process
    self.wsgi = self.app.wsgi()
  File "/usr/lib/python2.7/dist-packages/gunicorn/app/base.py", line 115, in wsgi
    self.callable = self.load()
  File "/usr/lib/python2.7/dist-packages/gunicorn/app/wsgiapp.py", line 33, in load
    return util.import_app(self.app_uri)
  File "/usr/lib/python2.7/dist-packages/gunicorn/util.py", line 362, in import_app
    __import__(module)
  File "/home/www/youtstream/app.py", line 20, in <module>
    from models import User, Video
  File "/home/www/youtstream/models.py", line 68, in <module>
    db.generate_mapping(create_tables=True)
  File "<auto generated wrapper of generate_mapping() function>", line 2, in generate_mapping
  File "/usr/local/lib/python2.7/dist-packages/pony/utils.py", line 65, in cut_traceback
    return func(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/pony/orm/core.py", line 770, in generate_mapping
    'Different column names should be specified for attributes %s and %s' % (attr, reverse))
  File "/usr/local/lib/python2.7/dist-packages/pony/utils.py", line 103, in throw
    raise exc
MappingError: Different column names should be specified for attributes User.followers and User.followers

This is what I currently have as the model

class User(db.Entity):
    id = PrimaryKey(int, auto=True)
    photo_url = Optional(unicode)
    username = Required(unicode, 20, unique=True)
    name = Optional(unicode, 255)
    email = Required(unicode, 255, unique=True)
    password_hash = Optional(unicode, 255)
    videos = Set(lambda: Video)
    verified = Required(bool, default=False)
    premium = Required(bool, default=False)
    followers = Set(lambda: User, reverse="followers")
    bio = Optional(unicode, 255)
    gifs = Set(lambda: Gif)
    newsletter = Optional(unicode, 255)
    created_at = Required(datetime, default=datetime.now())

    def hash_password(self, password):
        self.password_hash = pwd_context.encrypt(password)

    def verify_password(self, password):
        return pwd_context.verify(password, self.password_hash)

    def generate_auth_token(self):
        s = Serializer(current_app.config['SECRET_KEY'])
        return s.dumps({ 'id': self.id })

    def generate_auth_email_token(self):
        s = Serializer(current_app.config['SECRET_KEY'], expires_in = 60 * 60 * 24)
        return s.dumps({ 'id': self.id })

    @staticmethod
    def verify_auth_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except SignatureExpired:
            return None # valid token, but expired
        except BadSignature:
            return None # invalid token
        if not data['id']:
            return None
        user = User[data['id']]
        return user

class Video(db.Entity):
    video_id = Required(unicode)
    users = Set(lambda: User)

class Gif(db.Entity):
    name = Required(unicode)
    gif_id = Required(unicode)
    users = Set(lambda: User)


sql_debug(True)
db.generate_mapping(create_tables=True)

nadermx avatar Apr 08 '16 22:04 nadermx

Thanks for the reporting, this is a bug. As a workaround, please replace the line

    followers = Set(lambda: User, reverse="followers")

with the next line:

    followers = Set("User", reverse="followers")

As a side note, I'm not sure that followers attribute should be defined as specified. When the attribute is specified as a reverse attribute to itself, this means that the attribute is symmetrical, that is, when you add John as a follower to Mike, Mike becomes follower of John automatically. But probably this is not what you want. It is more traditional to define following relation in the next way:

class User(db.Entity):
    ...
    followers = Set(lambda: User, reverse='followee')
    followee = Set(lambda: User, reverse='followers')

This way it will be possible to user to not follow its followers.

kozlovsky avatar Apr 09 '16 07:04 kozlovsky

Thank you, I ended up doing the followee and followers, but i suppose glad I found a bug in the process.

nadermx avatar Apr 14 '16 07:04 nadermx

Sorry for updating this ancient issue! But where is the bug exactly? It seems to work as intended, has it gotten fixed?

Sajadrahimi avatar Jul 30 '21 16:07 Sajadrahimi