Sqlalchemy Relationship field return None - python

I have the following models:
class DictionaryItem(Base):
__tablename__ = 'dictionary_items'
id = Column(Integer, primary_key=True)
text = Column(String, nullable=True)
phonetic_symbols = Column(String, nullable=True)
fgner_entity = Column(String, nullable=True)
status = Column(String, default="waiting")
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
user = relationship("User", back_populates="dictionary_items")
data_type_id = Column(Integer, ForeignKey('data_types.id'), nullable=False)
data_type = relationship("DataType", back_populates="dictionary_items", lazy="joined")
# can null if manually add
sns_raw_data_id = Column(Integer, ForeignKey('sns_raw_data.id'), nullable=True)
sns_raw_data = relationship("SnsRawData", back_populates="dictionary_items")
language_id = Column(Integer, ForeignKey('languages.id'), nullable=False, default=1)
language = relationship("Language", back_populates="dictionary_items")
created_at = Column(DateTime, nullable=False, default=func.now())
updated_at = Column(DateTime, nullable=False,
default=func.now(), onupdate=func.now())
deleted_at = Column(DateTime, nullable=True)
def __repr__(self):
return 'id: {}'.format(self.id)
#classmethod
def check_is_exists(cls, user_id, text, dictionary_id=None):
if dictionary_id is not None:
count = session.query(DictionaryItem).filter(DictionaryItem.user_id==user_id, DictionaryItem.text==text, DictionaryItem.deleted_at == None, DictionaryItem.id != dictionary_id).count()
else:
count = session.query(DictionaryItem).filter(DictionaryItem.user_id==user_id, DictionaryItem.text==text, DictionaryItem.deleted_at == None).count()
return count > 0
#classmethod
def add_dictionary_item(cls, text=None, phonetic_symbols=None, fgner_entity=None, user_id=None, data_type_id=None, language_id=None, sns_raw_data_id=None):
if not DictionaryItem.check_is_exists(user_id, text):
new_dictionary_item = DictionaryItem(
text=text,
phonetic_symbols=phonetic_symbols,
fgner_entity=fgner_entity,
user_id=user_id,
data_type_id=data_type_id,
language_id=language_id,
sns_raw_data_id=sns_raw_data_id
)
try:
session.add(new_dictionary_item)
session.commit()
session.refresh(new_dictionary_item)
except:
session.rollback()
raise
return new_dictionary_item
else:
return None
Language.dictionary_items = relationship("DictionaryItem", order_by=DictionaryItem.id, back_populates="language")
#event.listens_for(DictionaryItem, 'before_update')
def dictionary_item_before_update(mapper, connection, target):
print("before_update: %d" % target.id)
if target.sns_raw_data_id is not None:
if "language" in target.sns_raw_data.data:
target.language_id = get_language_id_by_language(target.sns_raw_data.data["language"])
if target.language_id is None:
target.language_id = detect_language_id(target.text)
#event.listens_for(DictionaryItem, 'before_insert')
def dictionary_item_before_insert(mapper, connection, target):
print("before_insert: %s" % target.text)
print(target.sns_raw_data, file=sys.stderr)
if target.sns_raw_data_id is not None:
if "language" in target.sns_raw_data.data:
target.language_id = get_language_id_by_language(target.sns_raw_data.data["language"])
if target.language_id is None:
target.language_id = detect_language_id(target.text)
SnsRawData.dictionary_items = relationship("DictionaryItem", order_by=DictionaryItem.id, back_populates="sns_raw_data")
User.dictionary_items = relationship("DictionaryItem", order_by=DictionaryItem.id, back_populates="user")
DataType.dictionary_items = relationship("DictionaryItem", order_by=DictionaryItem.id, back_populates="data_type")
class SnsRawData(Base):
__tablename__ = 'sns_raw_data'
id = Column(Integer, primary_key=True)
data = Column(postgresql.JSON)
status = Column(String, default="waiting")
sns_account_id = Column(Integer, ForeignKey('sns_accounts.id'), nullable=False)
sns_account = relationship("SnsAccount", back_populates="sns_raw_data", lazy="joined")
created_at = Column(DateTime, nullable=False, default=func.now())
updated_at = Column(DateTime, nullable=False,
default=func.now(), onupdate=func.now())
deleted_at = Column(DateTime, nullable=True)
def __repr__(self):
return 'id: {}'.format(self.id)
SnsAccount.sns_raw_data = relationship("SnsRawData", order_by=SnsRawData.id, back_populates="sns_account")
The DictionaryItem have relationship with SnsRawData through sns_raw_data_id.
In DictionaryItem lister_for event('before_insert') i currently getting
target.sns_raw_data.data returning None but target.sns_raw_data_id return a value when i do an insert.
File "/app/models.py", line 230, in dictionary_item_before_insert
if "language" in target.sns_raw_data.data:
AttributeError: 'NoneType' object has no attribute 'data'
How do i access data field from SnsRawData model when i have the Id of it ?

