SQLAlchemy with multiple primary keys does not automatically set any - python

I had a simple table:
class test(Base):
__tablename__ = 'test'
id = Column(Integer, primary_key=True)
title = Column(String)
def __init__(self, title):
self.title = title
When using this table, id was set automatically. I want to add another field that is unique and efficient to search, so I added the field:
id2 = Column(String, primary_key=True)
And updated the constructor:
def __init__(self, id2, title):
self.id2 = id2
self.title = title
Now, id is no longer automatically set, or rather I get the error:
IntegrityError: (IntegrityError) test.id may not be NULL u'INSERT INTO test (id2, title) VALUES (?, ?)' [u'a', u'b']
Is there a way to maintain a second primary key without removing the autoincrement behavior of the first?

I have few problems here
1) What is a purpose of your hand-made __init__? If it does just what you wrote, you can omit constructor completely since SQLAlchemy machinery generates exactly the same constructor for all your models automagically. Although if you take some additional actions and thus have to override __init__ you probably want to call super-constructor:
def __init__(self, lalala, *args, **kwargs):
# do something with lalala here...
super(test, self).__init__(*args, **kwargs)
# ...or here
2) Once you have more than one field with primary_key=True you get a model with composite primary key. Composite primary keys are not generated automatically since there is ambiguity here: how should subsequent key differ from previous?
I suspect what you're trying can be achieved using unique indexed column and not using composite key:
class test(Base):
__tablename__ = 'test'
id = Column(Integer, primary_key=True)
id2 = Column(String, index=True, unique=True)
title = Column(String)
# def __init__(self) is not necessary

Related

SQLAlchemy unique constrain by field

I have UniqueConstraint on field, but it wont allow me to add multiple entries (two is max!)
from sqlalchemy import Column, Integer, String, Boolean, UniqueConstraint
class Cart(SqlAlchemyBase):
__tablename__ = 'cart'
__table_args__ = (UniqueConstraint('is_latest'), {})
sid = Column(Integer, primary_key=True)
is_latest = Column(Boolean, index=True, nullable=False)
name = Column(String)
I would like to support more entries, so that one name can have two variants:
name=foo, is_latest=True
name=foo, is_latest=False
name=bar, is_latest=True
name=bar, is_latest=False
but then reject any subsequent attempt to write name=foo (or bar) and is_latest=True
What you are trying to achieve here is a type 2 slowly changing dimension, this is a topic that has been discussed extensively and I encourage you to look it up.
When I look at your table you seem to use sid as a surrogate key, but I fail to see what is the natural key and what will be updated as time goes.
Anyway, there are several ways to achieve SCD type 2 result without the need to worry about your check, but the the simplest in my mind is to keep on adding records with your natural key and when querying, select only the one with highest surrogate key (autoincrementing integer), no need for current uniqueness here as only the latest value is fetched.
There are examples for versioning rows in SQLAlchemy docs, but since website come and go, I'll put a simplified draft of the above approach here.
class VersionedItem(Versioned, Base):
id = Column(Integer, primary_key=True) # surrogate key
sku = Column(String, index=True) # natural key
price = Column(Integer) # the value that changes with time
#event.listens_for(Session, "before_flush")
def before_flush(session, flush_context, instances):
for instance in session.dirty:
if not (
isinstance(instance, VersionedItem)
and session.is_modified(instance)
and attributes.instance_state(instance).has_identity
):
continue
make_transient(instance) # remove db identity from instance
instance.id = None # remove surrogate key
session.add(instance) # insert instance as new record
Looks like a Partial Unique Index can be used:
class Cart(SqlAlchemyBase):
__tablename__ = 'cart'
id = Column(Integer, primary_key=True)
cart_id = Column(Integer)
is_latest = Column(Boolean, default=False)
name = Column(String)
__table_args__ = (
Index('only_one_latest_cart', name, is_latest,
unique=True,
postgresql_where=(is_latest)),
)
name=foo, is_latest = True
name=foo, is_latest = False
name=bar, is_latest = False
name=bar, is_latest = False
And when adding another name=foo, is_latest = True
psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "only_one_latest_cart"
DETAIL: Key (name, is_latest)=(foo, t) already exists.

SQLAlchemy how to create a relationship or mapping of a table's "type" row to the table model or mixin

