Flask-SQLalchemy one-to-one relationship: associate child to already existing parent? - python-3.x

first time I use sqlalchemy and flask and I miss something
I'm trying to do a one-to-one relationship between two tables with Flask-SQLalchemy.
This is my first table (the parent)
class User(db.Model):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True, autoincrement=True)
user_email = Column(String(150), nullable=False)
user_password = Column(String(60), nullable=False)
child_employee = relationship('Employee', uselist=False, backref='users')
def __init__(self, user_email, user_password):
self.user_email = user_email
self.user_password = user_password
And the child
class Employee(User):
__tablename__ = 'employees'
employee_id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey('users.user_id'), nullable=False)
employee_lastname = Column(String(30), nullable=False)
employee_firstname = Column(String(30), nullable=False)
employee_comment = Column(String)
def __init__(self, user_email, user_password, employee_lastname, employee_firstname, employee_comment=None):
super().__init__(user_email, user_password)
self.employee_lastname = employee_lastname
self.employee_firstname = employee_firstname
self.employee_comment = employee_comment
Know when I create an user, I have a user in my table "users" and nothing in my table "employees" : That make sense, perfect
Then if I create an employee, I have a new entry in my table "employees" and a new entry in my table "users". They are linked to each other : that make also sense, perfect
Now the question :
I created an user so there is no employee linked to it.
Now I want to create an employee and I want to link it to the already existing user, how can I do it ?
I tried
# creation of the new employee
new_employee = Employee(exisiting_user.user_email,
exisiting_user.user_password, "email", "lastname", "firstname")
#trying to associate the new employee to the already existing user
exisiting_user.child_employee = new_employee
# save the employee to the database
db.session.add(new_employee)
db.session.commit()
but I get this error
sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) duplicate key value violates unique constraint "users_pkey"
DETAIL: Key (user_id)=(2) already exists.
[SQL: 'INSERT INTO users (user_id, user_email, user_password) VALUES (%(user_id)s, %(user_email)s, %(user_password)s)'] [parameters: {'user_id': 2, 'user_email': 'email', 'user_password': 'password'}]
Thanks
----------------First try to resolve it-------------------------
So as if I understand correctly, when I create an instance of "Employee" I also have an instance of the super class "User". Then whend I db.session.add(new_employee), SQLalchemy create an INSERT for the employee and then for the user, but the user already exist so I have the error.
I change my Employee table :
class Employee(User):
__tablename__ = 'employees'
employee_id = Column(Integer, ForeignKey(User.user_id), primary_key=True)
employee_lastname = Column(String(30), nullable=False)
employee_firstname = Column(String(30), nullable=False)
employee_comment = Column(String)
parent = relationship("User", back_populates="child_employee")
def __init__(self, employee_id, employee_lastname, employee_firstname, employee_comment=None):
self.employee_id = employee_id
self.employee_lastname = employee_lastname
self.employee_firstname = employee_firstname
self.employee_comment = employee_comment
and to add only an employee i'm doing
emp = Employee(1, "lastname", "firstname", "comment")
db.session.execute("INSERT INTO employees VALUES("emp.employee_id",'"emp.employee_lastname+"', '"+emp.employee_firstname+"', '"+emp.employee_comment+"')")
Because of the Id who is also a foreign key, the employee and the user are automatically linked
It works but I would prefer to use something more simple like db.session.add(emp), I still miss something

Related

sqlalchemy orm returning only row id

I have a table users.
class UserDBModel(Base):
__tablename__ = "users"
id = Column('id',INTEGER, primary_key=True)
first_name = Column('first_name', VARCHAR(255), nullable=False)
last_name = Column('last_name', VARCHAR(255), nullable=False)
email = Column('email', VARCHAR(255), nullable=False)
password = Column('password', VARCHAR(255), nullable=False)
created_at = Column('created_at', TIMESTAMP, server_default=func.now())
modified_at = Column('modified_at', TIMESTAMP, server_default=func.now(), onupdate=func.now())
Code for database connection:
from databases import Database
db_url = f'postgresql+asyncpg://{DB_USER}:{DB_PASS}#{DB_HOST}:{DB_PORT}/{DB_NAME}'
database = Database(db_url)
I am using sqlalchemy orm to query this table:
stmt = select(UserDBModel).where(UserDBModel.email==email)
print(stmt) #12
result = await database.execute(stmt)
Above code only fetches the row id(primary key).
If I change to
stmt = select(UserDBModel.first_name).where(UserDBModel.email==email)
print(stmt) #Hemant
then it give the first name.
With the first stmt, why I am not getting complete record.
Is there something that I am missing regarding query or orm's functionality?

Flask/SQLAlchemy: Create relations between multiple databases without foreign key constraints?

