2015-07-14 13 views
9

Sag, ich habe dieses einfache kleine Pony ORM Mapping hier. Die integrierte Enum-Klasse ist ab Python 3.4 neu und wurde auf 2.7 zurückportiert.Wie kann ich ein Python Enum mit Pony ORM speichern?

from enum import Enum 

from pony.orm import Database, Required 


class State(Enum): 
    ready = 0 
    running = 1 
    errored = 2 

if __name__ == '__main__': 
    db = Database('sqlite', ':memory:', create_db=True) 

    class StateTable(db.Entity): 
     state = Required(State) 

    db.generate_mapping(create_tables=True) 

Wenn ich das Programm ausführen, wird ein Fehler ausgelöst.

TypeError: No database converter found for type <enum 'State'> 

Dies passiert, weil Pony die Zuordnung des Aufzählungstyps nicht unterstützt. Natürlich besteht die Problemumgehung darin, den Enum-Wert nur zu speichern und einen Getter in der Klasse StateTable bereitzustellen, um den Wert erneut in Enum zu konvertieren. Aber das ist mühsam und fehleranfällig. Ich kann auch einfach ein anderes ORM verwenden. Vielleicht werde ich es tun, wenn dieses Problem zu viele Kopfschmerzen verursacht. Aber ich würde lieber bei Pony bleiben, wenn ich kann.

Ich würde viel lieber einen Datenbankkonverter erstellen, um die enum zu speichern, wie die Fehlermeldung darauf hinweist. Weiß jemand, wie man das macht?

UPDATE: Dank Ethans Hilfe, habe ich die folgende Lösung gefunden.

from enum import Enum 

from pony.orm import Database, Required, db_session 
from pony.orm.dbapiprovider import StrConverter 


class State(Enum): 
    ready = 0 
    running = 1 
    errored = 2 

class EnumConverter(StrConverter): 

    def validate(self, val): 
     if not isinstance(val, Enum): 
      raise ValueError('Must be an Enum. Got {}'.format(type(val))) 
     return val 

    def py2sql(self, val): 
     return val.name 

    def sql2py(self, value): 
     # Any enum type can be used, so py_type ensures the correct one is used to create the enum instance 
     return self.py_type[value] 

if __name__ == '__main__': 
    db = Database('sqlite', ':memory:', create_db=True) 

    # Register the type converter with the database 
    db.provider.converter_classes.append((Enum, EnumConverter)) 

    class StateTable(db.Entity): 
     state = Required(State) 

    db.generate_mapping(create_tables=True) 

    with db_session: 
     s = StateTable(state=State.ready) 
     print('Got {} from db'.format(s.state)) 

Antwort

7

Excerpt from some random mailing list:

2,2. WANDLER METHODEN

Jeder Konverter-Klasse sollte die folgenden Methoden definieren:

class MySpecificConverter(Converter): 

    def init(self, kwargs): 
     # Override this method to process additional positional 
     # and keyword arguments of the attribute 

     if self.attr is not None: 
      # self.attr.args can be analyzed here 
      self.args = self.attr.args 

     self.my_optional_argument = kwargs.pop("kwarg_name") 
     # You should take all valid options from this kwargs 
     # What is left in is regarded as unrecognized option 

    def validate(self, val): 
     # convert value to the necessary type (e.g. from string) 
     # validate all necessary constraints (e.g. min/max bounds) 
     return val 

    def py2sql(self, val): 
     # prepare the value (if necessary) to storing in the database 
     return val 

    def sql2py(self, value): 
     # convert value (if necessary) after the reading from the db 
     return val 

    def sql_type(self): 
     # generate corresponding SQL type, based on attribute options 
     return "SOME_SQL_TYPE_DEFINITION" 

Sie den Code der bestehenden Wandler studieren können, um zu sehen, wie diese Methoden umgesetzt werden.

+0

Danke! Ich hatte dieses Posting schon einmal gesehen, aber ich dachte, es ginge darum, SQL-Typen hinzuzufügen, und nicht Python-Typen. Ich habe meine Frage aktualisiert, um eine Lösung zu zeigen, die mir einfällt. – zalpha314

Verwandte Themen