I figured it out, it return None because it was run before insert so there wasn't any relationship to begin with, i just have to query in SnsRawData with sns_raw_data_id from DictionaryItem
#event.listens_for(DictionaryItem, 'before_insert')
def dictionary_item_before_insert(mapper, connection, target):
print("before_insert: %s" % target.text)
if target.sns_raw_data_id is not None:
sns_raw_data = session.query(SnsRawData).filter(SnsRawData.id==target.sns_raw_data_id).first()
if "language" in sns_raw_data.data:
target.language_id = get_language_id_by_language(sns_raw_data.data["language"])
if target.language_id is None:
target.language_id = detect_language_id(target.text)

Related

FastAPI, Pydantic joinedload instead of lazy

I am trying to optimize on the performance of my models, however I can see that FastAPI is loading the relations of my model PurchaseOrderPart as lazy.. This is causing a n+1 problem, which is a big problem.
Is there any way I can make FastAPI load the relations as joined? When I am not using the response model parameter I get most of the data I need, but not all the relations.
class PurchaseOrderPart(Base):
__tablename__ = "purchase_order_part"
id = Column(BigInteger, primary_key=True, index=True)
quantity = Column(Float)
ordered_at = Column(DateTime, nullable=True)
expected_at = Column(DateTime, nullable=True)
confirmed_at = Column(DateTime, nullable=True)
delivered_at = Column(DateTime, nullable=True)
invoiced_at = Column(DateTime, nullable=True)
price_dkk = Column(BigInteger, server_default="0")
deviation = Column(String, nullable=True)
supplier_part_id = Column(BigInteger, ForeignKey("supplier_part.id"))
supplier_id = Column(BigInteger, ForeignKey("supplier.id"))
project_reference_id = Column(BigInteger, ForeignKey(
"project_reference.id"), nullable=True)
stockpile_part_id = Column(BigInteger, ForeignKey(
"stockpile_part.id"), nullable=True)
purchase_order_id = Column(BigInteger, ForeignKey(
"purchase_order.id"), nullable=True)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(),
onupdate=func.current_timestamp())
supplier_part = relationship(
"SupplierPart", uselist=False, foreign_keys=[supplier_part_id], lazy="selectin")
supplier = relationship("Supplier", uselist=False,
foreign_keys=[supplier_id], lazy="selectin")
project_reference = relationship(
"ProjectReference", uselist=False, foreign_keys=[project_reference_id], lazy="selectin")
stockpile_part = relationship(
"StockpilePart", uselist=False, foreign_keys=[stockpile_part_id], lazy="selectin")
purchase_order = relationship(
"PurchaseOrder", uselist=False, foreign_keys=[purchase_order_id], lazy="selectin")
purchase_order_part_notes = relationship(
"PurchaseOrderPartNote", back_populates="purchase_order_part", lazy="selectin")`
class PurchaseOrderPartRelation(PurchaseOrderPart):
purchase_order_part_notes: Optional[List["PurchaseOrderPartNote"]] = None
supplier: Optional[SupplierBase] = None
supplier_part: Optional[SupplierPart] = None
purchase_order: Optional[PurchaseOrder] = None
project_reference: Optional[ProjectReference] = None
stockpile_part: Optional[StockpilePartRelation] = None
part: Optional["Part"]
#################################
# Not Confirmed
#################################
#router.get(
"/not_confirmed",
summary="List all purchase order parts that are ordered but not confirmed",
status_code=status.HTTP_200_OK,
)
def get_not_confirmed_parts(request: Request, end_days_from_now: int = 7,
filter_and: Optional[List[str]] = Query(None),
filter_or: Optional[List[str]] = Query(None),
sort: Optional[List[str]] = Query(None),
page: int = 1,
size: int = 1000,
current_user: User = Depends(
get_current_user),
db: Session = Depends(get_db)):
with tracer.start_span("traces for not confirmed parts") as current_span:
query = db.query(models.PurchaseOrderPart, models.SupplierPart, models.Part).\
filter(and_(
models.PurchaseOrderPart.confirmed_at.is_(None),
models.PurchaseOrderPart.delivered_at.is_(None),
models.PurchaseOrderPart.ordered_at <= ordered_date,
models.PurchaseOrderPart.supplier_part_id == models.SupplierPart.id,
models.Part.primary_supplier_part_id == models.SupplierPart.id
))
# logger.debug(str(query))
# return query
with tracer.start_span("get filtered page trace") as current_span:
results = get_filtered_page(
db,
query,
filter_or,
filter_and,
sort,
page,
size)
return results

Order of declaring classes in SqlAlchemy

I'm sory for this newby question, but I can't figure it out for a couple days and it is driving me nuts.
I'm building a small project in order to learn flask, SqlAlchemy and Postrges.
I have major problems with declaring classes in SqlAlchemy. I have already simplified models by removing all many-to-many relationships. However, now I have new problems even with one-to many relationships, although I think I have tried all the possible options. Maybe there's a typo that I keep on overlooking, or I don't grasp something fundamental. Please let me know...
So, I have classes declared as follows in my models.py:
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
date_registered = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
role = db.Column(db.Integer, nullable=False)
role_exp_date = db.Column(db.DateTime)
#o2o
personal_datas = db.relationship('PersonalData', uselist=False, backref='user', lazy=True)
persons = db.relationship('Person', uselist=False, backref='user', lazy=True)
#o2m
posts = db.relationship('Post', backref='author', lazy=True)
comments = db.relationship('PostComment', backref='author', lazy=True)
projects_owned = db.relationship('ConstrProject', backref='owner', lazy=True)
attachments = db.relationship('Attachment', backref='author', lazy=True)
def __repr__(self):
return f"{self.username} ({self.email})"
class PersonalData(db.Model):
id = db.Column(db.Integer, primary_key = True)
date_birth = db.Column(db.DateTime)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
interests = db.Column(db.Text)
experties = db.Column(db.Text) #Потом сделать отдельную таблицу...
#o2o
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
#o2m
class Person(db.Model):
id = db.Column(db.Integer, primary_key = True)
first_name = db.Column(db.String(30), nullable=False)
middle_name = db.Column(db.String(40), nullable=False)
last_name = db.Column(db.String(60), nullable=False)
email = db.Column(db.String(120))
license = db.Column(db.String(120))
address = db.Column(db.String(240))
telephone = db.Column(db.String(30))
#o2o
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
#o2m
signers = db.relationship('Signer', backref='person', lazy=True)
def __repr__(self):
return f"{self.last_name.Capitalize} {self.first_name[0].Upper}. {self.middle_name[0].Upper}."
class ConstrProject(db.Model):
__tablename__ = 'constrproject'
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(120), nullable=False, default='New Project')
full_title = db.Column(db.Text, default='New Project')
notes = db.Column(db.Text)
public = db.Column(db.Boolean, default=True) #? check expamples
date_created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
date_last_edit = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
document_template = db.Column(db.Integer, nullable=False, default=1) #later to m2m
print_settings = db.Column(db.Integer, nullable=False, default=1) #later to m2m
address = db.Column(db.String(240))
#o2m
documents = db.relationship('Document', backref='project', lazy=True)
#m2o
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) #+ #default = CurrentUser
developer_id = db.Column(db.Integer, db.ForeignKey('company.id'))
main_contractor_id = db.Column(db.Integer, db.ForeignKey('company.id'))
architect_id = db.Column(db.Integer, db.ForeignKey('company.id'))
subcontractor_id = db.Column(db.Integer, db.ForeignKey('company.id'))
other_id = db.Column(db.Integer, db.ForeignKey('company.id'))
developer = db.relationship('Company', foreign_keys=[developer_id], back_populates='constr_projects_developed')
main_contractor = db.relationship('Company', foreign_keys=[main_contractor_id], back_populates='constr_projects_main_contracts')
architect = db.relationship('Company', foreign_keys=[architect_id], back_populates='constr_projects_architect')
subcontractor = db.relationship('Company', foreign_keys=[subcontractor_id], back_populates='constr_projects_subcontracts')
other = db.relationship('Company', foreign_keys=[other_id], back_populates='constr_projects_other')
tech_control_reps_id = db.Column(db.Integer, db.ForeignKey('signer.id'), nullable=False)
main_contractor_reps_id = db.Column(db.Integer, db.ForeignKey('signer.id'), nullable=False)
architect_reps_id = db.Column(db.Integer, db.ForeignKey('signer.id'), nullable=False)
subcontractor_reps_id = db.Column(db.Integer, db.ForeignKey('signer.id'), nullable=False)
other_reps_id = db.Column(db.Integer, db.ForeignKey('signer.id'), nullable=False)
tech_control_reps = db.relationship('Signer', foreign_keys=[tech_control_reps_id], back_populates='tech_control_projects')
main_contractor_reps = db.relationship('Signer', foreign_keys=[main_contractor_reps_id], back_populates='main_contractor_projects')
architect_reps = db.relationship('Signer', foreign_keys=[architect_reps_id], back_populates='architect_projects')
subcontractor_reps = db.relationship('Signer', foreign_keys=[subcontractor_reps_id], back_populates='subcontractor_projects')
other_reps = db.relationship('Signer', foreign_keys=[other_reps_id], back_populates='others_projects')
def __repr__(self):
return f"Site: {self.name}, (id{self.id})"
class Signer(db.Model):
id = db.Column(db.Integer, primary_key = True)
decree = db.Column(db.String(120))
job_title = db.Column(db.String(120))
date_duty_start = db.Column(db.DateTime)
date_duty_end = db.Column(db.DateTime)
#o2m
person_id = db.Column(db.Integer, db.ForeignKey('person.id'), nullable=False)
company_id = db.Column(db.Integer, db.ForeignKey('company.id'), nullable=False)
#m2o
tech_control_projects = db.relationship('ConstrProject', back_populates='tech_control_reps')
main_contractor_projects = db.relationship('ConstrProject', back_populates='main_contractor_reps')
architect_projects = db.relationship('ConstrProject', back_populates='architect_reps')
subcontractor_projects = db.relationship('ConstrProject', back_populates='subcontractor_reps')
others_projects = db.relationship('ConstrProject', back_populates='other_reps')
def __repr__(self):
return f"{self.job_title} as per {self.decree}." #название компании как подтянуть
class Company(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(60))
full_title = db.Column(db.String(240))
tin = db.Column(db.Integer)
kpp = db.Column(db.Integer)
ogrn = db.Column(db.Integer)
email = db.Column(db.String(120))
address = db.Column(db.String(240))
telephone = db.Column(db.String(30))
#o2m
license_number = db.Column(db.String(40), nullable = False)
license_date_issued = db.Column(db.DateTime)
license_category = db.Column(db.String(120), default = '2nd')
license_issued_by = db.Column(db.String(120))
license_issued_by_tin = db.Column(db.Integer)
license_issued_by_kpp = db.Column(db.Integer)
license_issued_by_ogrn = db.Column(db.Integer)
signers = db.relationship('Signer', backref='company', lazy=True)
constr_projects_developed = db.relationship('ConstrProject', back_populates='developer')
constr_projects_main_contracts = db.relationship('ConstrProject', back_populates='main_contractor')
constr_projects_architect = db.relationship('ConstrProject', back_populates='architect')
constr_projects_subcontracts = db.relationship('ConstrProject', back_populates='subcontractor')
constr_projects_other = db.relationship('ConstrProject', back_populates='other')
def __repr__(self):
return f"{self.name}"
class Post(db.Model):
id = db.Column(db.Integer, primary_key = True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
#o2m
comments = db.relationship('PostComment', backref='Post', lazy=True)
#m2o
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f"Post('{self.title}', '{self.date_posted}')"
class PostComment(db.Model):
id = db.Column(db.Integer, primary_key = True)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
#m2o
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
def __repr__(self):
return f"Comment('{self.id}', '{self.date_posted}')"
class Document(db.Model):
id = db.Column(db.Integer, primary_key = True)
type = db.Column(db.String(60), nullable=False, default='АОСР')
date_last_edit = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
notes = db.Column(db.Text)
public = db.Column(db.Boolean, default=True)
number = db.Column(db.String(20), nullable=False)
date = db.Column(db.DateTime, default=datetime.utcnow)
job_name = db.Column(db.Text) #? обязательный? на каком этапе делать проверку?
job_place = db.Column(db.String(200))
date_job_start = db.Column(db.DateTime, default=datetime.utcnow)
date_job_end = db.Column(db.DateTime, default=datetime.utcnow)
regulations = db.Column(db.Text)
next_job_allowed = db.Column(db.String(240))
attachments_user_defined = db.Column(db.Text)
#o2m
attachments = db.relationship('Attachment', backref='document', lazy=True)
#m2o
project_id = db.Column(db.Integer, db.ForeignKey('constrproject.id'), nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
#m2m
arch_docs = db.Column(db.Text)
building_materials = db.Column(db.Text)
work_drawings = db.Column(db.Text)
def __repr__(self):
return f"АОСР ('{self.number}', '{self.job_name} {self.job_place}', '{self.project}' )"
class Attachment(db.Model):
id = db.Column(db.Integer, primary_key = True)
type_of_document = db.Column(db.String(60), nullable=False, default="QAC")
number = db.Column(db.String(50), nullable=False)
date = db.Column(db.DateTime)
date_valid_start = db.Column(db.DateTime)
date_valid_end = db.Column(db.DateTime)
contents = db.Column(db.Text)
type_of_file = db.Column(db.String(10), nullable=False, default = 'jpg')
image_file = db.Column(db.String(20), nullable=False)
#m2o
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
document_id = db.Column(db.Integer, db.ForeignKey('document.id'), nullable=False)
def __repr__(self):
if self.text:
return f'{self.text}'
return f'+{self.type_of_document} id{self.id} ({self.type_of_file})'
I cannot understand why, when I try to create an instance of a "Document"
I get errors like this:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join
condition between parent/child tables on relationship
Signer.tech_control_projects - there are multiple foreign key paths
linking the tables. Specify the 'foreign_keys' argument, providing a
list of those columns which should be counted as containing a foreign
key reference to the parent table.
or
sqlalchemy.exc.InvalidRequestError: When initializing mapper
Mapper|ConstrProject|constrproject, expression 'tech_control_reps_id'
failed to locate a name ("name 'tech_control_reps_id' is not
defined"). If this is a class name, consider adding this
relationship() to the class
after both dependent classes have been defined.
I haven't even tried to create these classes. Adding "foreign_keys" doesn't seem to help either. all relationships declared as strings. I tried to use lambda's also without success.
Nevertheless moving the order of class declearations alters the error messages i get...
I cannot find any good examples (resources) of more complicated databases with multiple many-to-many and one-to-many relationships in Each class. Usually examples are very basic and obvious.
So I would really appreciate if you post links to such projects or tutorials.
I wanted to learn some of it myself so I created a working example with one-to-one and multiple one-to-many relationships based on your code (User, Person, Post and Comment classes). I hope it will be a good (simple but not trivial) example for you.
'''SQLAlchemy one-to-one and one-to-many SSCCE'''
import sqlalchemy
import sqlalchemy.ext.declarative
from passlib.hash import pbkdf2_sha256
Base = sqlalchemy.ext.declarative.declarative_base()
class User(Base):
__tablename__ = 'users'
user_id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
username = sqlalchemy.Column(sqlalchemy.String(20), unique=True, nullable=False)
email = sqlalchemy.Column(sqlalchemy.String(120), unique=True, nullable=False)
password = sqlalchemy.Column(sqlalchemy.String(100), nullable=False)
registered = sqlalchemy.Column(
sqlalchemy.DateTime(timezone=True),
nullable=False,
server_default=sqlalchemy.func.now()
)
#o2o
person = sqlalchemy.orm.relationship(
'Person',
uselist=False,
back_populates='user',
lazy='joined'
)
#o2m
posts = sqlalchemy.orm.relationship('Post', back_populates='user')
comments = sqlalchemy.orm.relationship('Comment', back_populates='user')
def __repr__(self):
return f'{self.username} ({self.email})'
class Person(Base):
__tablename__ = 'persons'
person_id = sqlalchemy.Column(
sqlalchemy.Integer,
sqlalchemy.ForeignKey('users.user_id'),
primary_key=True
)
first_name = sqlalchemy.Column(sqlalchemy.String(30), nullable=False)
middle_name = sqlalchemy.Column(sqlalchemy.String(40), nullable=False)
last_name = sqlalchemy.Column(sqlalchemy.String(60), nullable=False)
#o2o
user = sqlalchemy.orm.relationship('User', back_populates='person', lazy='joined')
def __repr__(self):
return (
f'{self.last_name.upper()}'
f' {self.first_name[:1].upper()}.'
f' {self.middle_name[:1].upper()}.'
)
class Post(Base):
__tablename__ = 'posts'
post_id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
title = sqlalchemy.Column(sqlalchemy.String(100), nullable=False)
posted = sqlalchemy.Column(
sqlalchemy.DateTime(timezone=True),
nullable=False,
server_default=sqlalchemy.func.now()
)
content = sqlalchemy.Column(sqlalchemy.Text, nullable=False)
#o2m
comments = sqlalchemy.orm.relationship('Comment', back_populates='post')
#m2o
user_id = sqlalchemy.Column(
sqlalchemy.Integer,
sqlalchemy.ForeignKey('users.user_id'),
nullable=False
)
user = sqlalchemy.orm.relationship('User', uselist=False, back_populates='posts', lazy='joined')
def __repr__(self):
return f'Post({self.title!r}, {self.posted!r})'
class Comment(Base):
__tablename__ = 'comments'
comment_id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
posted = sqlalchemy.Column(
sqlalchemy.DateTime(timezone=True),
nullable=False,
server_default=sqlalchemy.func.now()
)
content = sqlalchemy.Column(sqlalchemy.Text, nullable=False)
#m2o
user_id = sqlalchemy.Column(
sqlalchemy.Integer,
sqlalchemy.ForeignKey('users.user_id'),
nullable=False
)
user = sqlalchemy.orm.relationship(
'User',
uselist=False,
back_populates='comments',
lazy='joined'
)
post_id = sqlalchemy.Column(
sqlalchemy.Integer,
sqlalchemy.ForeignKey('posts.post_id'),
nullable=False
)
post = sqlalchemy.orm.relationship(
'Post',
uselist=False,
back_populates='comments',
lazy='joined'
)
def __repr__(self):
return f'Comment({self.comment_id!r}, {self.posted!r})'
def main():
engine = sqlalchemy.create_engine(
'postgresql+psycopg2:///stack',
echo=True,
server_side_cursors=True,
use_batch_mode=True
)
Base.metadata.create_all(engine)
Session = sqlalchemy.orm.sessionmaker(bind=engine)
session = Session()
session.commit()
try:
the_user = session.query(User).filter(User.username == 'example').one()
except sqlalchemy.orm.exc.NoResultFound:
the_user = User(
username='example',
email='example#example.com',
password=pbkdf2_sha256.hash('correct horse battery staple')
)
the_user.person = Person(first_name='Ex', middle_name='', last_name='Ample')
session.add(the_user)
print(the_user)
print(the_user.person)
if not the_user.posts:
the_user.posts.append(Post(title='First post', content='Lorem ipsum'))
session.commit()
print(the_user.posts[0])
if not the_user.posts[0].comments:
the_user.posts[0].comments.append(Comment(content='Me too', user=the_user))
session.commit()
print(the_user.posts[0].comments[0])
if __name__ == '__main__':
main()
Several comments:
I don't think it is easy to have foreign-keys both ways for a mandatory one-to-one relationship.
I made both User and Person use the same id numbers, as they are one-to-one.
I made the PostgreSQL now() as the default value for timestamps, instead of client-side utcnow.
I made the timestamps use "timestamp with timezone" type - the "timestamp" type (without timezone) is an abomination.
"user" is a bad name for a table, as this is also a keyword in PostgreSQL, so I changed it to "users". Other tables also changed to plural form for consistency.
A secure password storage is a must.
I used back_populates consistently, as it is more explicit and works better with static code analyzers than backref.

Many to Many relationship calling on Flask SQL Alchemy

I have the following badge (achievement) system database structure:
class Base(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
date_modified = db.Column(db.DateTime, default=db.func.current_timestamp(),
onupdate=db.func.current_timestamp())
class User(UserMixin, Base):
__tablename__ = 'users'
username = db.Column(db.String(20), nullable=False, unique=True)
email = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(200), nullable=False)
name = db.Column(db.String(30), nullable=False)
badges = db.relationship('UserBadge', backref='ubadge',
lazy='dynamic')
class Badge(Base):
__tablename__ = 'badges'
name = db.Column(db.String(35), unique=True)
description = db.Column(db.String(300))
imgfile = db.Column(db.String(80))
badges = db.relationship('UserBadge', backref='badge',
lazy='dynamic')
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.name, self.description)
class UserBadge(Base):
__tablename__ = 'userbadges'
user_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
badge_id = db.Column(db.Integer(), db.ForeignKey('badges.id'))
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.user_id, self.badge_id)
So i can return all the badges by a specific user, using:
ubadges = UserBadge.query.filter_by(user_id=user.id).all()
It returns:
[<Achievement: 1 - 1>]
But instead of 1 (user_id) and 1 (badge_id) i want to show the users.name and badges.name. How can i access those attributes?
In your UserBadge class, just use:
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.ubadge.name, self.badge.name)
It has both properties because you set them up using backref in the other classes.
PS: You might need to change the User backref to user, and then use self.user.name int he function above

SqlAlchemy sort by first record in relationship column

Need a bit of help concerning relationships and filtering. Given the following models, how can I sort the Project's Milestones by the Milestone.active_date?
After trying this, I understand I can't do this with a property. I've tried and the error i get that its not a mappable table makes sense. So what's the best/correct way to perform this?
I've looked into hybrid_property and joins but I can't seem to get this working. I'm quite rusty on databases and haven't done much with ORM's before so a detailed explanation would be very much appreciated.
Thanks!
BW
class MilestoneType(db.Model):
__tablename__ = 'MilestoneType'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(25), nullable=False)
icon_name = db.Column(db.String(255))
def __repr__(self):
return self.name
class Milestone(db.Model):
__tablename__ = 'Milestone'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
milestone_type_id = db.Column(db.ForeignKey(MilestoneType.id))
milestone_type = db.relationship("MilestoneType")
milestone_dates = db.relationship("MilestoneDate", order_by="MilestoneDate.datestamp", lazy="dynamic")
notes = db.Column(db.String(255), nullable=False)
project_id = db.Column(db.Integer, db.ForeignKey('Project.id'), nullable=False)
project = db.relationship("Project")
#property
def active_date(self):
return self.milestone_dates.first()
def __repr__(self):
return str(self.name) + ' (Type: ' + str(self.milestone_type) + ')'
class MilestoneDate(db.Model):
__tablename__ = 'MilestoneDate'
id = db.Column(db.Integer, primary_key=True)
milestone_id = db.Column(db.Integer, db.ForeignKey('Milestone.id'), nullable=False)
milestone = db.relationship("Milestone")
datestamp = db.Column(db.DateTime, nullable=False)
notes = db.Column(db.String(255), nullable=False)
def __repr__(self):
return str(self.datestamp)
class Project(db.Model):
__tablename__ = 'Project'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
active = db.Column(db.Boolean, nullable=False, default=True)
name = db.Column(db.String(50), nullable=False)
code = db.Column(db.String(8), nullable=False)
responsible = db.Column(db.String(50), nullable=False)
customer_id = db.Column(db.ForeignKey(Customer.id))
customer = db.relationship("Customer")
project_type_id = db.Column(db.ForeignKey(ProjectType.id))
project_type = db.relationship("ProjectType")
product_id = db.Column(db.ForeignKey(Product.id))
product = db.relationship("Product")
site_id = db.Column(db.ForeignKey(Site.id))
site = db.relationship("Site")
milestones = db.relationship("Milestone", lazy="dynamic")
def __repr__(self):
return str(self.customer) + " - " + str(self.name) + '(' + str(self.product) + ')'

errorclass, errorvalue when I try to add new model/table to existing database

When I try to add new model to SQLAlchemy I get error ( I am using MySQL below)
raise errorclass, errorvalue
sqlalchemy.exc.OperationalError 1005 Can't create table
I have model/table PlayerModel/players since beginning of the time and I am trying now to add new table (Base has id column id)
class Base(object):
def __tablename__(self):
return self.__name__.lower()
id = Column(Integer, primary_key=True, nullable=False)
class PlayerModel(Base):
__tablename__ = 'players'
alliances_id = Column(Integer, ForeignKey('alliances.id'), nullable=True)
alliance = relationship('AllianceModel')
username = Column(String(90), nullable=True, default=None)
email = Column(String(75), nullable=True, default=None)
password = Column(String(128), nullable=True, default=None)
money = Column(Integer, nullable=False, default=INITIAL_MONEY)
# used for push notifications
push_token = Column(String(64), nullable=True)
registered_for_push = Column(Boolean, nullable=False, default=False)
device_id = Column(String(64), nullable=True)
device_type = Column(String(64), nullable=True)
device_os = Column(Integer, nullable=True)
notify = Column(Boolean, default=True)
def __repr__(self):
return "<Player('%s')>" % (self.type)
and here throw error
class ArmyModel(Base):
__tablename__ = 'army'
player_id = Column(Integer, ForeignKey('players.id', ondelete='CASCADE'), nullable=False, index=True)
speed = Column(Float, nullable=False, default=1)
# mode of army behavior ( always fight, ignore )
mode = Column(Integer, nullable=False)
x = Column(Float, nullable=False, default=0)
y = Column(Float, nullable=False, default=0)
z = Column(Float, nullable=False, default=0)
def __repr__(self):
return "<Army('%s')>" % (self.type)

Categories

Resources