Sqlalchemy serialisieren

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Guten Nachmittag,

ich habe letztens schon ein Problem mit dem serialisieren von SQLAlchemyobjekten gehabt, wofür ich nun eine Lösung gefunden habe.

Nun habe ich in meinen Datenbankobjekten (abgeleitet von Base) die Methoden zum serialisieren überschrieben.

Hier ein Beispielcode:

Code: Alles auswählen

###Transaction
class Transaction(Base):
    __tablename__ = "transactions"
    
    id = Column(Integer, primary_key=True)
    created = Column(DateTime,default=datetime.datetime.now)
    products = relationship("Product")
    
    def create(self):
        do(self)
        return self
    
    def __getstate__(self):
        """Returns pickled object. (special options for database connection)"""
        return sqlalchemy.ext.serializer.dumps(self)
    
    def __setstate__(self,value):
        """Returns unpickled object. (special options for database connection)"""
        return sqlalchemy.ext.serializer.loads(value,metadata,Session)
    
    def __repr__(self):
        return "<Database:Transaction (%s)>" % (self.id,)
Mein Ziel ist es, dass das spezielle Serialisierverfahren nur für diese Datenbankobjekte verwendet wird, bei den normalen Objekten dagegen das gehabte (wie bei pickle üblich).

Nun kommt jedoch jedes Mal eine Fehlermeldung "RuntimeError: maximum recursion depth exceeded while calling a Python object"

Habe ich hier noch etwas falsch gemacht, oder ist es vielleicht so garnicht möglich, diese Methoden zu überschreibne?

Ich danke für eure Hilfe.

Beste Grüße
Chris
deets

Warum will man denn sowas machen wenn ich mal fragen darf? SQLAlchemy ist doch fuer das speichern von Objekten in Datenbanken gemacht... warum speichert man die parallel in Pickles?

Und ohne den Code + vollen Stacktrace (damit man mal sieht was genau wen endlos aufruft) kann man da auch nix zu sagen.
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Der Grund ist der folgende:

Es handelt sich um eine Art kleines Kassensystem. Ich möchte die Objekte wie die aktuell geöffnete Transaction aber in der Session (es ist eine Webanwendung) halten, bis diese dann endgültig gefüllt ist, und dann auch gespeichert wird.

Fehlermeldungen sind die Folgende:

Code: Alles auswählen

File "/usr/share/pyshared/flask/app.py", line 1518, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/share/pyshared/flask/app.py", line 1506, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/usr/share/pyshared/flask/app.py", line 1504, in wsgi_app
response = self.full_dispatch_request()
File "/usr/share/pyshared/flask/app.py", line 1266, in full_dispatch_request
response = self.process_response(response)
File "/usr/share/pyshared/flask/app.py", line 1407, in process_response
self.save_session(ctx.session, response)
File "/usr/share/pyshared/flask/app.py", line 757, in save_session
return self.session_interface.save_session(self, session, response)
File "/usr/share/pyshared/flask/sessions.py", line 205, in save_session
secure=secure, domain=domain)
File "/usr/share/pyshared/werkzeug/contrib/securecookie.py", line 329, in save_cookie
data = self.serialize(session_expires or expires)
File "/usr/share/pyshared/werkzeug/contrib/securecookie.py", line 235, in serialize
self.quote(value)
File "/usr/share/pyshared/werkzeug/contrib/securecookie.py", line 192, in quote
value = cls.serialization_method.dumps(value)
File "/usr/lib/python2.7/copy_reg.py", line 84, in _reduce_ex
dict = getstate()
File "/data/Entwicklung/Aktuelle Projekte (Python)/CashpointX/lib/database.py", line 209, in __getstate__
return sqlalchemy.ext.serializer.dumps(self)
File "/usr/share/pyshared/sqlalchemy/ext/serializer.py", line 153, in dumps
pickler.dump(obj)
File "/usr/lib/python2.7/copy_reg.py", line 84, in _reduce_ex
dict = getstate()
File "/data/Entwicklung/Aktuelle Projekte (Python)/CashpointX/lib/database.py", line 209, in __getstate__
return sqlalchemy.ext.serializer.dumps(self)
File "/usr/share/pyshared/sqlalchemy/ext/serializer.py", line 153, in dumps
pickler.dump(obj)
File "/usr/lib/python2.7/copy_reg.py", line 84, in _reduce_ex
dict = getstate()
File "/data/Entwicklung/Aktuelle Projekte (Python)/CashpointX/lib/database.py", line 209, in __getstate__
return sqlalchemy.ext.serializer.dumps(self)
File "/usr/share/pyshared/sqlalchemy/ext/serializer.py", line 153, in dumps
pickler.dump(obj)
File "/usr/lib/python2.7/copy_reg.py", line 84, in _reduce_ex
dict = getstate()
File "/data/Entwicklung/Aktuelle Projekte (Python)/CashpointX/lib/database.py", line 209, in __getstate__
return sqlalchemy.ext.serializer.dumps(self)
Hilft das?
deets

