Delete attribute from SQLAlchemy model object - python

I'm trying to delete an attribute from model object that is created on the go, but there seems to be some problem doing so. So, far I've searched through every place including SQLAlchemy documentation and some of its code to find a potential fix but unable to find one.
Below code works fine on python classes but not with a class inherited from declarative_base
for row in data:
model_obj = DBEngine.models.User()
[setattr(model_obj, key, value) for key, value in row.items()]
# below line doesn't work as expected, instead of deleting the
# attribute it just sets the value of attribute to None
delattr(model_obj, 'localedit')
session.add(model_obj)
User model
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column('id', Integer, primary_key=True)
localedit = Column('localedit', String, default="0000-00-00 00:00:00")
I've also tried deleting the attribute using del but it's doing the same job as delattr, I guess both del and delattr calling the same code under the hood.
I'm totally stumped on this problem and couldn't come up with anything.
Any help would be appreciated, Thanks.

So this was expected, as SQLAlchemy ORM mapped objects don't support this particular state for an attribute, that is, attribute doesn't exist and would raise AttributeError. for an ORM mapped class, a mapped attribute always defaults to None and/or empty collection. there's a little bit of an introduction to this here: https://docs.sqlalchemy.org/en/14/tutorial/orm_data_manipulation.html#instances-of-classes-represent-rows
For this particular problem, you can define your column as
localedit = Column('localedit', String, FetchedValue())
FetchedValue is used when the database is configured to provide some automatic default for a column. So, in this case you only want to ignore the column this would work like a charm. Updated model would look something like:
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column('id', Integer, primary_key=True)
localedit = Column('localedit', String, FetchedValue())

Related

Python delete attribute method is deleting the value not the attribute

In my flask admin app, I have a sqlalchemy orm model, old_model. I want to delete an attribute, action_id from the object. I am doing
del old_model.action_id
I expect the attribute action_id to be removed from the object old_model. Instead of getting deleted, its value is updated to None and the attribute still exists. How to delete the attribute completely along with the value?
So this was expected, as SQLAlchemy ORM mapped objects don't support this particular state for an attribute, that is, attribute doesn't exist and would raise AttributeError. for an ORM mapped class, a mapped attribute always defaults to None and/or empty collection. there's a little bit of an introduction to this here: https://docs.sqlalchemy.org/en/14/tutorial/orm_data_manipulation.html#instances-of-classes-represent-rows
For this particular problem, you can define your column as
id = Column('id', String, FetchedValue())
FetchedValue is used when the database is configured to provide some automatic default for a column. So, in this case you only want to ignore the column this would work like a charm. Updated model would look something like:
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
pid = Column('id', Integer, primary_key=True)
id = Column('id', String, FetchedValue())

slqlalchemy UniqueConstraint VS Index(unique=True)

I am using MySQL (running InnoDB), and wrapped the entire thing using sqlalchemy. Now, I would like to generate changes in my database by using (see docs)
sqlalchemy_utils.functions.create_database(...)
Generally the above function does what it is supposed to. The only exception being the generation of unique indexes.
Say, I define a table like this:
## ...
# DeclBase = declarative_base()
## ...
class MyTable(DeclBase):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True)
attr_1 = Column(String(32))
attr_2 = Column(Integer, nullable=False)
attr_3 = Column(DateTime)
attr_4 = Column(
Integer,
ForeignKey('other_table.id', onupdate='CASCADE', ondelete='CASCADE'),
nullable=False
)
u_idx = UniqueConstraint(attr_2, attr_3, 'my_table_uidx')
when I call create_database I will get sqlalchemy to create the table 'my_table' with all columns as specified. The foreign key is also setup fine, but no unique index can be found on the database side. I then tried using a Index(unique=True) instead. So instead of
u_idx = UniqueConstraint(attr_2, attr_3, 'my_table_uidx')
I put
u_idx_1 = Index('my_table_uidx', attr_2, attr_3, unique=True)
My impression was this logically produces a similar result. This time sqlalchemy indeed created the unique index on the db.
Maybe I am miserably misunderstanding something about the difference between UniqueConstraint and Index(unique=True), or the way sqlalchemy uses them to automate generation of databases.
Can anyone shed some light on this?
The main difference is that while the Index API allows defining an index outside of a table definition as long as it can reference the table through the passed SQL constructs, a UniqueConstraint and constraints in general must be defined inline in the table definition:
To apply table-level constraint objects such as ForeignKeyConstraint to a table defined using Declarative, use the __table_args__ attribute, described at Table Configuration.
The thing to understand is that during construction of a declarative class a new Table is constructed, if not passed an explicit __table__. In your example model class the UniqueConstraint instance is bound to a class attribute, but the declarative base does not include constraints in the created Table instance from attributes. You must pass it in the table arguments:
class MyTable(DeclBase):
__tablename__ = 'my_table'
...
# A positional argument tuple, passed to Table constructor
__table_args__ = (
UniqueConstraint(attr_2, attr_3, name='my_table_uidx'),
)
Note that you must pass the constraint name as a keyword argument. You could also pass the constraint using Table.append_constraint(), if called before any attempts to create the table:
class MyTable(DeclBase):
...
MyTable.__table__.append_constraint(
UniqueConstraint('attr_2', 'attr_3', name='my_table_uidx'))

