コード例 #1
0
ファイル: handlers.py プロジェクト: zworkb/python-sync-db
def handle_query(data, session=None):
    "Responds to a query request."
    model = core.synched_models.model_names.\
        get(data.get('model', None), core.null_model).model
    if model is None: return None
    mname = model.__name__
    filters = dict((k, v) for k, v in ((k[len(mname) + 1:], v)
                                       for k, v in list(data.items())
                                       if k.startswith(mname + '_'))
                   if k and k in column_properties(model))
    message = BaseMessage()
    q = query_model(session, model)
    if filters:
        q = q.filter_by(**filters)
    for obj in q:
        message.add_object(obj)
    return message.to_json()
コード例 #2
0
ファイル: handlers.py プロジェクト: bintlabs/python-sync-db
def handle_query(data, session=None):
    "Responds to a query request."
    model = core.synched_models.model_names.\
        get(data.get('model', None), core.null_model).model
    if model is None: return None
    mname = model.__name__
    filters = dict((k, v) for k, v in ((k[len(mname) + 1:], v)
                                       for k, v in data.iteritems()
                                       if k.startswith(mname + '_'))
                   if k and k in column_properties(model))
    message = BaseMessage()
    q = query_model(session, model)
    if filters:
        q = q.filter_by(**filters)
    for obj in q:
        message.add_object(obj)
    return message.to_json()
コード例 #3
0
def find_unique_conflicts(pull_ops, unversioned_ops, pull_message, session):
    """
    Unique constraints violated in a model. Returns two lists of
    dictionaries, the first one with the solvable conflicts, and the
    second one with the proper errors. Each conflict is a dictionary
    with the following fields::

        object: the local conflicting object, bound to the session
        columns: tuple of column names in the unique constraint
        new_values: tuple of values that can be used to update the
                    conflicting object

    Each error is a dictionary with the following fields::

        model: the model (class) of the conflicting object
        pk: the value of the primary key of the conflicting object
        columns: tuple of column names in the unique constraint
    """
    def verify_constraint(model, columns, values):
        """
        Checks to see whether some local object exists with
        conflicting values.
        """
        match = query_model(session, model, only_pk=True).\
            options(*(undefer(column) for column in columns)).\
            filter_by(**dict((column, value)
                             for column, value in izip(columns, values))).first()
        pk = get_pk(model)
        return match, getattr(match, pk, None)

    def get_remote_values(model, row_id, columns):
        """
        Gets the conflicting values out of the remote object set
        (*container*).
        """
        obj = pull_message.query(model).filter(
            attr('__pk__') == row_id).first()
        if obj is not None:
            return tuple(getattr(obj, column) for column in columns)
        return (None, )

    # keyed to content type
    unversioned_pks = dict(
        (ct_id,
         set(op.row_id for op in unversioned_ops if op.content_type_id == ct_id
             if op.command != 'd')) for ct_id in set(
                 operation.content_type_id for operation in unversioned_ops))
    # the lists to fill with conflicts and errors
    conflicts, errors = [], []

    for op in pull_ops:
        model = op.tracked_model

        for constraint in ifilter(
                lambda c: isinstance(c, UniqueConstraint),
                class_mapper(model).mapped_table.constraints):

            unique_columns = tuple(col.name for col in constraint.columns)
            # Unique values on the server, to check conflicts with local database
            remote_values = get_remote_values(model, op.row_id, unique_columns)

            obj_conflict, pk_conflict = verify_constraint(
                model, unique_columns, remote_values)

            is_unversioned = pk_conflict in unversioned_pks.get(
                op.content_type_id, set())

            if all(value is None for value in remote_values):
                continue  # Null value
            if pk_conflict is None: continue  # No problem
            if pk_conflict == op.row_id:
                if op.command == 'i':
                    # Two nodes created objects with the same unique
                    # value and same pk
                    errors.append({
                        'model': type(obj_conflict),
                        'pk': pk_conflict,
                        'columns': unique_columns
                    })
                continue

            # if pk_conflict != op.row_id:
            remote_obj = pull_message.query(model).\
                filter(attr('__pk__') == pk_conflict).first()

            if remote_obj is not None and not is_unversioned:
                old_values = tuple(
                    getattr(obj_conflict, column) for column in unique_columns)
                # The new unique value of the conflictive object
                # in server
                new_values = tuple(
                    getattr(remote_obj, column) for column in unique_columns)

                if old_values != new_values:
                    # Library error
                    # It's necesary to first update the unique value
                    session.refresh(obj_conflict,
                                    column_properties(obj_conflict))
                    conflicts.append({
                        'object': obj_conflict,
                        'columns': unique_columns,
                        'new_values': new_values
                    })
                else:
                    # The server allows two identical unique values
                    # This should be impossible
                    pass
            elif remote_obj is not None and is_unversioned:
                # Two nodes created objects with the same unique
                # values. Human error.
                errors.append({
                    'model': type(obj_conflict),
                    'pk': pk_conflict,
                    'columns': unique_columns
                })
            else:
                # The conflicting object hasn't been modified on the
                # server, which must mean the local user is attempting
                # an update that collides with one from another user.
                errors.append({
                    'model': type(obj_conflict),
                    'pk': pk_conflict,
                    'columns': unique_columns
                })
    return conflicts, errors
