Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do SQLAlchemy classes inheriting from Base not need a constructor?

With SQLAlchemy objects inheriting from the Base class I can pass arguments to a class for variables which aren't defined in a constructor:

from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
    name = Column(String(50))
    fullname = Column(String(50))
    password = Column(String(12))

    def __repr__(self):
        return "<User(name='%s', fullname='%s', password='%s')>" % (
                                self.name, self.fullname, self.password)

ed_user = User(name='ed', fullname='Ed Jones', password='edspassword')

of course, if I tried passing arguments like that to another class, to set class variables in the same way I'd get an error:

In [1]: class MyClass(object):
   ...:     i = 2
   ...:     

In [2]: instance = MyClass(i=9)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-6d3ec8445b00> in <module>()
----> 1 instance = MyClass(i=9)

TypeError: object.__new__() takes no parameters

What sort of trickery is SQLalchemy doing? Why don't they just use a constructor (i.e an __init__ method)?

like image 312
Mike Vella Avatar asked Dec 21 '13 03:12

Mike Vella


People also ask

What is a base class in SQLAlchemy?

A base class stores a catlog of classes and mapped tables in the Declarative system. This is called as the declarative base class. There will be usually just one instance of this base in a commonly imported module. The declarative_base() function is used to create base class. This function is defined in sqlalchemy.

What is Declared_attr in SQLAlchemy?

declarative_base() class sqlalchemy.ext.declarative. declared_attr(fget, cascading=False) Mark a class-level method as representing the definition of a mapped property or special declarative member name. @declared_attr turns the attribute into a scalar-like property that can be invoked from the uninstantiated class.

What does declarative base do?

declarative_base is a factory function, that returns a base class (actually a metaclass), and the entities are going to inherit from it. Once the definition of the class is done, the Table and mapper will be generated automatically.

What is a declarative class?

The Declarative system is the typically used system provided by the SQLAlchemy ORM in order to define classes mapped to relational database tables. However, as noted in Classical Mappings, Declarative is in fact a series of extensions that ride on top of the SQLAlchemy mapper() construct.


1 Answers

You actually asked two questions here.

First, you asked "What sort of trickery is SQLAlchemy doing". There is a bit more going on behind the scenes using a concept called Metaclasses to dynamically create the Base class.

But in reality all you need to know is that SQLAlchemy is defining a constructor (albeit in a roundabout way) in the Base class that dynamically sets the elements. Here is actually the implementation of that method (at least as it exists at the time of this answer):

def _declarative_constructor(self, **kwargs):
    """A simple constructor that allows initialization from kwargs.

    Sets attributes on the constructed instance using the names and
    values in ``kwargs``.

    Only keys that are present as
    attributes of the instance's class are allowed. These could be,
    for example, any mapped columns or relationships.
    """
    cls_ = type(self)
    for k in kwargs:
        if not hasattr(cls_, k):
            raise TypeError(
                "%r is an invalid keyword argument for %s" %
                (k, cls_.__name__))
        setattr(self, k, kwargs[k])

Basically, this is dynamically determining the keyword arguments, and setting the attributes on the new object automatically. You can imagine theBase class as looking like the following, although keep in mind it is actually a bit more complex (you can read the code to find out more):

class Base(object):
    def __init__(self, **kwargs):
        cls_ = type(self)
        for k in kwargs:
            if not hasattr(cls_, k):
                raise TypeError(
                    "%r is an invalid keyword argument for %s" %
                    (k, cls_.__name__))
            setattr(self, k, kwargs[k])

If you created the above code, any class that you create that inherits from Base would automatically get the ability to have the attributes of the property auto-filled as long as the attribute was already defined on the class. This is because of the typical Python Object-Oriented inheritance structure: if you don't define a method on your User object, Python looks for the method being defined on a base class (in this case the Base method) and will use that instead. This goes for the __init__ method, just like any other method.

Your second question is "Why don't they just use a constructor (i.e an __init__ method)?" Well, as we've described above, they do! They set the _declarative_constructor method to the __init__ attribute the Base class, effectively setting the default logic for object construction. Of course, this only defines the default; you can always override this if you want to...

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
    name = Column(String(50))
    fullname = Column(String(50))
    password = Column(String(12))

    def __init__(self, name):
        self.name = name
        self.fullname = name
        self.password = generate_new_password()

# The following will now work...
ed_user = User('ed')
mark_user = User(name='mark')

# ...but this will not...
problem_user = User(name='Error', fullname='Error M. McErrorson', password='w00t')
like image 174
Mark Hildreth Avatar answered Sep 27 '22 16:09

Mark Hildreth