""" This module contains functions that are triggered by SQLAlchemy events. These are useful for complex model validation, sending notifications (via fedmsg, for example), or anything else you can think. """ import collections import logging from sqlalchemy import event import fedmsg from .meta import Session from .models import Pet _log = logging.getLogger(__name__) @event.listens_for(Session, 'before_flush') def set_name(session, flush_context, instances): """ An SQLAlchemy event listener that sets the name of a Pet if it's null and performs validation on the name if it's not null. Args: session (sqlalchemy.orm.session.Session): The session that is about to be committed. flush_context (sqlalchemy.orm.session.UOWTransaction): Unused. instances (object): deprecated and unused Raises: ValueError: If the ecosystem_name isn't valid. """ for new_obj in session.new: if isinstance(new_obj, Pet): if new_obj.name is None: new_obj.name = 'Beelzepug' _log.info('Your %s is now named %s', new_obj.breed, new_obj.name) else: if new_obj.name not in ['Fido', 'Toto', 'Spot']: raise ValueError('Unacceptable name for your pet.') @event.listens_for(Session, 'after_commit') def send_fedmsgs_after_commit(session): """ An SQLAlchemy event listener to send fedmsgs after a database commit. This relies on the session ``info`` dictionary being populated. This is populated by :func:`queue_fedmsgs` prior to a commit. This ensures that messages are only emitted after the transaction has been successfully committed. Args: session (sqlalchemy.orm.session.Session): The session that was committed. """ if 'fedmsg' in session.info: for topic, messages in session.info['fedmsg'].items(): _log.info('emitting {n} fedmsgs to the "{topic}" topic.'.format( n=len(messages), topic=topic)) for msg in messages: fedmsg.publish(topic=topic, msg=msg) # Tidy up after ourselves so a second call to commit on this session won't # send the same messages again. del session.info['fedmsg'][topic] @event.listens_for(Session, 'before_commit') def queue_fedmsgs(session): """ An SQLAlchemy event listener that queues up fedmsgs for dispatch. This populates the session ``info`` dictionary with fedmsgs to send. The format of the ``info`` dict is:: { "fedmsg": { "topic1": [{"first_message": "body"}, {"second_message": "body"}], "topic2": [{"first_message": "body"}, {"second_message": "body"}], } } Args: session (sqlalchemy.orm.session.Session): The session that is about to be committed. """ if 'fedmsg' not in session.info: session.info['fedmsg'] = collections.defaultdict(list) for new_obj in session.new: session.info['fedmsg']['petshop.pet.new'].append(new_obj.as_dict()) for modified_obj in session.dirty: # Objects can end up in the dirty state even if no database-side changes occur. if session.is_modified(modified_obj): session.info['fedmsg']['petshop.pet.modified'].append(modified_obj.as_dict())