Why is my SQLalchemy database having attribute errors [duplicate] - flask

I have two very simple models. In my Post model there are supposed to be two relationships into the User table. One is for the owner of the post and one is for the last editor of the post. They can be different values, but both refer to the same User table.
My models are set up like this
class Post(Base):
last_editor_id = Column(BigInteger, ForeignKey('users.id'), nullable=True)
last_editor = relationship('User', backref='posts', foreign_keys=[last_editor_id])
owner_id = Column(BigInteger, ForeignKey('users.id'), nullable=False, index=True)
owner = relationship('User', backref='posts', foreign_keys=[owner_id])
class User(Base):
'''This represents a user on the site'''
__tablename__ = 'users'
id = Column(BigInteger, primary_key=True, unique=True)
name = Column(BigInteger, nullable=False)
When I attempt to create these models though, I get the following error
sqlalchemy.exc.ArgumentError: Error creating backref 'posts' on relationship 'Post.owner': property of that name exists on mapper 'Mapper|User|users'
How do I correct this so that I can maintain both forgeign keys in the Post model?

The error is telling you that you've used post as a name more then once for your backrefs, all you need to do is give the backref's unique names. Here's a complete example-- I've added a id primary key to the Post class, and also some __repr__s so we get some readable output.
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, BigInteger, ForeignKey, Integer
from sqlalchemy.orm import relationship, sessionmaker
Base = declarative_base()
engine = create_engine('sqlite://') ## In Memory.
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()
class Post(Base):
__tablename__ = 'post'
id = Column(Integer, primary_key=True)
last_editor_id = Column(BigInteger, ForeignKey('users.id'), nullable=True)
last_editor = relationship('User', backref='editor_posts', foreign_keys=[last_editor_id])
owner_id = Column(BigInteger, ForeignKey('users.id'), nullable=False, index=True)
owner = relationship('User', backref='owner_posts', foreign_keys=[owner_id])
def __repr__(self):
return '<Post: {}>'.format(self.id)
class User(Base):
'''This represents a user on the site'''
__tablename__ = 'users'
id = Column(BigInteger, primary_key=True, unique=True)
name = Column(BigInteger, nullable=False)
def __repr__(self):
return '<User: {}>'.format(self.name)
Base.metadata.create_all(engine)
bob = User(name='Bob', id=1)
alice = User(name='Alice', id=2)
post = Post(owner=alice, last_editor=bob, id=1)
session.add(post)
session.commit()
bob = session.query(User).get(1)
print bob
# <User: Bob>
print bob.editor_posts
# [<Post: 1>]
print bob.owner_posts
# []
post = session.query(Post).get(1)
print post.owner
# <User: Alice>
print post.last_editor
# <User: Bob>
Now when you query a user, you can ask that object user.owner_posts or user.editor_posts.

In general it's a naming Problem of the backref.
Since 1:n relationships are sometimes a bit confusing, I set the relationship attribute
always on the singular site, to avoid confusion.
then the backref name is always singular. and the relationship attribute is always in the Class where the foreignkey is referencing to.
Now to my suggestion for the fixed code:
class Post(Base):
last_editor_id = Column(BigInteger, ForeignKey('users.id'), nullable=True)
owner_id = Column(BigInteger, ForeignKey('users.id'), nullable=False, index=True)
class User(Base):
'''This represents a user on the site'''
__tablename__ = 'users'
id = Column(BigInteger, primary_key=True, unique=True)
name = Column(BigInteger, nullable=False)
owned_posts = relationship('Post', backref='owner')
edited_posts = relationship('Post', backref='last_editor')
Now you can get all the owned posts of a User with User.owned_posts and all owners of a post with Post.owner. Same with the last_edited attribute.
For additional info you could read the docs how to set up relationships

Related

SqlAlchemy AttributeError: 'category' object has no attribute 'subcategories'

