2013-08-20 17 views
7

W moim bieżącym modelu Użytkownik, mam pole "nazwa", które nie może być puste.Błędy w transakcjach SQLAlchemy nie zwracają poprawnego śledzenia (Python 2.7)

Próbuję utworzyć obiekt użytkownika i dodać go do DBSession dostarczonego przez Pyramid i przesłać go z transakcji, tak jak w przypadku.

with transaction.manager: 
    u = models.User() 
    models.DBSession.add(u) 

Dla tych, którzy nie korzystają z piramidy DBSession jest:

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) 

Teraz, moim transakcji powyżej, mam problem walidacji - Muszę przypisać nazwę użytkownika, ale Nie zrobiłem tego. Jednak zamiast się błąd mówiąc mi „Musisz przypisać swoją użytkownik nazwę!”, Otrzymuję to:

<ipython-input-5-47d9c0e393f7> in <module>() 
     2  u = models.User() 
----> 3  models.DBSession.add(u) 
     4 

/home/user/Projects/env/local/lib/python2.7/site-packages/transaction-1.4.1-py2.7.egg/transaction/_manager.pyc in __exit__(self, t, v, tb) 
    118  def __exit__(self, t, v, tb): 
    119   if v is None: 
--> 120    self.commit() 
    121   else: 
    122    self.abort() 

/home/user/Projects/env/local/lib/python2.7/site-packages/transaction-1.4.1-py2.7.egg/transaction/_manager.pyc in commit(self) 
    109   """ See ITransactionManager. 
    110   """ 
--> 111   return self.get().commit() 
    112 
    113  def abort(self): 

/home/user/Projects/env/local/lib/python2.7/site-packages/transaction-1.4.1-py2.7.egg/transaction/_transaction.py in commit(self) 
    276    tb = None 
    277    try: 
--> 278     t, v, tb = self._saveAndGetCommitishError() 
    279     self._callAfterCommitHooks(status=False) 
    280     reraise(t, v, tb) 

/home/user/Projects/env/local/lib/python2.7/site-packages/transaction-1.4.1-py2.7.egg/transaction/_transaction.py in _saveAndGetCommitishError(self) 
    300    import pdb 
    301    pdb.set_trace() 
--> 302    traceback.print_stack(sys._getframe(1), None, ft) 
    303    # Append the stack entries from here down to the exception. 
    304    traceback.print_tb(tb, None, ft) 

/usr/lib/python2.7/traceback.py in print_stack(f, limit, file) 
    267   except ZeroDivisionError: 
    268    f = sys.exc_info()[2].tb_frame.f_back 
--> 269  print_list(extract_stack(f, limit), file) 
    270 
    271 def format_stack(f=None, limit=None): 

/usr/lib/python2.7/traceback.py in print_list(extracted_list, file) 
    23    ' File "%s", line %d, in %s' % (filename,lineno,name)) 
    24   if line: 
---> 25    _print(file, ' %s' % line.strip()) 
    26 
    27 def format_list(extracted_list): 

/usr/lib/python2.7/traceback.py in _print(file, str, terminator) 
    11 
    12 def _print(file, str='', terminator='\n'): 
---> 13  file.write(str+terminator) 
    14 
    15 

TypeError: 'unicode' does not have the buffer interface 

Znalazłem ten problem pod ręką jest to, że gdzieś tam jest python wersja 2 vs 3 niezgodności, pokazano tutaj TypeError: 'str' does not support the buffer interface. Wiem, że SQLAlchemy obsługuje pythona 3+, i stąd właśnie może pochodzić problem.

Pamiętaj, że jeśli wykonuję transakcję poprawnie, nie są zgłaszane żadne błędy.

Czy istnieje sposób na obejście tego problemu bez konieczności nadpisywania kodu w pliku traceback.py?

+0

Czy możesz podać pełny kod, aby odtworzyć błąd? – javex

+0

Nie jestem pewien, co jeszcze trzeba zobaczyć. Model użytkownika ma po prostu kilka kolumn SQLAlchemy, a to, jak wygląda, nie ma związku z niemożnością poprawnego obsługi błędów (zdarzało się to w przypadku wszystkich modeli, z którymi próbowałem to zrobić, ale tylko podczas próby zatwierdzenia() z niepoprawnymi wpisami dla niektórych kolumn) – limasxgoesto0

