Как создать атрибут класса и свойство класса в Elixir / SQLAlchemy

#python #sqlalchemy #python-elixir

#python #sqlalchemy #python-elixir

Вопрос:

У меня есть класс:

 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__)
  

Я хотел бы выполнить запрос следующим образом:

 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
  

Однако результирующий SQL-запрос выглядит примерно следующим образом:

 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
  

Обратите внимание на «false» вместо User.name поле.

Я получаю эту ошибку:

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

Я думаю, что я бы хотел, чтобы SQL-запрос выглядел следующим образом:

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

Редактировать: желаемый / второй SQL-запрос был неверным для того, что я действительно хотел: какой-то пассивный способ создания поля ‘name’ в базе данных, которое соответствует объединению ‘first_name’, ‘middle_name’, ‘last_name’.

Редактирование 2: я считаю, что следующее поможет мне почти достичь цели. Тем не менее, я все еще борюсь с правильным выражением.

Edit3: похоже, это работает для того, что мне нужно. Поэтому я включаю его в качестве ответа.

Комментарии:

1. User.name это похоже на @property def name(self): return foo # попробуйте выполнить запрос без этого оператора User.name==user, потому что вы запрашиваете тип свойства

2. @rob.alarcon: 😉 Запрос выполняется просто отлично, без лишней строки. Но когда я выполняю запрос, я специально хочу использовать ORM для создания полезного свойства в запросе.

3. __set_name__ теперь это зарезервированное специальное имя метода в Python 3.6

Ответ №1:

Вы определяете свойство, и оно будет доступно после назначения объекта.

Примечание: я изменяю ваш код для своего удобства

 from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, DateTime, String
from datetime import datetime

Base = declarative_base()
class User(Base):
    first_name = Column('first_name', String)
    middle_name = Column('middle_name', String)
    last_name = Column('last_name', String)

    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__)
  

То first_name, middle_name and last_name же самое относится и к атрибутам класса. Когда вы определяете свойство, ему нужен экземпляр этого объекта.

 In [13]: User.first_name
Out[13]: Column('first_name', String(), )

In [14]: User.name
Out[14]: <property object at 0x26fef70>
  

В приведенном выше примере вы можете увидеть разницу. Пока вы не установите это свойство или любые поля этого свойства, оно всегда будет пустым.

Вы должны предоставить

 In [16]: u1 = User()

In [17]: u1.name = "first middle last"

In [18]: u1.name
Out[18]: 'first middle last'

In [19]: u1.first_name
Out[19]: 'first'
  

После этого вы можете использовать свойство.

Это поможет вам понять вашу проблему. Ваше свойство связано с экземпляром, к которому вы не можете получить доступ по классу.

Комментарии:

1. Я полагаю, что вы, возможно, запутали мою проблему, когда вы перешли с Elixir на Sqlalchemy. Я хочу использовать name, как если бы это был атрибут класса. Здесь есть пример SQA, но я хочу, чтобы это было сделано в Elixir. Я считаю, что соответствующее использование имеет какое-то отношение к ColumnProperty здесь . Однако документация не очень хорошо поддается. У Google есть что-то здесь , но оно не имеет дело со строками.

2. __set_name__ теперь это зарезервированное имя метода в Python 3.6

Ответ №2:

 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
  

Часть выражения находится здесь:

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