Filter out data from sqlalchemy dynamically - python

I have these models
class Integration(Base, ModelBase, ModelSerializer):
__tablename__ = 'integration'
id = Column(Integer, primary_key=True)
domain = relationship('Domain', backref='integration')
created_at = Column(DateTime, default=datetime.now())
updated_at = Column(DateTime, default=datetime.now())
name = Column(String(512), index=True)
meta = Column(JSON)
tag = Column(String(512))
identifier_id = Column(String(512), unique=True, index=True)
def __repr__(self):
return '<name {}>'.format(self.name)
class Domain(Base, ModelBase, ModelSerializer):
__tablename__ = 'domain'
id = Column(Integer, primary_key=True)
integration_id = Column(Integer, ForeignKey('integration.id'), index=True)
url = relationship('Url', backref='domain')
created_at = Column(DateTime, default=datetime.now())
updated_at = Column(DateTime, default=datetime.now())
name = Column(String(512), index=True)
domain = Column(String(512), nullable=True)
def __repr__(self):
return '<name {}>'.format(self.name)
class Url(Base, ModelBase, ModelSerializer):
__tablename__ = 'url'
id = Column(Integer, primary_key=True)
domain_id = Column(Integer, ForeignKey('domain.id'), index=True)
created_at = Column(DateTime, default=datetime.now())
updated_at = Column(DateTime, default=datetime.now())
name = Column(String(512), index=True)
url = Column(String(512), nullable=True)
meta = Column(JSON)
auth = Column(JSON)
def __repr__(self):
return '<name {}>'.format(self.name)
class ModelBase(object):
def __repr__(self):
return '<id {}>'.format(self.id)
class ModelSerializer(object):
def as_dict(self):
return {attribute.name: getattr(self, attribute.name) for attribute in self.__table__.columns}
Now I need to filter out the data based on the filters.
If suppose integration_filters are given then only filter the Integration data.
If Integration and Domain are given then filter only domain and integration by joining them
Same with Url and Domain
Solutions I tried -
1). Doesnt work as it doesnt join then tables. Later figured out that add_entity doesnt add the models.
if integration_filter:
query = query.add_entity(Integration)
query = query.filter_by(**kwargs['integration_filter'])
if domain_filter:
query = query.add_entity(Domain)
query = query.filter_by(**kwargs['domain_filter'])
if url_filter:
query = query.add_entity(Url)
query = query.filter_by(**kwargs['url_filter'])
2). It checks for the attribute 'domain' in Integration table
if integration_filter:
query = session.query(Integration).filter_by(**integration_filter)
if domain_filter:
if query:
query = query.join(Domain)
else:
query = session.query(Domain)
query = query.filter_by(**domain_filter)
if url_filter:
if query:
query = query.join(Url)
else:
query = session.query(Url)
query = query.filter_by(**url_filter)`
3). This doesnt work either
models = []
joins = []
if integration_filter:
models.append(Integration)
if domain_filter:
if models:
joins.append((Integration, Domain, Integration.id == Domain.integration_id))
models.append(Domain)
if url_filter:
if models:
joins.append((Domain, Url, Domain.id == Url.domain_id))
models.append(Url)
query = session.query(*models)
for join in joins:
query = query.join(*join)

This one worked. Was pretty simple though, didn't think of it in the beginning
try:
session = Backend().get_session()
models = []
join_filter = []
if integration_filter:
models.append(Integration)
join_filter.append({'filter': integration_filter})
if domain_filter:
models.append(Domain)
join_filter.append({'join': (Domain, Domain.integration_id == Integration.id), 'filter': domain_filter})
if url_filter:
models.append(Url)
join_filter.append({'join': (Url, Url.domain_id == Domain.id), 'filter': url_filter})
query = session.query(*models)
for jf in join_filter:
if 'join' in jf:
query = query.join(jf['join'])
if 'filter' in jf:
query = query.filter_by(**jf['filter'])
query = query.__getattribute__(query_type)
self.records = query()
except InvalidRequestError as ir:
raise Exception("Invalid query in URL Builder Error")
except NoResultFound as nrf:
raise Exception(
"No URL was found for filters integration - {} domain - {} url - {}".format(integration_filter,
domain_filter, url_filter))
except MultipleResultsFound as mrf:
raise Exception(
"Multiple Results was found for filters integration - {} domain - {} url - {}".format(
integration_filter, domain_filter, url_filter))
except Exception as e:
raise Exception("Error while fetching records in URL Builder {}")
finally:
session.close()

Related

AttributeError: 'Session' object has no attribute 'session'

I am creating an endpoint that will effect a reversal using fastApi with python.I have an endpoint that takes in an id via post request & calls a reverse function on the transaction model. But I get the error AttributeError: 'Session' object has no attribute 'session' on 'db.session.add(transaction)' . I am not sure where this is coming from.
Transaction model
class Transaction(Base):
__tablename__ = "transactions"
id = Column(Integer, primary_key=True, index=True)
uuid = Column(UUID(as_uuid=True), nullable=False, default=uuid.uuid4)
amount = Column(Numeric(18, 2))
type = Column(
Enum(TransactionTypes, name="transaction_trans_types"), nullable=False
)
wallet_type = Column(
Enum(WalletTypes, name="transaction_wallet_types"), nullable=False
)
created_when = Column(DateTime(timezone=True), server_default=func.now())
payment_ref = Column(String, nullable=False)
running_balance = Column(Numeric(18, 2))
currency = Column(String, default="KES")
notes = Column(String)
paid_by = Column(UUID(as_uuid=True), ForeignKey("patients.uuid"), nullable=True,)
patient_uuid = Column(UUID(as_uuid=True), ForeignKey("patients.uuid"))
wallet_uuid = Column(UUID(as_uuid=True), ForeignKey("wallets.uuid"))
paid_by_names = Column(String)
def reverse(self,db: Session,notes:str =None,):
transaction =Transaction(amount =self.amount,type= self.type,wallet_type = self.wallet_type,payment_ref = self.payment_ref,
notes= notes )
db.session.add(transaction)
db.session.commit()
db.session.refresh(transaction)
reversal = Reversal(transaction_uuid=transaction.uuid,reversed_transaction_uuid= self.reversed_transaction_uuid)
db.session.add(reversal)
db.session.commit()
db.session.refresh(reversal)
return transaction
Reversal Model
class Reversal(Base):
__tablename__ = "reversals"
id = Column(Integer, primary_key=True, index=True)
transaction_uuid = Column(UUID(as_uuid=True), ForeignKey("transactions.uuid"))
reversed_transaction_uuid = Column(UUID(as_uuid=True),ForeignKey("transactions.uuid"))
__table_args__ = (
UniqueConstraint("reversed_transaction_uuid"),
)
Endpoint
#router.post("/{transaction_id}/reverse",)
async def reverse_transaction(
*,
db: Session = Depends(deps.get_db),
transaction_id: int,
current_user: models.User = Depends(deps.get_current_active_user),
) -> Any:
"""
Reverse transaction
"""
if not current_user.is_superuser:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not allowed to reverse transactions",
)
try:
transaction = (
db.query(Transaction).filter(Transaction.id == transaction_id).scalar()
)
transaction.reverse(db,transaction)
except NoResultFound:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Not found",
)
return transaction
This is my deps.db
def get_db() -> Generator:
try:
db = SessionLocal()
yield db
finally:
db.close()
The most important part would be to see how deps.get_db() looks like.
But according to your type hints I guess, this already gives you a Session. So instead of db.session in your reverse method, simply use db.

How to get parrent of object in session SQLAlchemy

I have two tables:
class Project(DataBase):
__tablename__ = 'projects'
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
name = Column(String, nullable=False, unique=True)
domain = Column(String, nullable=False)
phrases = relationship("Phrase", backref='proj')
def __init__(self, name, domain):
self.name = name
self.domain = domain
class Phrase(DataBase):
__tablename__ = 'phrases'
query_id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
query_text = Column(String, nullable=False)
project = Column(Integer, ForeignKey('projects.id'), nullable=False)
enable = Column(Boolean, nullable=False, default=True)
def __init__(self, query_text, project, city):
self.query_text = query_text
self.project = project
And I have a function:
def get_first_query():
session = Session(bind=engine)
q = session.query(Phrase).filter(Phrase.enable == True).first()
session.close()
return q
I want to get an object from table 2 and than get its parrent from first table:
session = Session(bind=engine)
q = get_first_query()
print(q.proj)
It doesn't work and print this error:
sqlalchemy.orm.exc.DetachedInstanceError: Parent instance is not bound to a Session; lazy load operation of
attribute 'proj' cannot proceed
I can do this:
session = Session(bind=engine)
q = get_first_query()
q_project = session.query(Project).filter(Project.id == q.project)
But it's a bad way.
You can assess related object via proj attribute.
session.query(Phrase).filter(
Phrase.enable == True
).first().proj
This way you'll hit database one additional time to get it so you'll need to open session again. To avoid additional queries you can use joined load:
session.query(Phrase).filter(
Phrase.enable == True
).options(
joinedload('proj')
).first().proj

Joining two table without relantioships in sqlalchemy

Here is my problem. I have three tables.
One called Project which has only one Column called id (this must be unique throughout the system).
One called ServiceAwarenessProject which has a one to one relationship with Project.id.
One called CorporateVPNProject which has a one to one relationship with Project.id
I'm using sqlalchemy ORM, so the code looks like the one below:
class Project(SqlAlchemyBase):
__tablename__ = 'project'
id = Column(Integer, primary_key=True, autoincrement=True)
class ServiceAwarenessProject(SqlAlchemyBase):
__tablename__ = 'sa_project'
id = Column(Integer, primary_key=True)
project_id = Column(Integer, ForeignKey(Project.id))
mop_url = Column(String, nullable=False)
expiration_date = Column(Datetime, index=True)
class CorporateVPNProject(SqlAlchemyBase):
__tablename__ = 'wvpn_project'
id = Column(Integer, primary_key=True)
project_id = Column(Integer, ForeignKey(Project.id))
mop_url = Column(String, nullable=False)
I designed my tables like that, so I can guarantee I have unique project_ids in the entire system. My problem is that I don't know how to join those tables together to find a project based on the project_id. To solve this problem for now, I'm querying both tables, using the function called get_project_by_id.
Is there a smarter way to solve this issue?
class ProjectService:
#staticmethod
def create_project_id():
session = DbSessionFactory.create_session()
result = session.query(Project.id).order_by(desc(Project.id)).first()
if result:
result = result[0]
if str(result)[:8] == datetime.datetime.now().strftime('%Y%m%d'):
project_id = str(result)[:8] + '{:03d}'.format(int(str(result)[8:]) + 1)
new_project = Project(id=project_id)
session.add(new_project)
session.commit()
return project_id
project_id = datetime.datetime.now().strftime('%Y%m%d') + '001'
new_project = Project(id=project_id)
session.add(new_project)
session.commit()
return project_id
#staticmethod
def get_project_by_id(project_id):
session = DbSessionFactory.create_session()
result = session.query(ServiceAwarenessProject) \
.filter(ServiceAwarenessProject.project_id == project_id) \
.first()
if result:
return result
result = session.query(CorporateVPNProject) \
.filter(CorporateVPNProject.project_id == project_id) \
.first()
if result:
return result
def create_serviceawareness_project(self):
session = DbSessionFactory.create_session()
project_id = self.create_project_id()
new_project = ServiceAwarenessProject(project_id=project_id, mop_url='http://www.thepacketwizards.com/1')
session.add(new_project)
session.commit()
return new_project
def create_corporatevpn_project(self):
session = DbSessionFactory.create_session()
project_id = self.create_project_id()
new_project = CorporateVPNProject(project_id=project_id, mop_url='http://www.thepacketwizards.com/wvpn')
session.add(new_project)
session.commit()
return new_project
Thank you!
Following #Ilja Everilä suggestion, I designed the table like so, using only joined table inheritance.
class Project(SqlAlchemyBase):
__tablename__ = 'project'
id = Column(Integer, primary_key=True)
created_on = Column(DateTime, default=datetime.datetime.now)
updated_on = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
project_url = Column(String(60))
mop_url = Column(String(60))
input_url = Column(String(60))
type = Column(String(60))
__mapper_args__ = {
'polymorphic_identity': 'project',
'polymorphic_on': type
}
class ServiceAwarenessProject(Project):
__tablename__ = 'sa_project'
id = Column(Integer, ForeignKey('project.id'), primary_key=True)
expiration_date = Column(DateTime)
__mapper_args__ = {
'polymorphic_identity': 'ServiceAwareness',
}
class CorporateVPNProject(Project):
__tablename__ = 'wvpn_project'
id = Column(Integer, ForeignKey('project.id'), primary_key=True)
client_name = Column(String(60))
__mapper_args__ = {
'polymorphic_identity': 'CorporateVPN',
}
Now, to query the DB I have to use with_polymorphic, so I can get different instances of Tables per row.
class ProjectService:
#staticmethod
def create_project_id():
session = DbSessionFactory.create_session()
result = session.query(Project.id).order_by(desc(Project.id)).first()
print(result)
if result:
result = result[0]
if str(result)[:8] == datetime.datetime.now().strftime('%Y%m%d'):
project_id = str(result)[:8] + '{:03d}'.format(int(str(result)[8:]) + 1)
return project_id
project_id = datetime.datetime.now().strftime('%Y%m%d') + '001'
return project_id
def create_serviceawareness_project(self):
session = DbSessionFactory.create_session()
project_id = self.create_project_id()
new_project = ServiceAwarenessProject(id=project_id,
project_url='http://project',
expiration_date=datetime.datetime.now() + datetime.timedelta(days=365),
mop_url='http://mop',
input_url='http://url',
type='ServiceAwareness')
session.add(new_project)
session.commit()
session.add(new_project)
return new_project
def create_corporatevpn_project(self):
session = DbSessionFactory.create_session()
project_id = self.create_project_id()
new_project = CorporateVPNProject(id=project_id,
project_url='http://project',
client_name='TIM',
mop_url='http://mop',
input_url='http://url',
type='CorporateVPN')
session.add(new_project)
session.commit()
session.add(new_project)
return new_project
#staticmethod
def get_project_by_id(project_id):
session = DbSessionFactory.create_session()
query = session.query(with_polymorphic(Project, [ServiceAwarenessProject, CorporateVPNProject])).filter(or_(
ServiceAwarenessProject.id == project_id,
CorporateVPNProject.id == project_id
)).first()
return query

SqlAlchemy Database Issue

I created a Table a Bmarks which has two foreign keys which have relation with same table Url_hash
class Hashed(Base):
__tablename__ = "url_hash"
hash_id = Column(Unicode(22), primary_key=True)
url = Column(UnicodeText)
clicks = Column(Integer, default=0)
def __init__(self, url):
cleaned_url = str(unidecode(url))
self.hash_id = unicode(generate_hash(cleaned_url))
self.url = url
class Bmark(Base):
__tablename__ = "bmarks"
bid = Column(Integer, autoincrement=True, primary_key=True)
hash_id = Column(Unicode(22), ForeignKey('url_hash.hash_id'))
clean_hash_id = Column(Unicode(22), ForeignKey('url_hash.hash_id'))
description = Column(UnicodeText())
extended = Column(UnicodeText())
stored = Column(DateTime, default=datetime.utcnow)
updated = Column(DateTime, onupdate=datetime.utcnow)
clicks = Column(Integer, default=0)
inserted_by = Column(Unicode(255))
username = Column(Unicode(255), ForeignKey('users.username'),
nullable=False,)
tag_str = Column(UnicodeText())
hashed = relation(Hashed,
foreign_keys="Bmark.hash_id",
backref="bmark",
uselist=False
)
clean_hashed = relation(Hashed,
foreign_keys="Bmark.clean_hash_id",
backref="bmark",
uselist=False
)
I am trying to store url after cleaning it a little bit like removing headers,utm parameters etc for indexing purposes
Error is occurring while creating the database
sqlalchemy.exc.ArgumentError: Error creating backref 'bmark' on relationship 'Bmark.clean_hashed': property of that name exists on mapper 'Mapper|Hashed|url_hash'
Actually the error message is very informative.
Just rename one of your backref="bmark" to something else like backref="my_clean_bmark".

SQLAlchemy Query foreignkey field(s)

I am trying to write a view/SQLAlchemy query that will allow me to display data thats linked as a ForeignKey and also where I have a ManyToMany relationship
have the models..
#Samples
class Sample(Base):
__tablename__ = 'samples'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
def __unicode__(self):
return self.name
samples_to_customer = Table('samples_to_customer', Base.metadata,
Column('customer_id', Integer, ForeignKey('customer.id')),
Column('sample_id', Integer, ForeignKey('samples.id'))
)
#Create a cusotmer model to store customer numbers
class Customer(Base):
__tablename__ = 'customer'
id = Column(Integer, primary_key=True)
name = Column(Unicode, unique=True) #this will be the variable used to search the existing db
customer_samples = relationship('Sample', secondary='samples_to_customer', backref='samples')
sales_rep = Column(Integer, ForeignKey('rep.id'))
Rep = relationship('Rep')
def __unicode__(self):
return self.name
# This model will have its own entry view/page a user logs on and enters notes (according to a customer
# number) they then get stored/saved onto the Customers account
class Note(Base):
__tablename__ = 'note'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
pub_date = Column(Date)
customer_no = Column(Integer, ForeignKey('customer.id'))
customer = relationship('Customer')
def __unicode__(self):
return self.name
And the views..
#view_config(route_name='view_customer', renderer='templates/view_customer.jinja2')
def view_page(request):
customer_no = request.matchdict['customer']
cust_slcustm = DBSessionRO.query(Slcustm).filter(Slcustm.customer == customer_no).first()
cust_customer = DBSessionRO.query(Custom).filter(Custom.cu_custref== customer_no).first()
# Return a 404 if a result isn't found, slcustm and customer share one2one relationship on customer_no
if cust_slcustm is None:
return HTTPNotFound('No such customer')
return dict(cust_slcustm=cust_slcustm, cust_customer=cust_customer)
But I cant seem to write a query that will display every sample and note related to a customer number? If anyone could help I would be very grateful :)
Try this
class Note(Base):
__tablename__ = 'note'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
pub_date = Column(Date)
customer_no = Column(Integer, ForeignKey('customer.id'))
customer = relationship('Customer', backref='notes')
#view_config(route_name='view_customer', renderer='templates/view_customer.jinja2')
def view_page(request):
customer_no = request.matchdict['customer']
cust = DBSessionRO.query(Customer).filter(Customer.id == customer_no).first()
print "Sample :", cust.customer_sample
print "Notes :", cust.notes
What ended up working was:
#views.py
#view_config(route_name='view_customer', renderer='templates/view_customer.jinja2')
def view_page(request):
customer_no = request.matchdict['customer']
cust_slcustm = DBSessionRO.query(Slcustm).filter(Slcustm.customer == customer_no).first()
cust_customer = DBSessionRO.query(Custom).filter(Custom.cu_custref== customer_no).first()
cust_books = DBSessionRW.query(Customer).filter(Customer.name == customer_no).first()
# Return a 404 if a result isn't found, slcustm and customer share one2one relationship on customer_no
if cust_slcustm is None:
return HTTPNotFound('No such customer')
return dict(cust_slcustm=cust_slcustm, cust_customer=cust_customer, cust_books=cust_books)
Then creating the template:
{% for sample in cust_books.customer_samples %}
{{ sample.name }}
{% endfor %}

Categories