sqlalchemy one-to-one relation real example - sqlalchemy

I'm a novice in sqlalchemy. I read a lot of guidelines but failed to reproduce it.
I Need to make relation 1-to-1 and I'm failed (every user has a related meta table to distinguish between data and info).
This is my current broken code.
class UserData(Base):
__tablename__ = "users"
user_id = Column(Integer, primary_key=True, index=True)
age = Column(Integer, index=True)
first_name = Column(String, index=True)
last_name = Column(String, index=True)
country = Column(String, index=True)
city = Column(String, index=True)
comment = Column(String, index=True)
users_meta = relationship("UserMeta", uselist=False, back_populates="users")
class UserMeta(Base):
__tablename__ = "users_meta"
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
user_id = Column(Integer, ForeignKey('users.user_id'))
users = relationship("UserData", back_populates="users")

The code is almost correct, it needs two changes:
UserMeta needs a primary key column
id = Column(Integer,primary_key=True)
The relationship definition in UserMeta should refer to UserData's users_meta attribute, not users, which does not exist.
users = relationship("UserData", back_populates="users_meta")
One minor point - there's no need for index=True in UserData.id, primary keys will be indexed automatically.

Related

SQLAlchemy - how to make column unique per ForeignKey relationship?

In the Todo model below there is a unique constraint put on text column.
How can I narrow this constraint to validate uniqueness per "foreign-keyed" user only, not per all users as it is now?
I use SQLite.
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from database import Base
class Todo(Base):
__tablename__ = 'todos'
id = Column(Integer, primary_key=True, index=True)
text = Column(String, unique=True)
user_id = Column(Integer, ForeignKey('users.id'))
user = relationship("User", back_populates="todos")
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
todos = relationship("Todo", back_populates="user")
If you use postgresql, you can use the Partial Index to implement this:
class Todo(Base):
__tablename__ = 'todos'
id = Column(Integer, primary_key=True, index=True)
text = Column(String) # ! removed the unique from here
user_id = Column(Integer, ForeignKey('users.id'))
user = relationship("User", back_populates="todos")
# ! added unique index with
__table_args__ = (
Index(
"todo_text_uc",
"text",
unique=True,
postgresql_where=(user_id != None),
# postgresql_where=(~user_id.is_(None)), # equivalent to the row above, but no linting warnings
),
)
For sqlite just replace postgresql_where with sqlite_where.

SqlAlchemy error: Foreign key could not find table

So I'm building an app and I'm trying to save new changes to my database but when I try to commit the changes in the flask using db.session.commit() it returns me the following error:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'products.country_id' could not find table 'countries' with which to generate a foreign key to target column 'id'
In my models.py I have the following:
from app import db
from . import db
from datetime import datetime
def now():
return datetime.now()
class Countries(db.Model):
__tablename__ = 'countries'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
code = db.Column(db.String(45))
def __repr__(self):
return f'Id {self.id}'
class Categories(db.Model):
__tablename__ = 'categories'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
def __repr__(self):
return f'Id {self.id}'
class Brands(db.Model):
__tablename__ = 'brands'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
logo = db.Column(db.String(5000))
feed = db.Column(db.String(5000))
feed_type = db.Column(db.String(45))
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
country_id = db.Column(db.Integer, db.ForeignKey('countries.id'))
awinmid = db.Column(db.Integer)
def __repr__(self):
return f'Id {self.id}'
class Products(db.Model):
__tablename__ = 'products'
__table_args__ = {'schema': 'products_data'}
id = db.Column(db.Integer, primary_key=True, unique=True, autoincrement=True)
name = db.Column(db.String(255))
url = db.Column(db.Text)
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'))
country_id = db.Column(db.Integer, db.ForeignKey('countries.id'))
price = db.Column(db.Float)
currency = db.Column(db.String(45))
discount_price = db.Column(db.Float)
shipping = db.Column(db.Float)
brand_id = db.Column(db.Integer, db.ForeignKey('brands.id'))
Am I doing anything wrong when associating a column in products with a foreign key? This is the first time I encounter this error so I'm really lost on what to do right now.
To fix I just added the schema to the db.ForeignKey and it worked
Example:
db.ForeignKey('products_data.countries.id')
PS:
Not my idea. Just wanted to post the answer in case someone visits the post later with the same problem.
Gord Thompson thanks for the help!
First of all, I don t see any table Categories. Secondly, you copy pasted your schema from the Products table into your Countries one.
PS: By default sqlalchemy gives the tables the name of the class (lower cased). So your __tablename__='products' does nothing actually.
EDIT:
The problem with your code lies in how you set the __table_args__ attribute. You assign an object to it, which by their specifications is wrong.
Take a look at the following example and modify your code accordingly
__table_args__ = ({'schema': 'products_data'})
Also for further reference, take a look at this https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/table_config.html

