Creating classes and many-to-many relations dynamically

49 views
Skip to first unread message

Stephan Hügel

unread,
Jul 25, 2012, 5:49:08 AM7/25/12
to sqlal...@googlegroups.com
I need to create 20 identical (in structure) tables, each of which will have a many-to-many relationship with a particular table (Table_A).

I've thought a bit about this, and there doesn't seem to be a better way to structure the setup; it's a canonical reference (Table_A), each entry of which can have multiple overlapping entries in a particular book (each of the 20 tables represents references in a particular book).
Is there a sensible, compact way for me to instantiate the 20 classes and association tables? Their structure is extremely simple; just a primary key column and a string column.

Michael Bayer

unread,
Jul 25, 2012, 11:44:16 AM7/25/12
to sqlal...@googlegroups.com
just build a function:

def create_my_class(x, y, z, ...):
    class MyClass(Base):
        __tablename__ = '...'
        # ...

   MyClass.__name__ = 'SomeName%s%s' % (q, p)
   return MyClass


--
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To view this discussion on the web visit https://groups.google.com/d/msg/sqlalchemy/-/fVKaMEuyN_IJ.
To post to this group, send email to sqlal...@googlegroups.com.
To unsubscribe from this group, send email to sqlalchemy+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.

Stephan Hügel

unread,
Jul 25, 2012, 4:29:44 PM7/25/12
to sqlal...@googlegroups.com
To unsubscribe from this group, send email to sqlalchemy+unsubscribe@googlegroups.com.

For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.


 OK, I've done the following

def create_signlist(sl):
    class SignList(db.Model):

        __tablename__ = listname.lower()
 
        id = db.Column("id", db.Integer(), primary_key=True)
        reference = db.Column(db.String(50), nullable=False, unique=True)

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

    SignList.__name__ = listname
    return SignList

signlists = ['lka', 'kal']
for s in signlists:
    create_signlists(s)

But this gives me a warning:

SAWarning: The classname 'SignList' is already in the registry of this declarative base, mapped to <class 'glyph.models.lka'>
  _as_declarative(cls, classname, cls.__dict__)

And e.g. lka.query.all() fails, presumably because there are no instances of it yet.

Sorry, I'm missing something completely obvious.

Michael Bayer

unread,
Jul 25, 2012, 5:32:34 PM7/25/12
to sqlal...@googlegroups.com
On Jul 25, 2012, at 4:29 PM, Stephan Hügel wrote:


 OK, I've done the following

def create_signlist(sl):
    class SignList(db.Model):

        __tablename__ = listname.lower()
 
        id = db.Column("id", db.Integer(), primary_key=True)
        reference = db.Column(db.String(50), nullable=False, unique=True)

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

    SignList.__name__ = listname
    return SignList

signlists = ['lka', 'kal']
for s in signlists:
    create_signlists(s)

But this gives me a warning:

SAWarning: The classname 'SignList' is already in the registry of this declarative base, mapped to <class 'glyph.models.lka'>
  _as_declarative(cls, classname, cls.__dict__)

that's only a warning, you can ignore it.   It means if you have some class which makes a relationship() to "SignList" using just the name, you won't get this new class.

if you want to make a new class that has a new name from the start, use type():

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

d = dict(
__tablename__ = listname.lower()

id = db.Column("id", db.Integer(), primary_key=True)
reference = db.Column(db.String(50), nullable=False, unique=True)

__init__ = __init__
)
my_class = type(listname, (db.Model,), d)


Stephan Hügel

unread,
Jul 25, 2012, 6:49:23 PM7/25/12
to sqlal...@googlegroups.com
Doing this creates classes (e.g. <class 'flask_sqlalchemy.lak'>) , but is there some way for me to instantiate them without resorting to some eval magic in my for loop? What I'd like to end up with is programmatically created instances (myapp.models.lak) that I can use the same way as my "normally" instantiated classes.

Michael Bayer

unread,
Jul 25, 2012, 6:52:18 PM7/25/12
to sqlal...@googlegroups.com
On Jul 25, 2012, at 6:49 PM, Stephan Hügel wrote:


if you want to make a new class that has a new name from the start, use type():

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

d = dict(
__tablename__ = listname.lower()

id = db.Column("id", db.Integer(), primary_key=True)
reference = db.Column(db.String(50), nullable=False, unique=True)

__init__ = __init__
)
my_class = type(listname, (db.Model,), d)



Doing this creates classes (e.g. <class 'flask_sqlalchemy.lak'>) , but is there some way for me to instantiate them without resorting to some eval magic in my for loop?

instantiate, as in, instance of the new class?  "my_class" above is a regular Python class,  just instantiate - myclass(x, y, z).   


