1.. _collections_toplevel: 2 3.. currentmodule:: sqlalchemy.orm 4 5======================================= 6Collection Configuration and Techniques 7======================================= 8 9The :func:`.relationship` function defines a linkage between two classes. 10When the linkage defines a one-to-many or many-to-many relationship, it's 11represented as a Python collection when objects are loaded and manipulated. 12This section presents additional information about collection configuration 13and techniques. 14 15.. _largecollections: 16.. currentmodule:: sqlalchemy.orm 17 18Working with Large Collections 19============================== 20 21The default behavior of :func:`.relationship` is to fully load 22the collection of items in, as according to the loading strategy of the 23relationship. Additionally, the :class:`.Session` by default only knows how to delete 24objects which are actually present within the session. When a parent instance 25is marked for deletion and flushed, the :class:`.Session` loads its full list of child 26items in so that they may either be deleted as well, or have their foreign key 27value set to null; this is to avoid constraint violations. For large 28collections of child items, there are several strategies to bypass full 29loading of child items both at load time as well as deletion time. 30 31.. _dynamic_relationship: 32 33Dynamic Relationship Loaders 34---------------------------- 35 36A key feature to enable management of a large collection is the so-called "dynamic" 37relationship. This is an optional form of :func:`~sqlalchemy.orm.relationship` which 38returns a :class:`~sqlalchemy.orm.query.Query` object in place of a collection 39when accessed. :func:`~sqlalchemy.orm.query.Query.filter` criterion may be 40applied as well as limits and offsets, either explicitly or via array slices:: 41 42 class User(Base): 43 __tablename__ = 'user' 44 45 posts = relationship(Post, lazy="dynamic") 46 47 jack = session.query(User).get(id) 48 49 # filter Jack's blog posts 50 posts = jack.posts.filter(Post.headline=='this is a post') 51 52 # apply array slices 53 posts = jack.posts[5:20] 54 55The dynamic relationship supports limited write operations, via the 56``append()`` and ``remove()`` methods:: 57 58 oldpost = jack.posts.filter(Post.headline=='old post').one() 59 jack.posts.remove(oldpost) 60 61 jack.posts.append(Post('new post')) 62 63Since the read side of the dynamic relationship always queries the 64database, changes to the underlying collection will not be visible 65until the data has been flushed. However, as long as "autoflush" is 66enabled on the :class:`.Session` in use, this will occur 67automatically each time the collection is about to emit a 68query. 69 70To place a dynamic relationship on a backref, use the :func:`~.orm.backref` 71function in conjunction with ``lazy='dynamic'``:: 72 73 class Post(Base): 74 __table__ = posts_table 75 76 user = relationship(User, 77 backref=backref('posts', lazy='dynamic') 78 ) 79 80Note that eager/lazy loading options cannot be used in conjunction dynamic relationships at this time. 81 82.. note:: 83 84 The :func:`~.orm.dynamic_loader` function is essentially the same 85 as :func:`~.orm.relationship` with the ``lazy='dynamic'`` argument specified. 86 87.. warning:: 88 89 The "dynamic" loader applies to **collections only**. It is not valid 90 to use "dynamic" loaders with many-to-one, one-to-one, or uselist=False 91 relationships. Newer versions of SQLAlchemy emit warnings or exceptions 92 in these cases. 93 94.. _collections_noload_raiseload: 95 96Setting Noload, RaiseLoad 97------------------------- 98 99A "noload" relationship never loads from the database, even when 100accessed. It is configured using ``lazy='noload'``:: 101 102 class MyClass(Base): 103 __tablename__ = 'some_table' 104 105 children = relationship(MyOtherClass, lazy='noload') 106 107Above, the ``children`` collection is fully writeable, and changes to it will 108be persisted to the database as well as locally available for reading at the 109time they are added. However when instances of ``MyClass`` are freshly loaded 110from the database, the ``children`` collection stays empty. The noload 111strategy is also available on a query option basis using the 112:func:`.orm.noload` loader option. 113 114Alternatively, a "raise"-loaded relationship will raise an 115:exc:`~sqlalchemy.exc.InvalidRequestError` where the attribute would normally 116emit a lazy load:: 117 118 class MyClass(Base): 119 __tablename__ = 'some_table' 120 121 children = relationship(MyOtherClass, lazy='raise') 122 123Above, attribute access on the ``children`` collection will raise an exception 124if it was not previously eagerloaded. This includes read access but for 125collections will also affect write access, as collections can't be mutated 126without first loading them. The rationale for this is to ensure that an 127application is not emitting any unexpected lazy loads within a certain context. 128Rather than having to read through SQL logs to determine that all necessary 129attributes were eager loaded, the "raise" strategy will cause unloaded 130attributes to raise immediately if accessed. The raise strategy is 131also available on a query option basis using the :func:`.orm.raiseload` 132loader option. 133 134.. versionadded:: 1.1 added the "raise" loader strategy. 135 136.. seealso:: 137 138 :ref:`prevent_lazy_with_raiseload` 139 140.. _passive_deletes: 141 142Using Passive Deletes 143--------------------- 144 145Use :paramref:`~.relationship.passive_deletes` to disable child object loading on a DELETE 146operation, in conjunction with "ON DELETE (CASCADE|SET NULL)" on your database 147to automatically cascade deletes to child objects:: 148 149 class MyClass(Base): 150 __tablename__ = 'mytable' 151 id = Column(Integer, primary_key=True) 152 children = relationship("MyOtherClass", 153 cascade="all, delete-orphan", 154 passive_deletes=True) 155 156 class MyOtherClass(Base): 157 __tablename__ = 'myothertable' 158 id = Column(Integer, primary_key=True) 159 parent_id = Column(Integer, 160 ForeignKey('mytable.id', ondelete='CASCADE') 161 ) 162 163 164.. note:: 165 166 To use "ON DELETE CASCADE", the underlying database engine must 167 support foreign keys. 168 169 * When using MySQL, an appropriate storage engine must be 170 selected. See :ref:`mysql_storage_engines` for details. 171 172 * When using SQLite, foreign key support must be enabled explicitly. 173 See :ref:`sqlite_foreign_keys` for details. 174 175When :paramref:`~.relationship.passive_deletes` is applied, the ``children`` relationship will not be 176loaded into memory when an instance of ``MyClass`` is marked for deletion. The 177``cascade="all, delete-orphan"`` *will* take effect for instances of 178``MyOtherClass`` which are currently present in the session; however for 179instances of ``MyOtherClass`` which are not loaded, SQLAlchemy assumes that 180"ON DELETE CASCADE" rules will ensure that those rows are deleted by the 181database. 182 183.. seealso:: 184 185 :paramref:`.orm.mapper.passive_deletes` - similar feature on :func:`.mapper` 186 187.. currentmodule:: sqlalchemy.orm.collections 188.. _custom_collections: 189 190Customizing Collection Access 191============================= 192 193Mapping a one-to-many or many-to-many relationship results in a collection of 194values accessible through an attribute on the parent instance. By default, 195this collection is a ``list``:: 196 197 class Parent(Base): 198 __tablename__ = 'parent' 199 parent_id = Column(Integer, primary_key=True) 200 201 children = relationship(Child) 202 203 parent = Parent() 204 parent.children.append(Child()) 205 print(parent.children[0]) 206 207Collections are not limited to lists. Sets, mutable sequences and almost any 208other Python object that can act as a container can be used in place of the 209default list, by specifying the :paramref:`~.relationship.collection_class` option on 210:func:`~sqlalchemy.orm.relationship`:: 211 212 class Parent(Base): 213 __tablename__ = 'parent' 214 parent_id = Column(Integer, primary_key=True) 215 216 # use a set 217 children = relationship(Child, collection_class=set) 218 219 parent = Parent() 220 child = Child() 221 parent.children.add(child) 222 assert child in parent.children 223 224Dictionary Collections 225---------------------- 226 227A little extra detail is needed when using a dictionary as a collection. 228This because objects are always loaded from the database as lists, and a key-generation 229strategy must be available to populate the dictionary correctly. The 230:func:`.attribute_mapped_collection` function is by far the most common way 231to achieve a simple dictionary collection. It produces a dictionary class that will apply a particular attribute 232of the mapped class as a key. Below we map an ``Item`` class containing 233a dictionary of ``Note`` items keyed to the ``Note.keyword`` attribute:: 234 235 from sqlalchemy import Column, Integer, String, ForeignKey 236 from sqlalchemy.orm import relationship 237 from sqlalchemy.orm.collections import attribute_mapped_collection 238 from sqlalchemy.ext.declarative import declarative_base 239 240 Base = declarative_base() 241 242 class Item(Base): 243 __tablename__ = 'item' 244 id = Column(Integer, primary_key=True) 245 notes = relationship("Note", 246 collection_class=attribute_mapped_collection('keyword'), 247 cascade="all, delete-orphan") 248 249 class Note(Base): 250 __tablename__ = 'note' 251 id = Column(Integer, primary_key=True) 252 item_id = Column(Integer, ForeignKey('item.id'), nullable=False) 253 keyword = Column(String) 254 text = Column(String) 255 256 def __init__(self, keyword, text): 257 self.keyword = keyword 258 self.text = text 259 260``Item.notes`` is then a dictionary:: 261 262 >>> item = Item() 263 >>> item.notes['a'] = Note('a', 'atext') 264 >>> item.notes.items() 265 {'a': <__main__.Note object at 0x2eaaf0>} 266 267:func:`.attribute_mapped_collection` will ensure that 268the ``.keyword`` attribute of each ``Note`` complies with the key in the 269dictionary. Such as, when assigning to ``Item.notes``, the dictionary 270key we supply must match that of the actual ``Note`` object:: 271 272 item = Item() 273 item.notes = { 274 'a': Note('a', 'atext'), 275 'b': Note('b', 'btext') 276 } 277 278The attribute which :func:`.attribute_mapped_collection` uses as a key 279does not need to be mapped at all! Using a regular Python ``@property`` allows virtually 280any detail or combination of details about the object to be used as the key, as 281below when we establish it as a tuple of ``Note.keyword`` and the first ten letters 282of the ``Note.text`` field:: 283 284 class Item(Base): 285 __tablename__ = 'item' 286 id = Column(Integer, primary_key=True) 287 notes = relationship("Note", 288 collection_class=attribute_mapped_collection('note_key'), 289 backref="item", 290 cascade="all, delete-orphan") 291 292 class Note(Base): 293 __tablename__ = 'note' 294 id = Column(Integer, primary_key=True) 295 item_id = Column(Integer, ForeignKey('item.id'), nullable=False) 296 keyword = Column(String) 297 text = Column(String) 298 299 @property 300 def note_key(self): 301 return (self.keyword, self.text[0:10]) 302 303 def __init__(self, keyword, text): 304 self.keyword = keyword 305 self.text = text 306 307Above we added a ``Note.item`` backref. Assigning to this reverse relationship, the ``Note`` 308is added to the ``Item.notes`` dictionary and the key is generated for us automatically:: 309 310 >>> item = Item() 311 >>> n1 = Note("a", "atext") 312 >>> n1.item = item 313 >>> item.notes 314 {('a', 'atext'): <__main__.Note object at 0x2eaaf0>} 315 316Other built-in dictionary types include :func:`.column_mapped_collection`, 317which is almost like :func:`.attribute_mapped_collection` except given the :class:`.Column` 318object directly:: 319 320 from sqlalchemy.orm.collections import column_mapped_collection 321 322 class Item(Base): 323 __tablename__ = 'item' 324 id = Column(Integer, primary_key=True) 325 notes = relationship("Note", 326 collection_class=column_mapped_collection(Note.__table__.c.keyword), 327 cascade="all, delete-orphan") 328 329as well as :func:`.mapped_collection` which is passed any callable function. 330Note that it's usually easier to use :func:`.attribute_mapped_collection` along 331with a ``@property`` as mentioned earlier:: 332 333 from sqlalchemy.orm.collections import mapped_collection 334 335 class Item(Base): 336 __tablename__ = 'item' 337 id = Column(Integer, primary_key=True) 338 notes = relationship("Note", 339 collection_class=mapped_collection(lambda note: note.text[0:10]), 340 cascade="all, delete-orphan") 341 342Dictionary mappings are often combined with the "Association Proxy" extension to produce 343streamlined dictionary views. See :ref:`proxying_dictionaries` and :ref:`composite_association_proxy` 344for examples. 345 346.. autofunction:: attribute_mapped_collection 347 348.. autofunction:: column_mapped_collection 349 350.. autofunction:: mapped_collection 351 352Custom Collection Implementations 353================================= 354 355You can use your own types for collections as well. In simple cases, 356inherting from ``list`` or ``set``, adding custom behavior, is all that's needed. 357In other cases, special decorators are needed to tell SQLAlchemy more detail 358about how the collection operates. 359 360.. topic:: Do I need a custom collection implementation? 361 362 In most cases not at all! The most common use cases for a "custom" collection 363 is one that validates or marshals incoming values into a new form, such as 364 a string that becomes a class instance, or one which goes a 365 step beyond and represents the data internally in some fashion, presenting 366 a "view" of that data on the outside of a different form. 367 368 For the first use case, the :func:`.orm.validates` decorator is by far 369 the simplest way to intercept incoming values in all cases for the purposes 370 of validation and simple marshaling. See :ref:`simple_validators` 371 for an example of this. 372 373 For the second use case, the :ref:`associationproxy_toplevel` extension is a 374 well-tested, widely used system that provides a read/write "view" of a 375 collection in terms of some attribute present on the target object. As the 376 target attribute can be a ``@property`` that returns virtually anything, a 377 wide array of "alternative" views of a collection can be constructed with 378 just a few functions. This approach leaves the underlying mapped collection 379 unaffected and avoids the need to carefully tailor collection behavior on a 380 method-by-method basis. 381 382 Customized collections are useful when the collection needs to 383 have special behaviors upon access or mutation operations that can't 384 otherwise be modeled externally to the collection. They can of course 385 be combined with the above two approaches. 386 387Collections in SQLAlchemy are transparently *instrumented*. Instrumentation 388means that normal operations on the collection are tracked and result in 389changes being written to the database at flush time. Additionally, collection 390operations can fire *events* which indicate some secondary operation must take 391place. Examples of a secondary operation include saving the child item in the 392parent's :class:`~sqlalchemy.orm.session.Session` (i.e. the ``save-update`` 393cascade), as well as synchronizing the state of a bi-directional relationship 394(i.e. a :func:`.backref`). 395 396The collections package understands the basic interface of lists, sets and 397dicts and will automatically apply instrumentation to those built-in types and 398their subclasses. Object-derived types that implement a basic collection 399interface are detected and instrumented via duck-typing: 400 401.. sourcecode:: python+sql 402 403 class ListLike(object): 404 def __init__(self): 405 self.data = [] 406 def append(self, item): 407 self.data.append(item) 408 def remove(self, item): 409 self.data.remove(item) 410 def extend(self, items): 411 self.data.extend(items) 412 def __iter__(self): 413 return iter(self.data) 414 def foo(self): 415 return 'foo' 416 417``append``, ``remove``, and ``extend`` are known list-like methods, and will 418be instrumented automatically. ``__iter__`` is not a mutator method and won't 419be instrumented, and ``foo`` won't be either. 420 421Duck-typing (i.e. guesswork) isn't rock-solid, of course, so you can be 422explicit about the interface you are implementing by providing an 423``__emulates__`` class attribute:: 424 425 class SetLike(object): 426 __emulates__ = set 427 428 def __init__(self): 429 self.data = set() 430 def append(self, item): 431 self.data.add(item) 432 def remove(self, item): 433 self.data.remove(item) 434 def __iter__(self): 435 return iter(self.data) 436 437This class looks list-like because of ``append``, but ``__emulates__`` forces 438it to set-like. ``remove`` is known to be part of the set interface and will 439be instrumented. 440 441But this class won't work quite yet: a little glue is needed to adapt it for 442use by SQLAlchemy. The ORM needs to know which methods to use to append, 443remove and iterate over members of the collection. When using a type like 444``list`` or ``set``, the appropriate methods are well-known and used 445automatically when present. This set-like class does not provide the expected 446``add`` method, so we must supply an explicit mapping for the ORM via a 447decorator. 448 449Annotating Custom Collections via Decorators 450-------------------------------------------- 451 452Decorators can be used to tag the individual methods the ORM needs to manage 453collections. Use them when your class doesn't quite meet the regular interface 454for its container type, or when you otherwise would like to use a different method to 455get the job done. 456 457.. sourcecode:: python 458 459 from sqlalchemy.orm.collections import collection 460 461 class SetLike(object): 462 __emulates__ = set 463 464 def __init__(self): 465 self.data = set() 466 467 @collection.appender 468 def append(self, item): 469 self.data.add(item) 470 471 def remove(self, item): 472 self.data.remove(item) 473 474 def __iter__(self): 475 return iter(self.data) 476 477And that's all that's needed to complete the example. SQLAlchemy will add 478instances via the ``append`` method. ``remove`` and ``__iter__`` are the 479default methods for sets and will be used for removing and iteration. Default 480methods can be changed as well: 481 482.. sourcecode:: python+sql 483 484 from sqlalchemy.orm.collections import collection 485 486 class MyList(list): 487 @collection.remover 488 def zark(self, item): 489 # do something special... 490 491 @collection.iterator 492 def hey_use_this_instead_for_iteration(self): 493 # ... 494 495There is no requirement to be list-, or set-like at all. Collection classes 496can be any shape, so long as they have the append, remove and iterate 497interface marked for SQLAlchemy's use. Append and remove methods will be 498called with a mapped entity as the single argument, and iterator methods are 499called with no arguments and must return an iterator. 500 501.. autoclass:: collection 502 :members: 503 504.. _dictionary_collections: 505 506Custom Dictionary-Based Collections 507----------------------------------- 508 509The :class:`.MappedCollection` class can be used as 510a base class for your custom types or as a mix-in to quickly add ``dict`` 511collection support to other classes. It uses a keying function to delegate to 512``__setitem__`` and ``__delitem__``: 513 514.. sourcecode:: python+sql 515 516 from sqlalchemy.util import OrderedDict 517 from sqlalchemy.orm.collections import MappedCollection 518 519 class NodeMap(OrderedDict, MappedCollection): 520 """Holds 'Node' objects, keyed by the 'name' attribute with insert order maintained.""" 521 522 def __init__(self, *args, **kw): 523 MappedCollection.__init__(self, keyfunc=lambda node: node.name) 524 OrderedDict.__init__(self, *args, **kw) 525 526When subclassing :class:`.MappedCollection`, user-defined versions 527of ``__setitem__()`` or ``__delitem__()`` should be decorated 528with :meth:`.collection.internally_instrumented`, **if** they call down 529to those same methods on :class:`.MappedCollection`. This because the methods 530on :class:`.MappedCollection` are already instrumented - calling them 531from within an already instrumented call can cause events to be fired off 532repeatedly, or inappropriately, leading to internal state corruption in 533rare cases:: 534 535 from sqlalchemy.orm.collections import MappedCollection,\ 536 collection 537 538 class MyMappedCollection(MappedCollection): 539 """Use @internally_instrumented when your methods 540 call down to already-instrumented methods. 541 542 """ 543 544 @collection.internally_instrumented 545 def __setitem__(self, key, value, _sa_initiator=None): 546 # do something with key, value 547 super(MyMappedCollection, self).__setitem__(key, value, _sa_initiator) 548 549 @collection.internally_instrumented 550 def __delitem__(self, key, _sa_initiator=None): 551 # do something with key 552 super(MyMappedCollection, self).__delitem__(key, _sa_initiator) 553 554The ORM understands the ``dict`` interface just like lists and sets, and will 555automatically instrument all dict-like methods if you choose to subclass 556``dict`` or provide dict-like collection behavior in a duck-typed class. You 557must decorate appender and remover methods, however- there are no compatible 558methods in the basic dictionary interface for SQLAlchemy to use by default. 559Iteration will go through ``itervalues()`` unless otherwise decorated. 560 561.. note:: 562 563 Due to a bug in MappedCollection prior to version 0.7.6, this 564 workaround usually needs to be called before a custom subclass 565 of :class:`.MappedCollection` which uses :meth:`.collection.internally_instrumented` 566 can be used:: 567 568 from sqlalchemy.orm.collections import _instrument_class, MappedCollection 569 _instrument_class(MappedCollection) 570 571 This will ensure that the :class:`.MappedCollection` has been properly 572 initialized with custom ``__setitem__()`` and ``__delitem__()`` 573 methods before used in a custom subclass. 574 575.. autoclass:: sqlalchemy.orm.collections.MappedCollection 576 :members: 577 578Instrumentation and Custom Types 579-------------------------------- 580 581Many custom types and existing library classes can be used as a entity 582collection type as-is without further ado. However, it is important to note 583that the instrumentation process will modify the type, adding decorators 584around methods automatically. 585 586The decorations are lightweight and no-op outside of relationships, but they 587do add unneeded overhead when triggered elsewhere. When using a library class 588as a collection, it can be good practice to use the "trivial subclass" trick 589to restrict the decorations to just your usage in relationships. For example: 590 591.. sourcecode:: python+sql 592 593 class MyAwesomeList(some.great.library.AwesomeList): 594 pass 595 596 # ... relationship(..., collection_class=MyAwesomeList) 597 598The ORM uses this approach for built-ins, quietly substituting a trivial 599subclass when a ``list``, ``set`` or ``dict`` is used directly. 600 601Collection Internals 602==================== 603 604Various internal methods. 605 606.. autofunction:: bulk_replace 607 608.. autoclass:: collection 609 610.. autodata:: collection_adapter 611 612.. autoclass:: CollectionAdapter 613 614.. autoclass:: InstrumentedDict 615 616.. autoclass:: InstrumentedList 617 618.. autoclass:: InstrumentedSet 619 620.. autofunction:: prepare_instrumentation 621