How do I code this relationship in SQLAlchemy?

Posted by Martin Del Vecchio on Stack Overflow See other posts from Stack Overflow or by Martin Del Vecchio
Published on 2010-05-12T21:11:48Z Indexed on 2010/05/12 21:14 UTC
Read the original article Hit count: 291

Filed under:
|
|

I am new to SQLAlchemy (and SQL, for that matter). I can't figure out how to code the idea I have in my head.

I am creating a database of performance-test results.

A test run consists of a test type and a number (this is class TestRun below)

A test suite consists the version string of the software being tested, and one or more TestRun objects (this is class TestSuite below).

A test version consists of all test suites with the given version name.

Here is my code, as simple as I can make it:

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)

The output of this program:

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

This is not correct, since there are two TestVersion objects with the name 'v1.0'. I hacked my way around this by adding a private list of TestVersion objects, and a function to find a matching one:

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)

Then I modified my code that adds the records to use it:

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

Now the output is what I want:

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

This feels wrong to me; it doesn't feel right that I am manually keeping track of the unique version names, and manually adding the suites to the appropriate TestVersion objects.

Is this code even close to being correct?

And what happens when I'm not building the entire database from scratch, as in this example. If the database already exists, do I have to query the database's TestVersion table to discover the unique version names?

Thanks in advance. I know this is a lot of code to wade through, and I appreciate the help.

© Stack Overflow or respective owner

Related posts about python

Related posts about sql