2010-05-12 4 views
0

Je suis nouveau à SQLAlchemy (et SQL, d'ailleurs). Je n'arrive pas à comprendre comment coder l'idée que j'ai dans ma tête. Je crée une base de données de résultats de tests de performance.Comment coder cette relation dans SQLAlchemy?

Un essai se compose d'un type de test et un certain nombre (ce qui est classe TestRun ci-dessous)

Une suite de test se compose la chaîne de version du logiciel testé, et un ou plusieurs objets TestRun (ce qui est classe TestSuite au dessous de).

Une version de test est constituée de toutes les suites de tests avec le nom de version donné.

Voici mon code, aussi simple que je peux le faire:

from sqlalchemy import * 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import relationship, backref, sessionmaker 

Base = declarative_base() 

class TestVersion (Base): 
    __tablename__ = 'versions' 
    id = Column (Integer, primary_key=True) 
    version_name = Column (String) 

def __init__ (self, version_name): 
    self.version_name = version_name 


class TestRun (Base): 
    __tablename__ = 'runs' 
    id = Column (Integer, primary_key=True) 
    suite_directory = Column (String, ForeignKey ('suites.directory')) 
    suite = relationship ('TestSuite', backref=backref ('runs', order_by=id)) 
    test_type = Column (String) 
    rate = Column (Integer) 

    def __init__ (self, test_type, rate): 
     self.test_type = test_type 
     self.rate = rate 


class TestSuite (Base): 
    __tablename__ = 'suites' 
    directory = Column (String, primary_key=True) 
    version_id = Column (Integer, ForeignKey ('versions.id')) 
    version_ref = relationship ('TestVersion', backref=backref ('suites', order_by=directory)) 
    version_name = Column (String) 

    def __init__ (self, directory, version_name): 
     self.directory = directory 
     self.version_name = version_name 


# Create a v1.0 suite 
suite1 = TestSuite ('dir1', 'v1.0') 
suite1.runs.append (TestRun ('test1', 100)) 
suite1.runs.append (TestRun ('test2', 200)) 

# Create a another v1.0 suite 
suite2 = TestSuite ('dir2', 'v1.0') 
suite2.runs.append (TestRun ('test1', 101)) 
suite2.runs.append (TestRun ('test2', 201)) 

# Create another suite 
suite3 = TestSuite ('dir3', 'v2.0') 
suite3.runs.append (TestRun ('test1', 102)) 
suite3.runs.append (TestRun ('test2', 202)) 

# Create the in-memory database 
engine = create_engine ('sqlite://') 
Session = sessionmaker (bind=engine) 
session = Session() 
Base.metadata.create_all (engine) 

# Add the suites in 
version1 = TestVersion (suite1.version_name) 
version1.suites.append (suite1) 
session.add (suite1) 

version2 = TestVersion (suite2.version_name) 
version2.suites.append (suite2) 
session.add (suite2) 

version3 = TestVersion (suite3.version_name) 
version3.suites.append (suite3) 
session.add (suite3) 

session.commit() 

# Query the suites 
for suite in session.query (TestSuite).order_by (TestSuite.directory): 
    print "\nSuite directory %s, version %s has %d test runs:" % (suite.directory, suite.version_name, len (suite.runs)) 
    for run in suite.runs: 
     print " Test '%s', result %d" % (run.test_type, run.rate) 

# Query the versions 
for version in session.query (TestVersion).order_by (TestVersion.version_name): 
    print "\nVersion %s has %d test suites:" % (version.version_name, len (version.suites)) 
    for suite in version.suites: 
     print " Suite directory %s, version %s has %d test runs:" % (suite.directory, suite.version_name, len (suite.runs)) 
     for run in suite.runs: 
      print " Test '%s', result %d" % (run.test_type, run.rate) 

La sortie de ce programme:

Suite directory dir1, version v1.0 has 2 test runs: 
    Test 'test1', result 100 
    Test 'test2', result 200 

Suite directory dir2, version v1.0 has 2 test runs: 
    Test 'test1', result 101 
    Test 'test2', result 201 

Suite directory dir3, version v2.0 has 2 test runs: 
    Test 'test1', result 102 
    Test 'test2', result 202 

Version v1.0 has 1 test suites: 
    Suite directory dir1, version v1.0 has 2 test runs: 
    Test 'test1', result 100 
    Test 'test2', result 200 

Version v1.0 has 1 test suites: 
    Suite directory dir2, version v1.0 has 2 test runs: 
    Test 'test1', result 101 
    Test 'test2', result 201 

Version v2.0 has 1 test suites: 
    Suite directory dir3, version v2.0 has 2 test runs: 
    Test 'test1', result 102 
    Test 'test2', result 202 

Ce n'est pas correct, car il y a deux objets Testversion avec le nom 'v1.0'. Je piraté mon chemin en ajoutant une liste privée d'objets Testversion, et une fonction de trouver un correspondant à un:

versions = [] 
def find_or_create_version (version_name): 

    # Find existing 
    for version in versions: 
     if version.version_name == version_name: 
      return (version) 

    # Create new 
    version = TestVersion (version_name) 
    versions.append (version) 
    return (version) 

Je modifié mon code qui ajoute les enregistrements à utiliser:

# Add the suites in 
version1 = find_or_create_version (suite1.version_name) 
version1.suites.append (suite1) 
session.add (suite1) 

version2 = find_or_create_version (suite2.version_name) 
version2.suites.append (suite2) 
session.add (suite2) 

version3 = find_or_create_version (suite3.version_name) 
version3.suites.append (suite3) 
session.add (suite3) 

maintenant, la sortie est ce que je veux:

Suite directory dir1, version v1.0 has 2 test runs: 
    Test 'test1', result 100 
    Test 'test2', result 200 

Suite directory dir2, version v1.0 has 2 test runs: 
    Test 'test1', result 101 
    Test 'test2', result 201 

Suite directory dir3, version v2.0 has 2 test runs: 
    Test 'test1', result 102 
    Test 'test2', result 202 

Version v1.0 has 2 test suites: 
    Suite directory dir1, version v1.0 has 2 test runs: 
    Test 'test1', result 100 
    Test 'test2', result 200 
    Suite directory dir2, version v1.0 has 2 test runs: 
    Test 'test1', result 101 
    Test 'test2', result 201 

Version v2.0 has 1 test suites: 
    Suite directory dir3, version v2.0 has 2 test runs: 
    Test 'test1', result 102 
    Test 'test2', result 202 

Cela se sent mal à moi; il ne me semble pas normal de garder une trace des noms de versions uniques et d'ajouter manuellement les suites aux objets TestVersion appropriés.

Ce code est-il proche d'être correct?

Et que se passe-t-il si je ne compile pas toute la base de données, comme dans cet exemple. Si la base de données existe déjà, dois-je interroger la table TestVersion de la base de données pour découvrir les noms de version uniques?

Merci d'avance. Je sais que c'est beaucoup de code à parcourir, et j'apprécie l'aide.

+0

Je viens de parler à un collègue qui m'a mis au clair. Je ne veux pas du tout la table TestVersion; quand je veux connaître les noms de version uniques, j'ai besoin d'interroger la base de données; par exemple, "sélectionnez nom_version distinct des suites"; Quand je trouverai l'équivalent SQLAlchemy, je le signalerai. Merci encore. –

+0

pour la valeur dans session.query (TestSuite.version_name) .distinct(): valeur d'impression –

Répondre

1

Je ne peux pas comprendre votre question, en grande partie parce que vous ne l'avez pas raffinée. Votre question concerne peut-être un schéma, et peut-être son modèle relationnel objet correspondant. Donc, voici le ORM dépouillé à son noyau:

class TestVersion(Base): 
    __tablename__ = 'versions' 
    id = Column(Integer, primary_key=True) 
    name = Column(String) 

class TestSuite(Base): 
    __tablename__ = 'suites' 
    directory = Column(String, primary_key=True) 
    version = Column(Integer, ForeignKey ('versions.id')) 

    parent = relationship(TestVersion, backref=backref('suites', 
     order_by=directory)) 

class TestRun(Base): 
    __tablename__ = 'runs' 
    id = Column(Integer, primary_key=True) 
    directory = Column(String, ForeignKey ('suites.directory')) 

    parent = relationship(TestSuite, backref=backref('runs', 
     order_by=id)) 

J'ai pris beaucoup de libertés avec votre déclaration: jeter des colonnes sans rapport avec votre question, réordonner les déclarations de rendre plus évidente la chaîne de dépendance, etc. Peut-être contre ce modèle réduit, vous pouvez mieux décrire votre problème.

En outre, les normes de codage comme PEP 8 existent pour une raison: si vous voulez que votre code soit compréhensible pour les autres, utiliser 4 renfoncements espace et évitent les espaces entre un nom et un « (», des lignes de limite à 79 caractères, etc. Oui, cela semble pédant, mais vous avez juste rencontré une situation où votre lecteur a eu plus de difficulté à lire votre code que vous le souhaitez

+0

J'ai corrigé l'indentation; il a été foiré pendant mon copier-coller. Ma question: mon code ajoute-t-il correctement les objets TestSuite aux bases de données? Il me semble étrange que je crée manuellement les objets TestVersion. Merci. –

+0

Je ne suis toujours pas clair sur la relation entre les données. Il semble que la Version ne soit pas votre primitive d'application mais que votre TestSuite soit et que vous ayez besoin d'un nom de version pour séparer TestSuites. Si cela est vrai, votre modèle est fondamentalement défectueux puisque TestVersion est en fait un attribut de TestSuite et ne devrait pas avoir sa propre table. Si vous avez besoin de garantir des étiquettes uniques pour TestSuites, il existe des moyens plus directs de le faire. – msw

+0

TestSuite est la primitive, et 'suites' est sa table correspondante, indexée par le nom du répertoire. Je suis à la recherche de deux choses: 1) un moyen de récupérer tous les noms de version uniques, et 2) un moyen de récupérer tous les tests qui sont associés à chaque nom de version. Je veux faire chacun sans inspecter chaque suite de tests dans la base de données. Par exemple, si j'ai 1000 objets TestSuite et 10 versions, je voudrais interroger la table 'versions' et obtenir 10 objets TestVersion, puis choisir une version et découvrir les objets TestSuite qui sont associés à cette version. Merci encore. –

Questions connexes