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()
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()
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
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