Search code examples
pythonsqlalchemypython-elixir

How to create a class attribute and class property within Elixir/SqlAlchemy


I have a class:

from sys import stderr
from elixir import *
from types import *

class User(Entity):
    using_options(tablename="users")
    first_name = Field(String(50))
    middle_name = Field(String(50))
    last_name = Field(String(50))

    def __get_name__ (self):
        first_name = self.first_name if self.first_name is not None else ""
        middle_name = self.middle_name if self.middle_name is not None else ""
        last_name = self.last_name if self.last_name is not None else ""
        return " ".join((first_name, middle_name, last_name)).strip()

    def __set_name__ (self,string):
        first_name = ""
        middle_name = ""
        last_name = ""
        split_string = string.split(' ')
        if len(split_string) == 1:
            first_name = string
        elif len(split_string) == 2:
            first_name, last_name = split_string
        elif len(split_string) == 3:
            first_name, middle_name, last_name = split_string
        else: #len(split_string) > 3:
            first_name = split_string[0]
            last_name = split_string[-1]
            middle_name = " ".join(split_string[1:-2])
        self.first_name = first_name
        self.middle_name = middle_name
        self.last_name = last_name

    name = property(__get_name__,__set_name__)

I'd like to run a query as follows:

def get_user(user):
    found = None
    if type(user) in [IntType,StringType]:
        if type(user) is StringType:
            where = or_(User.first_name==user,
                        User.middle_name==user,
                        User.last_name==user,
                        User.name==user)
            qry = User.query.filter(where)
        elif type(user) is IntType:
            where = or_(User.id==user,
                        User.employee_id==user)
            qry = User.query.filter(where)
        try:
            found = qry.one()
        except NoResultFound:
            print >> stderr, "Couldn't find '%s'" % user
    elif type(user) == User:
        found=user
    return found

However, the resultant SQL query looks something like the following:

SELECT users.first_name AS users_first_name, 
       users.middle_name AS users_middle_name, 
       users.last_name AS users_last_name
FROM users 
WHERE users.first_name = 'Joseph'
   OR users.middle_name = 'M'
   OR users.last_name = 'Schmoe'
   OR false

Notice the 'false' in place of the User.name field.

I'm getting this error:

sqlalchemy.exc.OperationalError: (OperationalError) no such column: false 

I think what I'd like the SQL query to look like is the following:

SELECT users.name
FROM users 
WHERE users.name = 'Joseph M Schmoe'

Edit: The desired/second SQL query was incorrect for what I really wanted: some sort of passive way to create a 'name' field within the database which corresponds to a concatenate of 'first_name','middle_name','last_name'.

Edit2: I believe that the following will get me almost there. However, I'm still struggling with the proper expression.

Edit3: Looks like it works for what I need it to do. So I'm including it as the answer.


Solution

  • from sqlalchemy.ext.hybrid import hybrid_property
    
    @hybrid_property
    def name (self):
        first_name = self.first_name if self.first_name is not None else ""
        middle_name = self.middle_name if self.middle_name is not None else ""
        last_name = self.last_name if self.last_name is not None else ""
        return " ".join((first_name, middle_name, last_name)).strip()
    
    @name.setter
    def name (self,string):
        first_name = ""
        middle_name = ""
        last_name = ""
        split_string = string.split(' ')
        if len(split_string) == 1:
            first_name = string
        elif len(split_string) == 2:
            first_name, last_name = split_string
        elif len(split_string) == 3:
            first_name, middle_name, last_name = split_string
        else: #len(split_string) > 3:
            first_name = split_string[0]
            last_name = split_string[-1]
            middle_name = " ".join(split_string[1:-2])
        self.first_name = first_name
        self.middle_name = middle_name
        self.last_name = last_name
    

    The Expression part is here:

    @name.expression
    def name (cls):
        f = cls.first_name
        m = cls.middle_name
        l = cls.last_name
        return f+' '+m+' '+l