offsets, either explicitly or via array slices::
class User(Base):
- __tablename__ = 'user'
+ __tablename__ = "user"
posts = relationship(Post, lazy="dynamic")
+
jack = session.get(User, id)
# filter Jack's blog posts
- posts = jack.posts.filter(Post.headline=='this is a post')
+ posts = jack.posts.filter(Post.headline == "this is a post")
# apply array slices
posts = jack.posts[5:20]
The dynamic relationship supports limited write operations, via the
:meth:`_orm.AppenderQuery.append` and :meth:`_orm.AppenderQuery.remove` methods::
- oldpost = jack.posts.filter(Post.headline=='old post').one()
+ oldpost = jack.posts.filter(Post.headline == "old post").one()
jack.posts.remove(oldpost)
- jack.posts.append(Post('new post'))
+ jack.posts.append(Post("new post"))
Since the read side of the dynamic relationship always queries the
database, changes to the underlying collection will not be visible
class Post(Base):
__table__ = posts_table
- user = relationship(User,
- backref=backref('posts', lazy='dynamic')
- )
+ user = relationship(User, backref=backref("posts", lazy="dynamic"))
Note that eager/lazy loading options cannot be used in conjunction dynamic relationships at this time.
accessed. It is configured using ``lazy='noload'``::
class MyClass(Base):
- __tablename__ = 'some_table'
+ __tablename__ = "some_table"
- children = relationship(MyOtherClass, lazy='noload')
+ children = relationship(MyOtherClass, lazy="noload")
Above, the ``children`` collection is fully writeable, and changes to it will
be persisted to the database as well as locally available for reading at the
emit a lazy load::
class MyClass(Base):
- __tablename__ = 'some_table'
+ __tablename__ = "some_table"
- children = relationship(MyOtherClass, lazy='raise')
+ children = relationship(MyOtherClass, lazy="raise")
Above, attribute access on the ``children`` collection will raise an exception
if it was not previously eagerloaded. This includes read access but for
this collection is a ``list``::
class Parent(Base):
- __tablename__ = 'parent'
+ __tablename__ = "parent"
parent_id = Column(Integer, primary_key=True)
children = relationship(Child)
+
parent = Parent()
parent.children.append(Child())
print(parent.children[0])
:func:`~sqlalchemy.orm.relationship`::
class Parent(Base):
- __tablename__ = 'parent'
+ __tablename__ = "parent"
parent_id = Column(Integer, primary_key=True)
# use a set
children = relationship(Child, collection_class=set)
+
parent = Parent()
child = Child()
parent.children.add(child)
of the mapped class as a key. Below we map an ``Item`` class containing
a dictionary of ``Note`` items keyed to the ``Note.keyword`` attribute::
- from sqlalchemy import Column, Integer, String, ForeignKey
+ from sqlalchemy import Column, ForeignKey, Integer, String
+ from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm.collections import attribute_mapped_collection
- from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
+
class Item(Base):
- __tablename__ = 'item'
+ __tablename__ = "item"
id = Column(Integer, primary_key=True)
- notes = relationship("Note",
- collection_class=attribute_mapped_collection('keyword'),
- cascade="all, delete-orphan")
+ notes = relationship(
+ "Note",
+ collection_class=attribute_mapped_collection("keyword"),
+ cascade="all, delete-orphan",
+ )
+
class Note(Base):
- __tablename__ = 'note'
+ __tablename__ = "note"
id = Column(Integer, primary_key=True)
- item_id = Column(Integer, ForeignKey('item.id'), nullable=False)
+ item_id = Column(Integer, ForeignKey("item.id"), nullable=False)
keyword = Column(String)
text = Column(String)
``Item.notes`` is then a dictionary::
>>> item = Item()
- >>> item.notes['a'] = Note('a', 'atext')
+ >>> item.notes["a"] = Note("a", "atext")
>>> item.notes.items()
{'a': <__main__.Note object at 0x2eaaf0>}
item = Item()
item.notes = {
- 'a': Note('a', 'atext'),
- 'b': Note('b', 'btext')
- }
+ "a": Note("a", "atext"),
+ "b": Note("b", "btext"),
+ }
The attribute which :func:`.attribute_mapped_collection` uses as a key
does not need to be mapped at all! Using a regular Python ``@property`` allows virtually
of the ``Note.text`` field::
class Item(Base):
- __tablename__ = 'item'
+ __tablename__ = "item"
id = Column(Integer, primary_key=True)
- notes = relationship("Note",
- collection_class=attribute_mapped_collection('note_key'),
- backref="item",
- cascade="all, delete-orphan")
+ notes = relationship(
+ "Note",
+ collection_class=attribute_mapped_collection("note_key"),
+ backref="item",
+ cascade="all, delete-orphan",
+ )
+
class Note(Base):
- __tablename__ = 'note'
+ __tablename__ = "note"
id = Column(Integer, primary_key=True)
- item_id = Column(Integer, ForeignKey('item.id'), nullable=False)
+ item_id = Column(Integer, ForeignKey("item.id"), nullable=False)
keyword = Column(String)
text = Column(String)
from sqlalchemy.orm.collections import column_mapped_collection
+
class Item(Base):
- __tablename__ = 'item'
+ __tablename__ = "item"
id = Column(Integer, primary_key=True)
- notes = relationship("Note",
- collection_class=column_mapped_collection(Note.__table__.c.keyword),
- cascade="all, delete-orphan")
+ notes = relationship(
+ "Note",
+ collection_class=column_mapped_collection(Note.__table__.c.keyword),
+ cascade="all, delete-orphan",
+ )
as well as :func:`.mapped_collection` which is passed any callable function.
Note that it's usually easier to use :func:`.attribute_mapped_collection` along
from sqlalchemy.orm.collections import mapped_collection
+
class Item(Base):
- __tablename__ = 'item'
+ __tablename__ = "item"
id = Column(Integer, primary_key=True)
- notes = relationship("Note",
- collection_class=mapped_collection(lambda note: note.text[0:10]),
- cascade="all, delete-orphan")
+ notes = relationship(
+ "Note",
+ collection_class=mapped_collection(lambda note: note.text[0:10]),
+ cascade="all, delete-orphan",
+ )
Dictionary mappings are often combined with the "Association Proxy" extension to produce
streamlined dictionary views. See :ref:`proxying_dictionaries` and :ref:`composite_association_proxy`
Setting ``b1.data`` after the fact does not update the collection::
- >>> b1.data = 'the key'
+ >>> b1.data = "the key"
>>> a1.bs
{None: <test3.B object at 0x7f7b1023ef70>}
This can also be seen if one attempts to set up ``B()`` in the constructor.
The order of arguments changes the result::
- >>> B(a=a1, data='the key')
+ >>> B(a=a1, data="the key")
<test3.B object at 0x7f7b10114280>
>>> a1.bs
{None: <test3.B object at 0x7f7b10114280>}
vs::
- >>> B(data='the key', a=a1)
+ >>> B(data="the key", a=a1)
<test3.B object at 0x7f7b10114340>
>>> a1.bs
{'the key': <test3.B object at 0x7f7b10114340>}
collection as well::
from sqlalchemy import event
-
from sqlalchemy.orm import attributes
+
@event.listens_for(B.data, "set")
def set_item(obj, value, previous, initiator):
if obj.a is not None:
obj.a.bs[value] = obj
obj.a.bs.pop(previous)
-
-
.. autofunction:: attribute_mapped_collection
.. autofunction:: column_mapped_collection
repeatedly, or inappropriately, leading to internal state corruption in
rare cases::
- from sqlalchemy.orm.collections import MappedCollection,\
- collection
+ from sqlalchemy.orm.collections import MappedCollection, collection
+
class MyMappedCollection(MappedCollection):
"""Use @internally_instrumented when your methods
of :class:`.MappedCollection` which uses :meth:`.collection.internally_instrumented`
can be used::
- from sqlalchemy.orm.collections import _instrument_class, MappedCollection
+ from sqlalchemy.orm.collections import MappedCollection, _instrument_class
+
_instrument_class(MappedCollection)
This will ensure that the :class:`.MappedCollection` has been properly