From 10cbcd2432e33dec64fbb5d0d1cde6b47b20f937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Fran=C3=A7oise?= Date: Thu, 6 Oct 2016 11:20:44 +0200 Subject: [PATCH] Added composite unique name constraints In this changeset, I added composite contraints on models that were currently subject to an implicit uniqueness. This composite constraint takes as an input the name and the deleted columns. The 'deleted' column is 0 by default and is updated with the value of the associated 'id' column upon soft deletion. What this means is that this composite constraint guarantees the fact that we can hold many soft deleted records holding the exact same name. Indeed, the value of the deleted column will be different whilst forbidding 2 non-soft deleted records with the same name as their associated 'deleted' value will be 0 and subsequently violates the constraint. Change-Id: I3ee39e23aa4ca9bc6b4ea9af8c6b7e6d67af0136 --- watcher/db/sqlalchemy/models.py | 22 +++++++++++++--------- watcher/tests/api/v1/test_strategies.py | 4 ++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/watcher/db/sqlalchemy/models.py b/watcher/db/sqlalchemy/models.py index be210ce04..6752b5206 100644 --- a/watcher/db/sqlalchemy/models.py +++ b/watcher/db/sqlalchemy/models.py @@ -27,10 +27,10 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import ForeignKey from sqlalchemy import Integer from sqlalchemy import Numeric -from sqlalchemy import schema from sqlalchemy import String from sqlalchemy import Text from sqlalchemy.types import TypeDecorator, TEXT +from sqlalchemy import UniqueConstraint from watcher.common import paths @@ -116,7 +116,8 @@ class Strategy(Base): __tablename__ = 'strategies' __table_args__ = ( - schema.UniqueConstraint('uuid', name='uniq_strategies0uuid'), + UniqueConstraint('uuid', name='uniq_strategies0uuid'), + UniqueConstraint('name', 'deleted', name='uniq_strategies0name'), table_args() ) id = Column(Integer, primary_key=True) @@ -132,7 +133,8 @@ class Goal(Base): __tablename__ = 'goals' __table_args__ = ( - schema.UniqueConstraint('uuid', name='uniq_goals0uuid'), + UniqueConstraint('uuid', name='uniq_goals0uuid'), + UniqueConstraint('name', 'deleted', name='uniq_goals0name'), table_args(), ) id = Column(Integer, primary_key=True) @@ -147,7 +149,8 @@ class AuditTemplate(Base): __tablename__ = 'audit_templates' __table_args__ = ( - schema.UniqueConstraint('uuid', name='uniq_audit_templates0uuid'), + UniqueConstraint('uuid', name='uniq_audit_templates0uuid'), + UniqueConstraint('name', 'deleted', name='uniq_audit_templates0name'), table_args() ) id = Column(Integer, primary_key=True) @@ -166,7 +169,7 @@ class Audit(Base): __tablename__ = 'audits' __table_args__ = ( - schema.UniqueConstraint('uuid', name='uniq_audits0uuid'), + UniqueConstraint('uuid', name='uniq_audits0uuid'), table_args() ) id = Column(Integer, primary_key=True) @@ -186,7 +189,7 @@ class Action(Base): __tablename__ = 'actions' __table_args__ = ( - schema.UniqueConstraint('uuid', name='uniq_actions0uuid'), + UniqueConstraint('uuid', name='uniq_actions0uuid'), table_args() ) id = Column(Integer, primary_key=True) @@ -205,7 +208,7 @@ class ActionPlan(Base): __tablename__ = 'action_plans' __table_args__ = ( - schema.UniqueConstraint('uuid', name='uniq_action_plans0uuid'), + UniqueConstraint('uuid', name='uniq_action_plans0uuid'), table_args() ) id = Column(Integer, primary_key=True) @@ -222,7 +225,7 @@ class EfficacyIndicator(Base): __tablename__ = 'efficacy_indicators' __table_args__ = ( - schema.UniqueConstraint('uuid', name='uniq_efficacy_indicators0uuid'), + UniqueConstraint('uuid', name='uniq_efficacy_indicators0uuid'), table_args() ) id = Column(Integer, primary_key=True) @@ -240,7 +243,8 @@ class ScoringEngine(Base): __tablename__ = 'scoring_engines' __table_args__ = ( - schema.UniqueConstraint('uuid', name='uniq_scoring_engines0uuid'), + UniqueConstraint('uuid', name='uniq_scoring_engines0uuid'), + UniqueConstraint('name', 'deleted', name='uniq_scoring_engines0name'), table_args() ) id = Column(Integer, primary_key=True) diff --git a/watcher/tests/api/v1/test_strategies.py b/watcher/tests/api/v1/test_strategies.py index 5ed7fdac2..34121a800 100644 --- a/watcher/tests/api/v1/test_strategies.py +++ b/watcher/tests/api/v1/test_strategies.py @@ -145,11 +145,13 @@ class TestListStrategy(api_base.FunctionalTest): obj_utils.create_test_strategy( self.context, id=id_, uuid=utils.generate_uuid(), + name='Goal %s' % id_, goal_id=goal1['id']) for id_ in range(3, 5): obj_utils.create_test_strategy( self.context, id=id_, uuid=utils.generate_uuid(), + name='Goal %s' % id_, goal_id=goal2['id']) response = self.get_json('/strategies/?goal=%s' % goal1['uuid']) @@ -175,11 +177,13 @@ class TestListStrategy(api_base.FunctionalTest): obj_utils.create_test_strategy( self.context, id=id_, uuid=utils.generate_uuid(), + name='Goal %s' % id_, goal_id=goal1['id']) for id_ in range(3, 5): obj_utils.create_test_strategy( self.context, id=id_, uuid=utils.generate_uuid(), + name='Goal %s' % id_, goal_id=goal2['id']) response = self.get_json('/strategies/?goal=%s' % goal1['name'])