コード例 #4
0
ファイル: conflicts.py プロジェクト: bintlabs/python-sync-db
def find_unique_conflicts(pull_ops, unversioned_ops, pull_message, session):
    """
    Unique constraints violated in a model. Returns two lists of
    dictionaries, the first one with the solvable conflicts, and the
    second one with the proper errors. Each conflict is a dictionary
    with the following fields::

        object: the local conflicting object, bound to the session
        columns: tuple of column names in the unique constraint
        new_values: tuple of values that can be used to update the
                    conflicting object

    Each error is a dictionary with the following fields::

        model: the model (class) of the conflicting object
        pk: the value of the primary key of the conflicting object
        columns: tuple of column names in the unique constraint
    """

    def verify_constraint(model, columns, values):
        """
        Checks to see whether some local object exists with
        conflicting values.
        """
        match = query_model(session, model, only_pk=True).\
            options(*(undefer(column) for column in columns)).\
            filter_by(**dict((column, value)
                             for column, value in izip(columns, values))).first()
        pk = get_pk(model)
        return match, getattr(match, pk, None)

    def get_remote_values(model, row_id, columns):
        """
        Gets the conflicting values out of the remote object set
        (*container*).
        """
        obj = pull_message.query(model).filter(attr('__pk__') == row_id).first()
        if obj is not None:
            return tuple(getattr(obj, column) for column in columns)
        return (None,)

    # keyed to content type
    unversioned_pks = dict((ct_id, set(op.row_id for op in unversioned_ops
                                       if op.content_type_id == ct_id
                                       if op.command != 'd'))
                           for ct_id in set(operation.content_type_id
                                            for operation in unversioned_ops))
    # the lists to fill with conflicts and errors
    conflicts, errors = [], []

    for op in pull_ops:
        model = op.tracked_model

        for constraint in ifilter(lambda c: isinstance(c, UniqueConstraint),
                                  class_mapper(model).mapped_table.constraints):

            unique_columns = tuple(col.name for col in constraint.columns)
            # Unique values on the server, to check conflicts with local database
            remote_values = get_remote_values(model, op.row_id, unique_columns)

            obj_conflict, pk_conflict = verify_constraint(
                model, unique_columns, remote_values)

            is_unversioned = pk_conflict in unversioned_pks.get(
                op.content_type_id, set())

            if all(value is None for value in remote_values): continue # Null value
            if pk_conflict is None: continue # No problem
            if pk_conflict == op.row_id:
                if op.command == 'i':
                    # Two nodes created objects with the same unique
                    # value and same pk
                    errors.append(
                        {'model': type(obj_conflict),
                         'pk': pk_conflict,
                         'columns': unique_columns})
                continue

            # if pk_conflict != op.row_id:
            remote_obj = pull_message.query(model).\
                filter(attr('__pk__') == pk_conflict).first()

            if remote_obj is not None and not is_unversioned:
                old_values = tuple(getattr(obj_conflict, column)
                                   for column in unique_columns)
                # The new unique value of the conflictive object
                # in server
                new_values = tuple(getattr(remote_obj, column)
                                   for column in unique_columns)

                if old_values != new_values:
                    # Library error
                    # It's necesary to first update the unique value
                    session.refresh(obj_conflict, column_properties(obj_conflict))
                    conflicts.append(
                        {'object': obj_conflict,
                         'columns': unique_columns,
                         'new_values': new_values})
                else:
                    # The server allows two identical unique values
                    # This should be impossible
                    pass
            elif remote_obj is not None and is_unversioned:
                # Two nodes created objects with the same unique
                # values. Human error.
                errors.append(
                    {'model': type(obj_conflict),
                     'pk': pk_conflict,
                     'columns': unique_columns})
            else:
                # The conflicting object hasn't been modified on the
                # server, which must mean the local user is attempting
                # an update that collides with one from another user.
                errors.append(
                    {'model': type(obj_conflict),
                     'pk': pk_conflict,
                     'columns': unique_columns})
    return conflicts, errors