I have a SQLAlchemy table class created using the Declarative method:
mysqlengine = create_engine(dsn)
session = scoped_session(sessionmaker(bind=mysqlengine))
Base = declarative_base()
Base.metadata.bind = mysqlengine
class MyTable(Base):
__table_args__ = {'autoload' : True}
Now, when using this table within the code I would like to not have to use the session.add method in order to add each new record to the active session so instead of:
row = MyTable(1, 2, 3)
session.add(row)
session.commit()
I would like to have:
row = MyTable(1, 2, 3)
session.commit()
Now, I know of this question already: Possible to add an object to SQLAlchemy session without explicit session.add()?
And, I realize you can force this behavior by doing the following:
class MyTable(Base):
def __init__(self, *args, **kw):
super(MyTable, self).__init__(*args, **kw)
session.add(self)
However, I do not want to bloat my code containing 30 tables with this method. I also know that Elixir ( http://elixir.ematia.de/trac/wiki ) does this so it must be possible in some sense.
Super simple. Use an event:
from sqlalchemy import event, Integer, Column, String
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
from sqlalchemy.ext.declarative import declarative_base
Session = scoped_session(sessionmaker())
#event.listens_for(mapper, 'init')
def auto_add(target, args, kwargs):
Session.add(target)
Base = declarative_base()
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
data = Column(String)
a1 = A(data="foo")
assert a1 in Session()
Related
I'm trying to map a class defined in a library into my existing table. After registering the mapper, however, every time I instantiate the relevant class, fields with names other than those of the object's properties are assigned None.
Existing library in class:
class LibraryClass:
def __init__(self, service_number:str, start_date:datetime, **kwargs):
self.__service_number = service_number
self.__start_date = start_date
#property
def service_number(self) -> str:
return self.__service_number
#service_number.setter
def service_number(self, service_number:str) -> None:
self.__service_number = service_number
#property
def start_date(self) -> datetime:
return self.__start_date
#start_date.setter
def start_date(self, value:datetime) -> None:
self.__start_date = value
The table in my db instead has these two fields defined: id varchar(50) PK, start_date timestamp
I would like to map the library class into my table, so I followed this approach:
from mylibrary.LibraryClass import LibraryClass
from sqlalchemy import Column, DateTime, String, Table, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, mapper, registry
engine = create_engine(
"<connections string>",
convert_unicode=True,
echo="debug",
)
Base = declarative_base()
mapper_registry = registry(Base.metadata)
mytable = Table("mytable", mapper_registry.metadata,
Column('id', String(50), primary_key=True, nullable=False),
Column('start_date', DateTime)
)
mapper_registry.map_imperatively(LibraryClass, mytable, properties={
'service_number': mytable.c.id
})
db_session = Session(engine)
When I go to instantiate the object x = LibraryClass("123456", datetime.now()), the service_number property of x is None, despite debugging I saw that the constructor of the LibraryClass class is correctly called, and the value is set. Once I exit the constructor and return to the affected piece of code, service_number is None.
By not registering the mapper instead the object is correctly instantiated.
python version: 3.6.9
sqlalchemy version: 1.4.39
As per the SQLAlchemy documentation on relationship loading:
When the given collection or reference is first accessed on a particular object, an additional SELECT statement is emitted such that the requested collection is loaded.
How do I achieve loading behavior such that only the single elements of a relationship collection that I access are loaded, rather than the entire collection all at once?
I have heard of deferred column loading; this would be more like "deferred row loading". Rather than deferring loading of attributes, I'd like to defer loading of relationship collection elements.
Desired use case:
# Persist instance.
coln = Collection([1, 2, 3])
session.add(coln)
session.commit()
# Test lazy loading.
print('data' in coln.__dict__)
# Lazy loads the entire collection. I'd like only one element.
print(coln.data[1])
# Will output: "True 3". I'd like: "True 1".
print('data' in coln.__dict__, len(coln.__dict__['data']))
Class definitions and other backwork:
from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
engine = create_engine('sqlite:///:memory:')
# Define classes.
class Collection(Base):
__tablename__ = 'collection'
id = Column(Integer, primary_key=True)
data = relationship('Element')
def __init__(self, list_):
self.data = [Element(e) for e in list_]
class Element(Base):
__tablename__ = 'element'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('collection.id'))
value = Column(Integer)
def __init__(self, value):
self.value = value
def __repr__(self):
return 'Element({})'.format(self.value)
# Create schema.
Base.metadata.create_all(engine)
# Create session.
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()
Use the lazy parameter with dynamic value:
data = relationship('Element', lazy='dynamic')
https://docs.sqlalchemy.org/en/13/orm/collections.html#dynamic-relationship
How to add a function/expression which takes arguments as other columns as a default value to a column in the table of SQLAlchemy? For example: I want to define c as a column which is 2*x(other column);which should be saved in the database(could be in other table too). Can #hybrid_property decorator be used in this context?
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, aliased
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
from sqlalchemy import create_engine
from sqlalchemy import MetaData
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///Helloworld.db', echo=False)
Session = sessionmaker(bind=engine)
metadata = MetaData(engine)
Base = declarative_base()
class HelloWorld(Base):
__tablename__ = 'helloworld'
pm_key = Column(Integer, primary_key=True)
x = Column(Integer, nullable=False)
c = Column(Integer,default=2*x)
Base.metadata.create_all(engine)
It is possible. Below I'am just adding a piece of code you can try . For more I think this will help you.
def mydefault(context):
return context.current_parameters.get('X')
class HelloWorld(Base):
__tablename__ = 'helloworld'
pm_key = Column(Integer, primary_key=True)
x = Column(Integer, nullable=False)
c = Column(Integer,default=mydefault)
Is it possible to instance a table with its tablename?
I've looked for in SQLAlchemy documentation and I couldn't find anything.
class A():
__tablename__ = 'x'
newTable = Table('x')
Is possible something like this?
This is a pseudo-language, not real Python code
Thanks,
Create_a.py
import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class A(Base):
__tablename__ = 'X'
A_id = Column(Integer, primary_key=True)
A_name = Column(String(250), nullable=False)
engine = create_engine('sqlite:///sqlalchemy_example.db')
Base.metadata.create_all(engine)
Insert_a.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from Create_a import A, Base, engine
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
print A.__tablename__
A.__tablename__ = A
new_A = A.__tablename__(A_name='new A')
session.add(new_A)
session.commit()
Were A.__tablename__ is X
Given some model Foo:
class Foo(Base):
__tablename__ = 'foos'
...
the associated table object can be accessed directly via the __table__ attribute (provided that the entity has been mapped, for example via Base.metadata.create_all):
tbl = Foo.__table__
If only the value of Foo.__tablename__ is available the table can be retrieved using reflection:
tbl = sa.Table('foos', sa.MetaData(), autoload_with=engine)
I went searching w/o result in a way to get the integer value or the boolean value from an object model created via sqlalchemy,
I mean i can add it and it works flawless but i cant get the integer value or the boolean value all i get when i tried to print it is the object name:
from sqlalchemy import create_engine, MetaData, Table, Column,Integer,String,Boolean,Sequence
from sqlalchemy.orm import mapper, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
import json
class Bookmarks(object):
pass
#----------------------------------------------------------------------
engine = create_engine('postgresql://u:p#localghost/asd', echo=True)
Base = declarative_base()
class Tramo(Base):
__tablename__ = 'tramos'
__mapper_args__ = {'column_prefix':'tramos'}
id = Column(Integer, primary_key=True)
nombre = Column(String)
tramo_data = Column(String)
estado = Column(Boolean,default=True)
def __init__(self,nombre,tramo_data):
self.nombre=nombre
self.tramo_data=tramo_data
def __repr__(self):
return "[id:%s][nombre:%s][tramo:%s]" % (getattr(self, 'id'), self.nombre,self.tramo_data)
Session = sessionmaker(bind=engine)
session = Session()
tabla = Tramo.__table__
metadata = Base.metadata
metadata.create_all(engine)
b=Tramo('tramo1','adadas')
session.add(b)
session.commit()
print b
print b.id
its prints
[id:tramos.id][nombre:tramo1][tramo:adadas]
tramos.id
i cant get to print the id value, looks like the object column is in there but it doesn't return the value ot the property
i even use
session.refresh(b)
after the add but the result is the same.
According to the documentation Naming All Columns with a Prefix:
...prefix to the mapped attribute names relative to the
(table) column name ...
Since you define the mapped attributes in your class, I do not think it does what you desire.
Solution-1: remove the 'column_prefix':'tramos' from your __mapper_args__
Solution-2: print b.tramosid will print its id. You would need to change the __repr__ accordingly:
def __repr__(self):
return "[id:%s][nombre:%s][tramo:%s]" % (getattr(self, 'tramosid'), self.nombre, self.tramo_data)