Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy ORM __init__ method vs

In the SQLAlchemy ORM tutorial the following code is given as an example of a class which will be mapped to a table:

>>> from sqlalchemy import Column, Integer, String
>>> class User(Base):
...     __tablename__ = 'users'
...
...     id = Column(Integer, primary_key=True)
...     name = Column(String)
...     fullname = Column(String)
...     password = Column(String)
...
...     def __init__(self, name, fullname, password):
...         self.name = name
...         self.fullname = fullname
...         self.password = password
...
...     def __repr__(self):
...        return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)

If name, fullname and password are set by the user in the __init__ method when the class is instantiated, what's the point of having them declared as Column objects (i.e as class variables)? I don't understand how and when SQLAlchemy is able to use the information - is it somehow passed to the SQLAlchemy module via the 'Base' class which User is inheriting from? (I didn't think it was possible to pass information to a unit in this way - by declaring a class which inherits from another class).

like image 300
Mike Vella Avatar asked Oct 08 '13 21:10

Mike Vella


1 Answers

first, you should understand that

class Foo(object):
    bar = ['baz']

and

class Foo(object):
    def __init__(self):   
        self.bar = ['baz']

Mean very different things, in the first case, bar is a class attribute, it is the same for all instances of Foo, and the class Foo itself (which is an instance of type, not Foo), with but in the second, bar is an attribute of each instance of Foo, and is not associated with the class at all!. If you try both, and append to the bar, for the first, the change will propagate to every instance of Foo, but for the second, only the instance you appended to will have that change.

You can even set both!, you could do:

class Foo(object):
    bar = 'baz'

    def __init__(self):   
        self.bar = 'quux'

And that's totally unanbigious. Foo.bar == 'baz', Foo().bar == 'quux', and you can get back to the class attribute from instances with type(Foo()).bar == 'baz'

so that means that the arguments in __init__ in the tutorial, apply to instances of User, but the Column definitions apply to the class

Sqlalchemy uses the class information, primarily. It uses it so that it can generate SQL which reads from and writes to the database in the proper way. Those definitions only really apply in the cases when sqlalchemy is converting instances of User to database calls, or trying to create instances of User when it receives rows from the database.

You should regard the class attributes as declaring the types, and the instances attributes as setting the values.

As for the matter of when sqlalchemy actually uses that information, there is a special bit of magic in Base, which is almost certainly from sqlalchemy.ext.declarative.declarative_base(). That function returns a class, and the class uses a metaclass which can track when new classes which derive from Base are defined. At the end of the class definition, even before you create instances of User, sqlalchemy looks at the User class to find the column properties it needs to know about.

like image 189
SingleNegationElimination Avatar answered Oct 08 '22 05:10

SingleNegationElimination