2014-06-19 2 views
5

나는 postgresql 데이터베이스에서 sqlalchemy를 통해 작업 중이다. postgresql "날짜"열 유형 (BC 날짜는 http://www.postgresql.org/docs/9.2/static/datatype-datetime.html을 지원합니다)으로 BC 날짜와 함께 작업해야합니다. 그러나 BC 날짜를 문자열 형식 (예 : '2000-01-01 BC')으로 삽입 할 수는 있지만 검색 할 수는 없습니다. 데이터베이스에서 BC 날짜를 얻으려고하는 중 값 오류 : 연도가 범위를 벗어났습니다. 처음에는 그 이유가 Date 형식의 sqlalchemy가 BC 날짜를 지원하지 않는 python datetime.date 모듈을 사용한다고 생각했지만 완전히 확신하지는 않습니다. datetime.date 모듈의 사용을 피하기 위해 데이터베이스에서 "날짜"유형을 사용자 지정 파이썬 클래스에 매핑하는 방법을 찾으려고했지만 성공하지 못했습니다 (sqlalchemy의 다양한 처리 단계에 논리를 추가하는 방법을 찾았습니다. result_processor 또는 bind_expression과 같이 column_expression을 사용했지만 작동하도록 만들지는 못했습니다.)postgresql로 sqlalchemy BC 날짜

다음은 문제를 재현하는 예입니다. 트레이스

from sqlalchemy import create_engine, Column, Integer, String, Date 
    from sqlalchemy.orm import sessionmaker 
    from sqlalchemy.ext.declarative import declarative_base 

    engine = create_engine('postgresql://database:[email protected]:5432/sqlalchemy_test', echo=True) 
    Base = declarative_base() 

    class User(Base): 
     __tablename__ = 'users' 

     id = Column(Integer, primary_key=True) 
     name = Column(String) 
     date = Column(Date) 

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

    Base.metadata.create_all(engine) 
    Session = sessionmaker(bind=engine) 
    session = Session() 

    # remove objects from previous launches 
    session.query(User).delete() 

    import datetime 
    a_date = datetime.date.today() 
    # Insert valid date in datetime.date format, it works. 
    ed_user = User(name='ed', date=a_date) 
    # Insert BC date in string format, it works. 
    al_user = User(name='al', date='1950-12-16 BC') 

    session.add_all([ed_user, al_user]) 
    session.commit() 

    # Insert data via raw sql, it works 
    conn = engine.connect() 
    conn.execute('INSERT INTO users (name, date) VALUES (%s, %s)', ("Lu", "0090-04-25 BC")) 

    # Check that all 3 objects are present 
    user_names = session.query(User.name).all() 
    print user_names 


    # Check entry with AD date, it works. 
    user = session.query(User).filter_by(name='ed').first() 
    print '-- user vith valid date: ', user, user.date 

    # But when trying to fetch date fields in any way, it raises Value error 

    # Trying to fetch model, it raises Value error 
    users = session.query(User).all() 
    print users 

    # Trying to fetch only field, it raises Value error 
    user_dates = session.query(User.date).all() 
    print user_dates 

    # Even trying to fetch dates in raw sql raises Value error 
    a = conn.execute('SELECT * FROM users') 
    print [c for c in a] 

그리고 출력 : 나는 PostgreSQL을 9.1.11, SQLAlchemy의 0.9.4을 사용하고

2014-06-19 16:06:20,466 INFO sqlalchemy.engine.base.Engine select version() 
    2014-06-19 16:06:20,466 INFO sqlalchemy.engine.base.Engine {} 
    2014-06-19 16:06:20,468 INFO sqlalchemy.engine.base.Engine select current_schema() 
    2014-06-19 16:06:20,468 INFO sqlalchemy.engine.base.Engine {} 
    2014-06-19 16:06:20,470 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 
    2014-06-19 16:06:20,470 INFO sqlalchemy.engine.base.Engine {} 
    2014-06-19 16:06:20,471 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 
    2014-06-19 16:06:20,471 INFO sqlalchemy.engine.base.Engine {} 
    2014-06-19 16:06:20,473 INFO sqlalchemy.engine.base.Engine show standard_conforming_strings 
    2014-06-19 16:06:20,473 INFO sqlalchemy.engine.base.Engine {} 
    2014-06-19 16:06:20,475 INFO sqlalchemy.engine.base.Engine select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where n.nspname=current_schema() and relname=%(name)s 
    2014-06-19 16:06:20,475 INFO sqlalchemy.engine.base.Engine {'name': u'users'} 
    2014-06-19 16:06:20,477 INFO sqlalchemy.engine.base.Engine BEGIN (implicit) 
    2014-06-19 16:06:20,478 INFO sqlalchemy.engine.base.Engine DELETE FROM users 
    2014-06-19 16:06:20,478 INFO sqlalchemy.engine.base.Engine {} 
    2014-06-19 16:06:20,480 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name, date) VALUES (%(name)s, %(date)s) RETURNING users.id 
    2014-06-19 16:06:20,481 INFO sqlalchemy.engine.base.Engine {'date': datetime.date(2014, 6, 19), 'name': 'ed'} 
    2014-06-19 16:06:20,482 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name, date) VALUES (%(name)s, %(date)s) RETURNING users.id 
    2014-06-19 16:06:20,482 INFO sqlalchemy.engine.base.Engine {'date': '1950-12-16 BC', 'name': 'al'} 
    2014-06-19 16:06:20,483 INFO sqlalchemy.engine.base.Engine COMMIT 
    2014-06-19 16:06:20,494 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name, date) VALUES (%s, %s) 
    2014-06-19 16:06:20,494 INFO sqlalchemy.engine.base.Engine ('Lu', '0090-04-25 BC') 
    2014-06-19 16:06:20,495 INFO sqlalchemy.engine.base.Engine COMMIT 
    2014-06-19 16:06:20,517 INFO sqlalchemy.engine.base.Engine BEGIN (implicit) 
    2014-06-19 16:06:20,517 INFO sqlalchemy.engine.base.Engine SELECT users.name AS users_name 
    FROM users 
    2014-06-19 16:06:20,517 INFO sqlalchemy.engine.base.Engine {} 
    -- user names: [(u'ed',), (u'al',), (u'Lu',)] 
    2014-06-19 16:06:20,520 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.date AS users_date 
    FROM users 
    WHERE users.name = %(name_1)s 
    LIMIT %(param_1)s 
    2014-06-19 16:06:20,520 INFO sqlalchemy.engine.base.Engine {'name_1': 'ed', 'param_1': 1} 
    -- user with valid date: <User(name='ed', date='2014-06-19')> 2014-06-19 
    2014-06-19 16:06:20,523 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.date AS users_date 
    FROM users 
    2014-06-19 16:06:20,523 INFO sqlalchemy.engine.base.Engine {} 
    Traceback (most recent call last): 
     File "test_script.py", line 49, in <module> 
     users = session.query(User).all() 
     File "/home/pavel/.virtualenvs/common/local/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 2292, in all 
     return list(self) 
     File "/home/pavel/.virtualenvs/common/local/lib/python2.7/site-packages/sqlalchemy/orm/loading.py", line 65, in instances 
     fetch = cursor.fetchall() 
     File "/home/pavel/.virtualenvs/common/local/lib/python2.7/site-packages/sqlalchemy/engine/result.py", line 788, in fetchall 
     self.cursor, self.context) 
     File "/home/pavel/.virtualenvs/common/local/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1111, in _handle_dbapi_exception 
     util.reraise(*exc_info) 
     File "/home/pavel/.virtualenvs/common/local/lib/python2.7/site-packages/sqlalchemy/engine/result.py", line 782, in fetchall 
     l = self.process_rows(self._fetchall_impl()) 
     File "/home/pavel/.virtualenvs/common/local/lib/python2.7/site-packages/sqlalchemy/engine/result.py", line 749, in _fetchall_impl 
     return self.cursor.fetchall() 
    ValueError: year is out of range 