I need to set the entity_type_id as a column value when I persist a row to a generic table of various entity_types. I should be able to load the entity_type_id for every specific instance at instantiation time because it is accessible via a simple select statement. I'd like to have that id automatically retrieved/set at the class (or instance) level without executing a query and/or manually setting to every time I persist a row of an "entity_type".
I tried an entity_type_id #property on the mixin that returns the id of the entity_type using the object_session but for reasons I don't fully understand the orm still inserts null as the entity_type_id value when I commit/flush the session. (my guess is having the "property" itself isn't the same thing as setting the attribute value on the instance and/or causing an issue because the column name from the base class has the same name)
Here's a slimmed down version of the relevant models in my schema:
class EntityType(Base):
__tablename__ = 'entity_type'
id = Column(UUID(as_uuid=True), primary_key=True, server_default=FetchedValue())
table_name = Column(String, nullable=False)
ui_label = Column(Text, unique=True, nullable=False)
entry_key = Column(Text, unique=True, nullable=False)
Base class model:
class TrackedEntity(Base):
#declared_attr
def __tablename__(cls):
return convert(cls.__name__)
__table_args__ = (
UniqueConstraint('entity_type_id', 'label'),
)
id = Column(UUID(as_uuid=True), primary_key=True, server_default=FetchedValue())
entity_type_id = Column('entity_type_id', ForeignKey('entity_type.id'))
label = Column('label', String, nullable=False)
entity_type = relationship('EntityType')
polymorphic_discriminator = column_property(select([EntityType.table_name]).where(EntityType.id == entity_type_id).as_scalar())
#declared_attr
def entity_type_label(cls):
return association_proxy('entity_type', 'label')
#declared_attr
def __mapper_args__(cls):
if cls.__name__ == 'TrackedEntity':
return {
"polymorphic_on": cls.polymorphic_discriminator,
"polymorphic_identity": cls.__tablename__
}
else:
return {"polymorphic_identity": cls.__tablename__}
Children class mixin:
class TrackedEntityMixin(object):
# noinspection PyMethodParameters
#declared_attr
def id(cls) -> Column:
return Column(ForeignKey('tracked_entity.id'), primary_key=True)
#gets me the id but isn't very helpful like this, still needs to be manually set like child.entity_type_id = child._entity_type_id
#property
def _entity_type_id(self):
return object_session(self). \
scalar(
select([EntityType.id]).
where(EntityType.table_name == self.__tablename__)
)
A child class model:
class DesignedMolecule(TrackedEntityMixin, TrackedEntity):
extra = Column('extra', String)
parents = relationship('TrackedEntity', secondary='mix_dm_parent_entity')

SQLAlchemy: Joined Inheritance with One-to-One Relationship

I'm trying to accomplish the following joined inheritance with one-to-one relationship structure with SQLAlchemy: Box and Item are both Elements, and Box has an Item.
The class definitions are as follows:
class Element(Base):
__tablename__ = 'element'
id = Column(Integer, primary_key=True)
el_type = Column(String)
__mapper_args__ = dict(
polymorphic_identity='element',
polymorphic_on=el_type,
)
class Box(Element):
# Joined table inheritance: Element.
__tablename__ = 'box'
id = Column(Integer, ForeignKey('element.id'), primary_key=True)
__mapper_args__ = dict(polymorphic_identity='box')
# One-to-one relationship: Item.
item = relationship('Item',
back_populates='box', uselist=False)
def __init__(self, item_name=None):
self.item = Item(item_name)
class Item(Element):
# Joined table inheritance: Element.
__tablename__ = 'item'
id = Column(Integer, ForeignKey('element.id'), primary_key=True)
__mapper_args__ = dict(polymorphic_identity='item')
# One-to-one relationship: Box.
box_id = Column(Integer, ForeignKey('box.id'))
box = relationship('Box',
back_populates='item')
name = Column(String)
def __init__(self, name):
self.name = name
When I try to instantiate b = Box('rock'), I get:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Box.item - 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.
Now I have gone to SQLAlchemy's page on this issue (great docs, I might add), but I'm not sure how to bridge the gap between their example and my situation. I thought this might fix it:
class Item(Element):
# ...
box = relationship('Box',
back_populates='item', foreign_keys=[id, box_id])
# ...
...but I get the same error.
What am I doing wrong?
UPDATE:
I have now tried using ForeignKeyConstraint constructs in an effort to more explicitly describe the desired behavior to SQLAlchemy; to no avail:
class Box(Element):
# ...
id = Column(Integer, primary_key=True)
# ...
ForeignKeyConstraint([id], ['element.id'])
# ...
class Item(Element):
# ...
id = Column(Integer, primary_key=True)
# ...
# One-to-one relationship: Box.
box_id = Column(Integer)
box = relationship('Box',
back_populates='item')
ForeignKeyConstraint([id, box_id], ['element.id', 'box.id'])
# ...
A simple AssertionError is thrown back at me (no description). Given that SQLAlchemy has a good track record of producing meaningful error messages, could this suggest that this is an unanticipated situation?

SQLAlchemy - operate with primary key instance inside model

I want to generate some unique URLs from IDs with the purpose of appending at the end of some endpoints. The result will be something like .../32dwr4. I would like to insert these short urls into the database on instantiation, based on the primary key id.
I do not know if there is some kind of 'flushing' for operating inside the model:
class Storm(db.Model):
id = db.Column(db.Integer, primary_key=True)
_url = db.Column(db.String(200), index=True)
## Relationships
#hybrid_property
def url(self):
return self._url
#url.setter
def _set_url(self, plaintext):
self._url= base64.b64encode(plaintext)
def __init__(self, name, question, *):
self.name = name
self.question = question
self.url = self.id # <<------ is it possible to pass its id on the fly, convert it through the setter and store it?
If it is not possible, which approach do you recommend?

SQLAlchemy - select data associated with foreign key, NOT the foreign key itself

I have two tables, Name and Person.
Name:
id (int, primary key)
name (varchar)
Person:
id (int, primary key)
name_id (int, foreign key->Name.id)
Assuming my models are set up with the foreign keys, if I run Person.query.first().name_id, this will return an integer. I want it to return the name varchar. Is this possible? Or is there something I can do to get the same result?
class Name(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text) # or varchar
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name_id = db.Column(db.Integer, db.ForeignKey('name.id'))
_name = db.relationship('Name')
#property
def name(self):
return self._name.name
Getting the actual name back could be done like this or with a select in the name function. I prefer this way with a property. You'll need to fill in the details with how you are using joins in the relationship and other details.
You could #property to#hybrid_property to get some neat functionality from SQLAlchemy. Of course, you need to use it effectively.

Categories