I am using python (django) with sqlalchemy. I created these two paintings related to each other.
def CreateDynamicSubCategories():
try:
category_subcategory = Table('category_subcategory', Base.metadata,
Column('category_id', Integer, ForeignKey('category.id')),
Column('subcategory_id', Integer, ForeignKey('subcategory.id')),
__table_args__ = {'extend_existing': True}
)
except Exception as e:
GetErrorDetails(e)
class Category(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'category'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
subcategories = relationship("Subcategory", secondary=category_subcategory, back_populates="categories")
products = relationship("Product", back_populates="category")
class Subcategory(Base):
__table_args__ = {'extend_existing': True}
__tablename__ = 'subcategory'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
categories = relationship("Category", secondary=category_subcategory, back_populates="subcategories")
products = relationship("Product", back_populates="subcategory")
The problem is that I wanted to add a new table with the following codes:
Session = sessionmaker(bind=engine)
session = Session()
# Adding category
new_category = Category(name="Electronics")
session.add(new_category)
session.commit()
new_subcategory = Subcategory(name="Smartphones")
session.add(new_subcategory)
session.commit()
new_category.subcategories.append(new_subcategory)
session.commit()
new_subcategory.categories.append(new_category)
session.commit()
I encounter an error like this:
new_category.subcategories.append(new_subcategory) AttributeError: 'category' object has no attribute 'subcategories'
What is the problem here? Can someone who knows please help?
I continued to get the same mistakes even after the chatgpt help. Can someone who knows the problem say?

Flask-alchemy many to many relationship between separate packages

I have two tables Member and Posts. Respective classes are declared into separate models.py modules, that belong to separate packages members and posts.
post package - models .py
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer(), primary_key=True)
subject = db.Column(db.Text, nullable=False, unique=True, index=True)
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
category_id = db.Column(db.Integer(), db.ForeignKey('categories.id'))
tags = db.relationship('Tag', secondary=post_tags, lazy='subquery', backref=db.backref('post'))
authors = db.relationship('Member', secondary=member_posts, lazy='subquery', backref=db.backref('post'))
def __init__ (self,**kwargs):
super(Post, self).__init__(**kwargs)
members package - models .py
class Member(db.Model):
__tablename__ = 'members'
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(50, collation='NOCASE'), nullable=False)
last_name = db.Column(db.String(50, collation='NOCASE'), nullable=False)
phone = db.Column(db.String(15))
twitter = db.Column(db.String(15))
avatar = db.Column(db.String(255), default='avatar.png')
# Relationships
field = db.relationship('Field', secondary=members_fields, lazy='subquery', backref=db.backref('member'))
position = db.relationship('Position', secondary=members_positions, lazy='subquery', backref=db.backref('member'))
def __init__ (self,**kwargs):
super(Member, self).__init__(**kwargs)
Those tables are connected with Many to Many relationship, and associative table member_posts is located along with posts class.
posts package - models .py
member_posts = db.Table('member_posts',
db.Column('member_id', db.Integer, db.ForeignKey('members.id'), primary_key=True),
db.Column('post_id', db.Integer, db.ForeignKey('posts.id'), primary_key=True)
)
When I send send request, debugger throws the following error:
sqlalchemy.exc.InvalidRequestError: When initializing mapper mapped class Post->posts, expression 'Member' failed to locate a name ('Member'). If this is a class name, consider adding this relationship() to the <class 'eierc.posts.models.Post'> class after both dependent classes have been defined.
Can anyone explain, hat is the problem and how to fix it?

How to combine and match 2 tables in Flask

The id is associated with the postID. How can I get the contact information and comment information of that id when I enter the id(/postComments/id)?
I'm getting an internal error...
class posts(db.Model):
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
postName = db.Column(db.String(100))
postDescription = db.Column(db.String(500))
postLike = db.Column(db.Integer)
class postComment(db.Model):
__tablename__ = "postComment"
id = db.Column(db.Integer, primary_key=True)
postID = db.Column(db.Integer)
senderName = db.Column(db.String(20))
commentPost = db.Column(db.String(300))
#app.route('/postComments/<id>',methods=['GET'])
def get_comment_post(id):
userList = posts.query\
.join(postComment, posts.id == postComment.postID)\
.add_columns(posts.id, posts.name, posts.postDescription, postComment.commentPost, postComment.senderName)\
.filter(posts.id == id)
Modify your models (and perform migrations) to allow reference for foreign keys:
class postComment(db.Model):
__tablename__ = "postComment"
id = db.Column(db.Integer, primary_key=True)
postID = db.Column(db.Integer, db.ForeignKey("posts.id")) # <--- Set to Foreign Key
senderName = db.Column(db.String(20))
commentPost = db.Column(db.String(300))
# Establish relationship
post = db.Relationship("posts", backref="postComment") # <--- backref makes this relationship available in the other class
# This should get simpler...
#app.route('/postComments/<id>',methods=['GET'])
def get_comment_post(id):
# Get the comment based on id
my_comment = postComment.query.get(id)
# Get the post associated with that comment
my_post = my_comment.post
# It looks like you're doing an intricate data transformation.
# Do that here...
# Also, for debugging, consider using the following print statements
print(my_comment.__dict__)
print(my_post.__dict__)
Consider reading this slightly more detailed explanation for establishing relationships.