A StackOverflow question/answer (sqlalchemy: create relations but without foreign key constraint in db?) looked interesting, but what if you have two databases and join through a secondary table?
FKs can't map to external databases by design, making this a more realistic case if you can't load the remote table in your database.
Below is a setup where if we didn't set the __bind_key__ and the tables shared a database, it would work. However, we do want the multiple database setup. To keep things easier both databases have a schema called api where tables are stored.
Default Database
class Store(db.Model):
'''api.stores'''
schema = 'api'
__tablename__ = 'stores'
__table_args__ = { 'schema': schema }
id = db.Column(db.Integer, primary_key=True)
product = db.relationship('Product'
secondary=Inventory.__table__,
back_populates='stores'
)
Bind Database (second_db)
class Product(db.Model):
'''second_db.api.products'''
schema = 'api'
__bind_key__ = 'second_db'
__tablename__ = 'products'
__table_args__ = { 'schema': schema }
id = db.Column(db.Integer, primary_key=True)
stores = db.relationship('Store',
secondary=Inventory.__table__,
back_populates='products'
)
class Inventory(db.Model):
'''second_db.api.inventory'''
schema = 'api'
__bind_key__ = 'second_db'
__tablename__ = 'inventory'
__table_args__ = { 'schema': schema }
product_id = db.Column(db.Integer, db.ForeignKey(f'{schema}.products.id'), primary_key=True)
# below won't work with multiple DBs as a FK cannot reference external databases
store_id = db.Column(db.Integer, db.ForeignKey(f'{schema}.stores.id'), primary_key=True)
Per the accepted answer it seems primaryjoin may be used to build out the association and its answer doesn't use a secondary, so I'm not quite sure where something like this would live in the example above.
a = relationship('A',
foreign_keys=[a_id],
primaryjoin='A.a_id == C.a_id'
)
Another answer (https://stackoverflow.com/a/56683671/10408280) demonstrates how one might reference the bind database, however what is the key to the default app database/engine set by SQLALCHEMY_DATABASE_URI?
id = db.Column(db.Integer, primary_key=True)
people_id = db.Column(db.Integer, db.ForeignKey('db1.people.id'))
dogs_id = db.Column(db.Integer, db.ForeignKey('db2.dogs.id'))

How to create an interface table in sqlAlchemy (relation many-to-many)

I'm using SqlAlchemy to interact with an existing Mariadb database. I need to access data organized in a many-to-many relationship. According to the source site documentation (sqlAlchemy), I created a multi-to-multiple relationship, but after entering the flask db migrate command, I get an error.
It should be noted that the tables of posts and categories have been created.
Documents for creating the interface table:
from sqlalchemy import Column, String, Text, Integer, Table, ForeignKey
from app import db
posts_categories = Table('posts_categories', db.metadata,
Column('post_id', Integer, ForeignKey('posts.id', ondelete='cascade')),
Column('category_id', Integer, ForeignKey('categories.id', ondelete='cascade'))
)
class Category(db.Model):
__tablename = 'categories'
id = Column(Integer, primary_key=True)
name = Column(String(128), nullable=False, unique=True)
description = Column(String(256), nullable=True, unique=False)
slug = Column(String(128), nullable=False, unique=True)
posts = db.relationship('Post', secondary=posts_categories, back_populates='categories')
class Post(db.Model):
__tablename = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String(128), nullable=False, unique=True)
summary = Column(String(256), nullable=True, unique=False)
content = Column(Text, nullable=False, unique=False)
slug = Column(String(128), nullable=False, unique=True)
categories = db.relationship('Category', secondary=posts_categories, back_populates='posts')

sqlalchemy.exc.ArgumentError while running migrations

I am facing an Argument error while trying to run migrations, but I have failed to figure out where the problem is, What am trying to implement is a relationship between two models. Below is my model class :
class TourPackages(db.Model):
__tablename__ = 'tour_package'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
description = db.Column(db.TEXT)
price = db.Column(db.Float)
destination = db.relationship('Destinations', backref='tourpackages', lazy=True)
capacity = db.Column(db.Integer)
#property
def serialize(self):
return {
'name': self.name,
'description': self.destination,
'price': self.price,
'destination': self.destination,
'capacity': self.capacity
}
class Destinations(db.Model):
__tablename__ = 'destination'
id = db.Column(db.Integer)
location = db.Column(db.String(50))
danger_type = db.Column(db.String(50))
When I run migrations with the command below:
flask db migrate -m "Initial migration."
I get this error :
sqlalchemy.exc.ArgumentError: Mapper mapped class Destinations->destination could not assemble any primary key columns for mapped table 'destination'
What am I doing wrong here
In your Destinations model, you need to change the id column to be a primary key
id = db.Column(db.Integer, primary_key=True)
and add a foreign key column for the relationship, again in Destinations:
tourpackages_id = db.Column(db.Integer, db.ForeignKey('tour_package.id'))
See the documentation.

Sqlalchemy get data from relationship

I have this models
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
content = Column(Text)
author = Column(Integer, ForeignKey('users.id'))
to_topic = Column(Integer, ForeignKey('topics.id'))
def __init__(self, content: str, author: int, to_topic: int) -> None:
self.content = content
self.author = author
self.to_topic = to_topic
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(30))
email = Column(String(40))
password = Column(String(255))
registred_at = Column(DateTime, default=datetime.datetime.now)
last_logged_in = Column(DateTime)
last_login_attempt = Column(DateTime)
avatar = Column(Integer, ForeignKey('files.id'))
role = Column(String(20))
email_confirm = Column(Boolean, default=False)
children_forum = relationship('Forum')
children_topic = relationship('Topic')
children_post = relationship('Post')
And I am trying to get query that will contain Post.content, Post.author.username but how do i do this in sessions?
I tried
posts = db_session.query(Post, User).filter(
Post.to_topic == topic_id).with_entities(Post.content, Post.author.username)
but that doesnt work because author is just integer (id) so I expect that I need somehow get the author object and post object in one query, but I dont know how. In sql that would be easy just 2 queries but here I dont know how it´s done.
query_results = db_session.query(Post, User).\
join(User, Post.author == User.id).\
filter(
Post.to_topic == topic_id
).all()
I've never used with_entities, but I know this would give you a list of tuples where query_results[0] would be your Post instance, and query_results[1] would be your User instance.
EDIT: I believe you don't have to include the Post.author == User.id bit, but it's more legible if you're explicit with your joins.

Resources