SQLAlchemy - Multiple Classes, Identical Tables

Been searching for this answer for a bit now, but I can't seem to find it, as everything refers back to joined table inheritance, which I understand, but do not want to use. I am looking to create multiple classes in SQLAlchemy that are identical in table construction, only differing in the class name and database table name. I am intentionally separating the tables and not using a discriminator because I expect these tables to grow to very large sizes. There is also a potential that the table schemas may diverge slowly over time, with some fields added to one but not another.
I know the following code doesn't work, as SQLAlchemy tries to find a foreign key for joined table inheritance rather than making them independent tables, but it's basically what I'm going for. I've been over the docs, but can't figure out the way to properly implement this. Is there a way (or multiple ways) to do this?
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class HDD(Base):
"""Class representing a hard drive."""
__tablename__ = 'HDDs'
_id = Column(
Integer,
doc="Auto-incrementing primary key",
name="id",
primary_key=True)
manufacturer = Column(
String(40),
doc="Hard drive manufacturer.")
...
class SDD(HDD):
__tablename__ = 'SSDs'
...
Use __abstract__ = True with no __tablename__ on the base class, and inherit from that.
class HDD(Base):
__abstract__ = True
_id = ...
class SDD(HDD):
__tablename__ = 'SSDs'
class SSD2(HDD):
__tablename = 'SSDs2'
This looks like Concrete Table Inheritance, as discussed in the Mapping Class Inheritance Hierarchies chapter of SQLAlchemy docs.
This is the way I ended up getting this working, with help from inklesspen from #sqlalchemy on freenode. (Thank you!)
class ComponentMixin(object):
"""Pythonic Mixin that defines columns common to most component types."""
_id = Column(
Integer,
doc="Auto-incrementing primary key",
name="id",
primary_key=True)
....
#declared_attr
def _server_id(self): # pylint: disable=R0201
"""
Foreign key to owning server.
MUST BE A DECLARED_ATTR since it's a foreign key!
"""
Subclass below inherits:
class HDD(ComponentMixin, ConcreteBase, Base):
"""Class representing a hard drive."""
__tablename__ = 'HDDs'
__mapper_args__ = {
'polymorphic_identity': 'hdd',
'concrete': True}

How do you specify the foreign key column value in SQLAlchemy?

One of my models has the following relationship:
class User(Base):
account = relationship("Account")
I would like to set the account id manually.
My first attempt was this:
class User(Base):
account = relationship("Account")
accounts_id = Column(Integer, ForeignKey("accounts.id"), nullable=True)
#classmethod
def from_json(cls, json):
appointment = Appointment()
appointment.account_id = json["account_id"]
return appointment
The above dosen't work. We can't refer to this column because SQLAlchemy throws a fit. This is the exception:
sqlalchemy.exc.InvalidRequestError: Implicitly combining column users.accounts_id with column users.accounts_id under attribute 'accounts_id'. Please configure one or more attributes for these same-named columns explicitly.
I've tried hunting through the docs and expermiented with getting to the attribute numerous ways but I haven't been able to find, much less set it.
print(self.account.account_id)
print(self.account.relationhip)
print(self.account.properties)
print(self.account.primaryjoin)
Any ideas?
[Edit- added exception above]
Use the Account class to define the relationship, and add the backref keyword argument:
from sqlalchemy.orm import relationship
class User(Base):
accounts_id = Column(Integer, ForeignKey('account.id'))
class Account(Base):
users = relationship('User', backref='account')
When the backref keyword is used on a single relationship, it’s exactly the same as if the above two relationships were created individually using back_populates on each.
References
Linking Relationships with Backref
Controlling Cascade on Backrefs
SQLAlchemy ORM Examples

Database Relationship - Syntax

A question on the syntax involved in SQLAlchemy.
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
Why is it ForeignKey('child.id') and not ForeignKey("Child.id")?
Why is it relationship("Child") and not relationship("child")? Is there something fundamental about how databases and SQLAlchemy work that I don't understand which is why I have to ask this question? Thanks!
relationship(Child) is also valid. By capitalising inside string, sqlalchemy will look for respective model.
Relationship isn't sql standard so SQLAlchemy is using its own convention, whereas ForeignKey is SQL Standard so tablename.column is used.
In general: A relationship is defined on orm level while ForeignKey represents a database model. Now, it well might be the case that sqlalchemy is smart enough to figure from from the other, but if you keep this separation in mind, you are safe.
Specifically to your question: just read the documentation. Extract below (verbatim)
From relationship:
argument – a mapped class, or actual Mapper instance, representing the
target of the relationship.
argument may also be passed as a callable function which is evaluated
at mapper initialization time, and may be passed as a Python-evaluable
string when using Declarative.
From ForeignKey
column – A single target column for the key relationship. A Column
object or a column name as a string: tablename.columnkey or
schema.tablename.columnkey. columnkey is the key which has been
assigned to the column (defaults to the column name itself), unless
link_to_name is True in which case the rendered name of the column is
used.

Categories

Resources