Flask: Querying inherited classes from SQLAlchemy brings different results in two similar contexts

I am working on a project with two different users: client, employee. Clients post and Employees confirm the posts or not. To do this I have on User class which deals mostly with authentication and basic procedures, and both the Client and Employee class inherit from it.
Practically:
class User(UserMixin, db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
user_type = db.Column(db.String(32), nullable=False)
__mapper_args__ = {'polymorphic_identity': user_type}
class Client(User):
__tablename__ = 'client'
__mapper_args__ = {'polymorphic_identity':'client'}
id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
telephone = db.Column(db.String(120))
birthday = db.Column(db.String(120))
posts = db.relationship('Post', backref='author', lazy='dynamic')
class Employee(User):
__tablename__ = 'employee'
__mapper_args__ = {'polymorphic_identity':'employee'}
id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
employee_extra = db.Column(db.String(140))
When I want to get all the clients in one of my routes which is something like:
#bp.route('/posts', methods=['GET', 'POST'])
#login_required
def posts():
clients = Client.query.all()
print(clients[0].birthday)
I get results like: AttributeError: 'User' object has no attribute 'birthday'. because the result is [<User 2>].
On the other hand, when I set up an environment for testing, and I run the same query.
db_app.setUp()
# self.app = create_app(Config)
# self.app_context = self.app.app_context()
# self.app_context.push()
# db.create_all()
# self.client = self.app.test_client()
# db.session.commit()
db_app.create_user('11123123123','user#gmail.com','123123123','client')
print(Client.query.all())
It brings the necessary results: [<Client 2>]
I have read on different sources, and main documentation of SQLAlchemy, but cant really see where this difference comes from. What am I missing?

How to record to record to a database with one-to-many relationship?

I am trying to do a small project, but have some difficulties around database structure in SQLAlchemy. I have two database models (code below).
User model is created and added to the database during the registration form.
Then I need to find a way to add Clients for loggedin user from his profile.
Question:
How do I do that? I need to create a record specifically for the logged-in user. So that when I display them (his clients)- they are only displayed to that specific user.
class User(UserMixin, db.Model):
tablename = 'user'
id = db.Column(db.Integer, primary_key=True)
company_name = db.Column(db.String(120), index=True, unique=False)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
clients = db.relationship('Client', backref='invoice_clients', lazy='dynamic')
class Client(db.Model):
tablename = 'client'
id = db.Column(db.Integer, primary_key=True)
client_name = db.Column(db.String(140))
client_company = db.Column(db.String(140))
client_email = db.Column(db.String(140))
invoice_amount = db.Column(db.Integer)
service_description = db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
I expect that the when a user adds a client from his profile page - it will be recorded in the database only for him.
First of all, I believe you have the backref parameter backwards. That is the property you can use on the referenced objects to "get back" to the current class. So it should probably look like this:
class User(UserMixin, db.Model):
__tablename__ = 'user'
clients = db.relationship('Client', backref='user', lazy='dynamic')
As to your question about adding clients for a specific user, there are two ways you can approach it. You can do it manually:
# Or flask_login, or whatever you're using for user management
from flask_security import current_user
new_client = Client(client_name="Lancelot", user_id=current_user.id)
db.session.add(new_client)
db.session.commit()
Or you can simply use SQLAlchemy to append the client to your user. The declared foreign key will cause the id to be assigned automatically:
from flask_security import current_user
new_client = Client(client_name="Sir Bedivere")
current_user.clients.append(new_client)
db.session.commit()