2015-07-14 16 views
9

Dire che ho questa semplice piccola mappatura Pony ORM qui. La classe Enum incorporata è nuova a partire da Python 3.4 e backportata a 2.7.Come posso memorizzare un Python Enum usando Pony ORM?

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) 

Quando si esegue il programma, viene generato un errore.

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

Ciò accade perché Pony non supporta la mappatura del tipo enum. Ovviamente, la soluzione qui è solo per memorizzare il valore Enum e fornire un getter in Class StateTable per convertire nuovamente il valore in Enum. Ma questo è noioso e soggetto a errori. Posso anche solo usare un altro ORM. Forse lo farò se questo problema diventasse troppo mal di testa. Ma preferirei restare con Pony se posso.

Preferirei creare un convertitore di database per archiviare l'enumerazione, come suggerisce il messaggio di errore. Qualcuno sa come fare questo?

AGGIORNAMENTO: Grazie all'aiuto di Ethan, ho trovato la seguente soluzione.

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

risposta

7

Excerpt from some random mailing list:

2,2. METODI CONVERTITORE

Ogni classe Converter dovrebbe definire i seguenti metodi:

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" 

si può studiare il codice dei convertitori esistenti per vedere come questi metodi applicazione.

+0

Grazie! Avevo visto quella pubblicazione prima, ma pensavo che si trattava di aggiungere tipi SQL e non di tipi python. Ho aggiornato la mia domanda per mostrare una soluzione che mi è venuta in mente. – zalpha314

Problemi correlati