views:

110

answers:

3

Hi there, I'm working on a web app and an db which deals with the following entities: computers, computer containers (computer_sets) and rules. It a firewall management tool. I'm having some troubles dealing with computer objects, because those computers stored within a computer_set are not the same objects as those computers not contained within a computer_set: a stand-alone computer can be in a rule, but a computer_set computer cannot be in a rule, i.e, only the container is allowed to be in a rule (I know I know, this is a mess, but I didn't create the firewall, I'm just programming a management tool :).

I created a DB (with SQLite) and I'm using SQLAlchemy. Thus, I created the classes Computer, ComputerSet, ComputerSetComputer and Rule and the corresponding tables . Now, in my application, I need to list all those computer objects. However, when I query the computers table, I get Computer objects and when I query my computerset_computers table, I get ComputerSetComputer objects. I would like to get a query object containing all my computers no matter whether or not they belong to a computer set. This way, I would be able to slice the query object and do pagination. Any ideas? I could change my DB design, though I cannot change the underlying concepts of a Computer, a Rule, .etc.

Thanks for your time, gsandorx :)

A: 

Hi everyone, I posted my response in groups.google.com. One guy help me to find a solution. However, I reply with the message shown below. I want to share it with you guys so you can help me to find a design solution:

//////

Hi Mariano, I tried your code and it worked great. Now I have to look a way of linking the "union" query with a mapper to obtain a rather elaborate object than a raw tuple, I mean, when you execute: q = session.query(Computer).all() you get Computer objects, but when you execute the "union" query you get tuples. When looking at the resulting tuples, I noticed that those tuples that represent Computer objects contains a field equal to "None" due to the extra column added in the query. I guess that field could be used somehow to distinguish between Computer-like tuples and CompSetComputer-like ones. I'm telling you this b/c I would like to know if there is a mechanism to link a method to a mapper. This way, the method would process the "union" query, and would create a list of Computer and CompSetComputer objects. Do I make myself clear? Thanks for your help, Sandor

PS: this is the code I tried. I made some modifications, but it's the same as yours.

---------------------------------------------------------------------------------------

from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, select, union_all, schema from sqlalchemy.orm import mapper, sessionmaker from sqlalchemy.sql.expression import null

Engine & Session

engine = create_engine('sqlite:///:memory:', echo=False) # set to True if you want to activate logging Session = sessionmaker(bind=engine)

Current session

session = Session()

Metadata & Tables & Mappers

metadata = MetaData()

Tables

comp_table = Table('comp', metadata, Column('id', Integer, primary_key=True), Column('name', String), Column('description', String), Column('ip', String), # This table has many-to-many relation with the rules table, # so I don't have a rule_id here, instead I have # a table rule_computers_table to store rule_ids and computer_ids, # you know :) # I included it in my messy-model to specify a relation but it is not # actually present in the table definition. I appologize. # That's why I'm including my code, to clarify my situation. )

compset_comp_table = Table('compset_comp', metadata, Column('id', Integer, primary_key=True), Column('name', String), Column('description', String), Column('ip', String), Column('compset_id', Integer), # This is actually a ForeinKey to the Computer_set table )

(...)

Classes

class Computer(object): pass class CompSetComputer(object): pass

Mappers

mapper(Computer, comp_table) mapper(CompSetComputer, compset_comp_table)

Create the tables

metadata.create_all(engine)

Testing...

Insert

c = Computer() c.name = "comp-1" c.ip = "1.2.3.4" c.description = "test comp 1" session.add(c)

c = Computer() c.name = "comp-2" c.ip = "10.20.30.40" c.description = "test comp 2" session.add(c)

csc = CompSetComputer() csc.name = "csc-1" csc.ip = "11.22.33.44" csc.description = "test compset-comp 1" csc.compset_id = "12" session.add(csc)

csc = CompSetComputer() csc.name = "csc-2" csc.ip = "101.202.303.404" # invalid ip address, i know, it's just for testing :) csc.description = "test compset-comp 2" csc.compset_id = "91" session.add(csc)

session.commit()

Multi-select

q1 = session.query(Computer.id, Computer.name, Computer.description, Computer.ip, null()) q2 = session.query(CompSetComputer.id, CompSetComputer.name, CompSetComputer.description, CompSetComputer.ip, CompSetComputer.compset_id) q = q2.union(q1) print 80*"-", print q print 80*"-" for x in q: print x

Clean up

session.close()

#

gsandorx
A: 

I see two ways to solve this problem: use union in low-level queries or use inheritance in model definition. I think the later is more convenient, since you get model objects from queries. Assuming you have ComputerBase abstract class and two its subclassess Computer and ComputerSetComputer, session.query(ComputerBase).all() will return the mixed list of all Computer and ComputerSetComputer objects.

Denis Otkidach
A: 

It's fine that way, I mean, using inheritance. :) Thanks.

gsandorx
Is it an answer to original question? Otherwise it should come as comment to corresponding answer. Also there are "vote up" and "accept" links to mark answer as useful to help other StackOverflow users to find better recipes.
Denis Otkidach
oops, didn't know about. sorry :)
gsandorx