What I'd like to end up with is programmatically created instances (myapp.models.lak) that I can use the same way as my "normally" instantiated classes.

that's what type() gives you.  Its the exact same thing as using "class Foo()".

Stephan Hügel

unread,
Jul 25, 2012, 7:26:20 PM7/25/12
to sqlal...@googlegroups.com


On Wednesday, 25 July 2012 23:52:18 UTC+1, Michael Bayer wrote:

instantiate, as in, instance of the new class?  "my_class" above is a regular Python class,  just instantiate - myclass(x, y, z).   


What I'd like to end up with is programmatically created instances (myapp.models.lak) that I can use the same way as my "normally" instantiated classes.

that's what type() gives you.  Its the exact same thing as using "class Foo()".



Hmm. I'm explaining this badly.

from myapp import db


class Foo(db.Model):
    __tablename__ = 'foo'
    id = db.Column(db.Integer, primary_key=True)
    othercol = db.Column(db.String(50), nullable=False, unique=True)
    
# I can import this, and immediately run a query like Foo.query.all()
# no manual instantiation required


def create_signlist(listname):
    def __init__(self, reference=None):
        self.reference = reference
    d = dict(
        __tablename__ = listname.lower(),
        __table_args__ = {'mysql_engine': 'InnoDB'},
        __mapper_args__ = {'always_refresh': True},
        id = db.Column("id", db.Integer(), primary_key=True),
        reference = db.Column(db.String(50), nullable=False, unique=True),
        __init__ = __init__
    )
    cls = type(listname, (db.Model,), d)
    return cls()

various = ['Abc', 'Def', 'Ghi']
for v in various:
    v = create_signlist(v)

# what do I have to do now to end up with instances Abc, Def, Ghi that I can query within my app in the same way as Foo?
 

Michael Bayer

unread,
Jul 25, 2012, 7:45:09 PM7/25/12
to sqlal...@googlegroups.com
On Jul 25, 2012, at 7:26 PM, Stephan Hügel wrote:


# what do I have to do now to end up with instances Abc, Def, Ghi that I can query within my app in the same way as Foo?

Let's use the right terminology, you mean "class".  Abc, Def, Ghi are classes - the word "instance" implies you instantiated a class, that is, a "self" that is associated with that class.

As far as Abc, Def, Ghi being just like Foo - the answer is, they are.     In Python, everything is an object - modules, classes, instances of classes, names, are all accessible in the same way.    Everything is passable as an argument to anything else, everything can be the value of an attribute somewhere else.   There is no difference between all of these snippets below:

# 1.
class Foo(object):
    some_attribute = "some value"

# 2.
Foo = type("Foo", (object,), {"some_attribute":"some_value"})

# 3.
def make_a_foo(somename):
    return type(somename, (object,), {"some_attribute":"some_value"})
Foo = make_a_foo("Foo")

so to answer your question, it depends on how you're using this function of yours.   If you have a data array of many anonymous classes, the answer is, "it depends".   You need to access them somehow.  If you had this:

my_list_of_classes = make_classes(my_list_of_1000_names)

you've got this use case where you have 1000 names to make into classes.   Do you want to use them by name in a dynamic way ?  OK, put them in a dict:

my_dict = dict((c.__name__, c) for c in my_list_of_classes)

session.query(my_dict["SomeName"]).all()

do you want to import them from a module ?  OK, then put them in the module:

from myapp import somemodule
for cls in my_list_of_classes:
setattr(somemodule, cls.__name__, cls)

# later

from myapp.somemodule import Cls1234
session.query(Cls1234).all()

In Python, classes are data and names are just ways to access that data, so you can organize this however you see fit.











 

-- 

Stephan Hügel

unread,
Jul 26, 2012, 4:32:24 AM7/26/12
to sqlal...@googlegroups.com


On Thursday, 26 July 2012 00:45:09 UTC+1, Michael Bayer wrote:

my_list_of_classes = make_classes(my_list_of_1000_names)

you've got this use case where you have 1000 names to make into classes.   Do you want to use them by name in a dynamic way ?  OK, put them in a dict:

my_dict = dict((c.__name__, c) for c in my_list_of_classes)

session.query(my_dict["SomeName"]).all()

do you want to import them from a module ?  OK, then put them in the module:

from myapp import somemodule
for cls in my_list_of_classes:
setattr(somemodule, cls.__name__, cls)

# later

from myapp.somemodule import Cls1234
session.query(Cls1234).all()


Yes, this is exactly what I was looking for. Thanks, and sorry about the confusion. 
Reply all
Reply to author
Forward
0 new messages