, 파이썬 2.7

내 질문은 : 작업하는 방법은 무엇입니까 BC는 postgresql "date"형식의 열을 sqlalchemy를 통해 열거합니다 (최소한 문자열로 검색하지만 FlexiDate 또는 astropy.time과 같은 BC 날짜를 사용할 수있는 일부 모듈에 시각적으로 매핑합니다)?

답변

3

이 문제를 파헤 쳤고이 값 오류 예외가 sqlalchemy가 아닌 데이터베이스 드라이버 (이 경우 psycopg2)에 의해 throw됩니다. 위에서 작성한 데이터베이스로 수행 할 경우이 코드는 동일한 값 오류 예외를 발생시킵니다.

import psycopg2 
    conn = psycopg2.connect("dbname=sqlalchemy_test user=database password=database host=localhost port=5432") 
    cursor = conn.cursor() 
    cursor.execute("SELECT * FROM users") 
    cursor.fetchall() 

출력 :이 값 데이터베이스에서 검색 및 실패에서 datetime.date 개체를 만들려고처럼

Traceback (most recent call last): 
     File "test_script.py", line 5, in <module> 
     values = cursor.fetchall() 
    ValueError: year is out of range 

보인다.

나는 또한 pg8000 방언을 시도했지만 값 오류는 발생시키지 않지만 BC 부분을 먹어서 년, 월, 일과 함께 datetime.date 객체를 반환한다. (데이터베이스 행의 '1990-11-25 BC 'datetime.date (1990, 11, 25), 실제로는 1990-11-25를 반환합니다. 다음 예 :

import pg8000 
    conn = pg8000.connect(user='postgres', host='localhost', port=5432, 
          database='sqlalchemy_test', password='postgres') 
    cursor = conn.cursor() 
    cursor.execute('SELECT * FROM users') 
    values = cursor.fetchall() 
    print values 
    print [str(val[2]) for val in values] 

출력 :

([1, u'ed', datetime.date(2014, 6, 19)], [2, u'al', datetime.date(1950, 12, 16)], [3, u'Lu', datetime.date(90, 4, 25)]) 
    ['2014-06-19', '1950-12-16', '0090-04-25'] 

이 또한 내가 PY-PostgreSQL의 방언을 원했고,하지만 그렇지 않은 옵션에 (생산 파이썬 2.7.6를 사용하는, 3+ 단지 파이썬위한거야 이 프로젝트를 위해).

내 질문에 대한 대답은 : "아니오, sqlalchemy에서 불가능합니다.", 왜냐하면 데이터베이스 리터럴과 파이썬 개체 사이의 형식 변환은 sqlalchemy 부분이 아니라 데이터베이스 드라이버에서 발생하기 때문입니다.

UPDATE :이 SQLAlchemy의에서 할 참으로 불가능하지만

, psycopg2에 자신의 타입 캐스팅을 정의 (및 기본 동작을 재정의), 및 watever 원하는 데이터베이스 리턴에서 날짜 형식을 만들 수 있습니다 .다음은 예입니다.

이 질문은 예를 들어 매력과 같습니다. 그리고 sqlalchemy에서 추가 데이터 처리를위한 방법을 엽니 다.