Wesentlich mehr als dein Stacktrace hilft deine Problembeschreibung. Das ist natuerlich grundfalsch was du da machst. Den Zustand dieser Objekte solltest du natuerlich *NICHT* in einer fluechtigen(!) Session speichern! Sondern stets in der Datenbank, um auch nach zB einem Neustart wieder aufsetzen zu koennen.

Insofern - du faengst dir hier ein Problem ein, dass du gar nicht haben brauchst.
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Es würde in der Datenbank gespeichert werden, sobald der Vorgang abgeschlossen ist. Es ist durchaus gewollt, dass der aktuelle Zustand (die aktuelle Session mit ihren Änderungen) nur flüchtig gespeichert ist, und bei einer verlorenen Session auch verloren geht.
Oder gibt es eine andere Möglichkeit, das zu machen?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

sprudel hat geschrieben: Oder gibt es eine andere Möglichkeit, das zu machen?
Indem man nicht comitted?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
sprudel
User
Beiträge: 250
Registriert: Donnerstag 8. März 2007, 17:12

Tu ich ja auch nicht, das transaction-Objekt muss aber wie gesagt so lange in der Session zwischengespeichert werden. Das Problem an der Session von Flask ist aber, dass diese insgeheim serialisiert werden. Ich möchte nun das Objekt so lange bereit halten, Änderungen zulassen (hinzufügen von Produkten, etc), bis der Vorgang abgeschlossen ist. Dann würde ich einen commit aufrufen, der das ganze dann endgültig speichert.
deets

Das ist kompletter Unsinn. Du vermischst ohne Not 2 serialisierungsarten, die außer die komplexität zu erhöhen keinen Gewinn haben.

Lass es einfach sein. Speicher die Zwischenstände deiner carts in der dB, wo du sie jederzeit inspizieren kannst, wo es egal ist ob die webanwendung zwischendurch unten war, wo die Session nicht Sticky sein muss, und wo du nicht in den Eingeweiden von 3rd-Party libraries rumfummelst ohne Garantie, dass das auf Dauer funktioniert.

In die Session gehört nur sowas wie eine User oder bestenfalls Vorgangs-ID. Damit ziehst du deine SA Objekte in einer neuen DB Trabsaktion hoch. Bei jedem request.
deets

Und um das noch mal ein bisschen weiter zu unterfuettern: wenn du so vorgehen willst wie skizziert, dann bedeutet das, dass du jede SQLAlchemy-Session nur genau einem User zuorden darfst. Statt einem Thread pro eingehendem Request. Das stellt dich nochmal vor genauso viele Huerden wie du sie jetzt schon vor dir hast. Zusaetzlich dazu limitiert es die Anzahl der gleichzeitig zugreifenden Benutzer auf die Anzahl der DB-Verbindungen. Es skaliert also nicht.
Antworten