sqlalchemy filter by data from other relationship table

I have user table:
class User(db.Model):
__tablename__ = "users"
UserID = db.Column(db.Integer, primary_key=True, autoincrement=True)
FirstName = db.Column(db.String(255), nullable=False)
# relationships
Emails = db.relationship('Email', backref='user', lazy='dynamic')
with emails table:
class Email(db.Model):
__tablename__ = "user_emails"
Email = db.Column(db.String(255), unique=True, primary_key=True, nullable=False)
UserID = db.Column(db.Integer, db.ForeignKey('users.UserID'), nullable=False)
how do i filter or find user based on email?
i tried below but it doesnt work:
DBModel.query.filter(DBModel.Emails.any(Email=searchInput))
DBModel.query.filter_by(Emails=searchInput).first()
I solve it with this:
DBModel.query.join(DBModel.Emails).filter_by(Email=id).first()

SQLAlchemy: How to delete a item on one-to-many relation?

I am using SQLAlchemy, and i want to delete certain item in a one-to-many-relationship. Well there are two pictures for you. First you can see an EER-Model. Its a one (gender) to many (person) relation.
Second, you can see a table with fictional data. In gender-table we have two gender currently, and in person-table we have three persons.
Imagine, you will just delete a gender, let us say 'male'. But we see, that 'male' is used as foreign by person-table.
My current source code looks as follows:
class PERSON_GENDER(Base):
__tablename__ = "person_gender"
id = Column(Integer, primary_key=True, unique=True, autoincrement=True)
gender = Column(String(50), nullable=False, unique=True)
class PERSON(Base):
__tablename__ = "person"
id = Column(Integer, primary_key=True, unique=True, autoincrement=True)
nickname = Column(String(255))
alias_name = Column(String (255))
name_normally_used = Column(String(50), nullable=False)
first_middle_name = Column(String(255))
last_name = Column(String(100))
birth_name = Column(String(100))
body_height = Column(String(10))
wedding_anniversary = Column(Date)
birthday = Column(Date)
day_of_death = Column(Date)
notice = Column(Text())
gender_id = Column(Integer, ForeignKey('person_gender.id', ondelete='CASCADE'))
gender = relationship("PERSON_GENDER", single_parent=True, cascade="all, delete, delete-orphan")
When I run this one, then not only the gender is deleted, but also the person. I want that only the gender is deleted, not the person.
ondelete='CASCADE')=> ondelete='SET NULL')

sqlalchemy python update a primary key with foreign key restraints

I need to change a primary key in multiple tables that also have foreign key restraints. I was thinking to either do an update or to copy the entry with an updated primary key and delete the old entry. How can I accomplish either?
Below is a minimal working set up of my tables. P and S rely on the primary key of S and S relies on the primary key of H.
I need to update the a value of ever table for one entry. I am continually limited on changing the value because of the constraints. I would rather not remove the constraints so this can be a permanent solution for later use.
class H(Base):
__tablename__ = 'ch'
a = Column(String(255), nullable=False, primary_key=True)
other_columns = Column(String(255))
class S(Base):
__tablename__ = 'cs'
a = Column(String(255), ForeignKey('ch.a'), nullable=False, primary_key=True)
sn = Column(Integer, nullable=False, primary_key=True)
other_columns = Column(Date)
header = relationship("H", backref=backref('cs', order_by=a, cascade='delete, all, delete-orphan'), foreign_keys='S.a')
__table_args__ = (UniqueConstraint('a', 'sn', name='_a_sn_uc'),)
class P(Base):
__tablename__ = 'cs_p'
a = Column(String(255), nullable=False, primary_key=True)
sn = Column(Integer, nullable=False, primary_key=True)
ps = Column(Integer, nullable=False, primary_key=True)
other_columns = Column(String(255))
pa = relationship("S", backref=backref('cs_p', order_by=a, cascade='delete, all, delete-orphan'))
__table_args__ = (UniqueConstraint('a', 'sn', 'ps', name='_a_sn_ps_uc'),
ForeignKeyConstraint([a, sn], [S.a, S.sn]),)
class F(Base):
__tablename__ = 'cs_f'
a = Column(String(255), nullable=False, primary_key=True)
sn = Column(Integer, nullable=False, primary_key=True)
fs = Column(Integer, nullable=False, primary_key=True)
other_columns = Column(String(255))
fa = relationship("S", backref=backref('cs_f', order_by=a, cascade='delete, all, delete-orphan'))
__table_args__ = (UniqueConstraint('a', 'sn', 'fs', name='_a_sn_fs_uc'),
ForeignKeyConstraint([a, sn], [S.a, S.sn]),)