+2

Czy możesz podać minimalny kod i plik wymagań, aby odtworzyć błąd w nowym virtualenv? –

Odpowiedz

2

Błąd, który widzisz, nie jest (przynajmniej bezpośrednio) spowodowany przez SQLAlchemy, ale jest spowodowany kombinacją SQLAlchemy, IPython i sposobu, w jaki próbujesz użyć transaction. Zniknie, jeśli zastosujesz zalecane użycie tych narzędzi.

Nota prawna: Poniższy opis nie jest zalecanym sposobem użycia sesji z ograniczeniem zakresu i ZopeTransactionExtension in Pyramids, ale chciałem trzymać się jak najbliżej podanego kodu.

umieścić to w pliku i uruchom go z virtualenv z zainstalowanym SQLAlchemy i widać prawidłowy komunikat o błędzie z SQLAlchemy:

from sqlalchemy import types 
from sqlalchemy import create_engine 
from sqlalchemy.schema import Column 
from zope.sqlalchemy import ZopeTransactionExtension 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import sessionmaker, scoped_session 

Base = declarative_base() 


class User(Base): 
    __tablename__ = 'user' 
    name = Column(types.String, primary_key=True) 


def pretend_view(request): 
    """Pretend view in a Pyramid application using pyramid_tm""" 
    import transaction 
    user = User() 
    with transaction.manager: 
     DBSession.add(user) 
    return user 

if __name__ == '__main__': 
    engine = create_engine('sqlite://') 
    global DBSession 
    DBSession = scoped_session(
     sessionmaker(extension=ZopeTransactionExtension())) 
    DBSession.configure(bind=engine) 
    Base.metadata.bind = engine 
    Base.metadata.create_all() 
    #import IPython; IPython.embed() 
    pretend_view("dummy request") 

generuje ten wyjątek: sqlalchemy.exc.IntegrityError: (IntegrityError) NOT NULL constraint failed: user.name u'INSERT INTO user DEFAULT VALUES'()

Jeśli odpalić Zamiast tego IPython i uruchom pretend_view, otrzymasz wspomniany błąd Unicode.

Właściwe stosowanie pyramid_tm

teraz, jeśli chcesz zobaczyć poprawny komunikat o błędzie w ipython użyj sesji „poprawnie”!

Zazwyczaj nie ma powodu, aby używać jawnie w kodzie transaction; piramid_tm zatwierdza transakcję, gdy widok powróci automatycznie (zakładając, że nie zostaną podjęte żadne wyjątki).Byłby to właściwa droga, aby uruchomić widok, i wygeneruje poprawny wyjątku nawet od wewnątrz ipython:

def pretend_view(request): 
    """Pretend view in a Pyramid application using pyramid_tm""" 
    session = DBSession() # You're using a sessionmaker, so you should make a session! 
    user = User() 
    session.add(user) 
    session.flush() 
    return user 

jeśli naprawdę chcesz popełnić transakcji z poziomu widoku:

def pretend_view(request): 
    """Pretend view in a Pyramid application using pyramid_tm""" 
    session = DBSession() 
    user = User() 
    session.add(user) 
    session.flush() 
    import transaction 
    transaction.commit() 
    return user 

Inne zasoby

SQLAlchemy-Pyramid Cookbook: http://docs.pylonsproject.org/projects/pyramid/en/latest/tutorials/wiki2/index.html#bfg-sql-wiki-tutorial

pyramid_tm d ocumentacja: http://pyramid-tm.readthedocs.org/en/latest/

+0

O tak, minęło sporo czasu, odkąd napisałem to pytanie. Nie używamy transakcji w naszym kodzie (lub jeśli tak, to był dzień lub dwa, wierzchołki, zanim zorientujemy się, jak działa ZTE), ale używamy go, gdy dane muszą być manipulowane w pshell/ipython. Z pewnością nie używamy transakcji w żadnym z naszych poglądów. Kiedy potrzebujemy użyć transakcji w ipython, teraz generalnie używamy transaction.commit() przez transaction.manager, a komunikaty o błędach nie sprawiają żadnych problemów od tego czasu. – limasxgoesto0