Naravno da postoje razni gotovi mapperi ali svaka dodatna biblioteka u projektu zna da ima i svoju cenu. ODM-i nisu teski i mogu biti zabavni za pisanje, posebno ako ti za tvoje potrebe ne treba sva masinerija koju nude OSS resenja.
Posto sam obecao primer evo ga za python. Primer je vrlo jednostavan mada ovo sve moze mnogo vise da se zakomplikuje.
Dakle osnovna klasa i globalno mesto gde se registruju modeli i verzije bi bili u modulu koji bi izgledao otprilike ovako:
Kôd:
from pymongo import Connection
version_reg = {} #"Globalni" dict koji cuve verzije
def register_version_model(cls, version):
"""Registruj klasu koja je model za odredjenu verziju scheme"""
version_reg[version] = cls
def get_version_by_model(cls):
"""Vrati za koju verziju scheme je registrovana klasa"""
for v, c in version_reg.iteritems():
if c is cls: return v
return None
class MongoBaseModel(object):
"""Klasa koju svi mongo modeli nasledjuju, i redefinisu potrebne metode"""
_db = 'test_db'
_collection = 'test_col'
def _to_dict(self):
"""
Ova metoda mora da se redefinise - ona ustvari
sadrzi logiku predstave modela u bazi.
"""
raise NotImplementedError
def save(self):
#Napravi od sebe dict
d = self._to_dict()
#vidi gde si registrovan, i zapisi to u bazu
d['version'] = get_version_by_model(self.__class__)
Connection()[self._db][self._collection].insert(d)
@classmethod
def read(cls, spec):
"""
Metoda procita dokument ako postoji, i na osnovu verzije konstruise
objekat i vrati ga
"""
doc = Connection()[self._db][self._collection].find_one(spec)
if doc:
model_cls = version_reg.get(doc['version'], None)
if not model_cls:
raise ValueError("No model registered for this version")
return model_cls(doc)
return None
I onda zatim pravis svoj model (evo ga jako jednostavan primer):
Kôd:
class Calculation(MongoBaseModel)
"""Zamisljena klasa kalkulacije ciji se rez cuva u bazu"""
def __init__(self, doc):
self.date = doc['date']
self.result = doc['result']
def _to_dict(self):
"""Obrnuto od konstruktora - u ovom primeru nezanimljivo"""
return dict(
date = self.date
result = self.result
)
Koji i registrujes sa:
Kôd:
register_version_model(Calculation, 1)
E sada kada je model potrebno malo izmeniti:
Kôd:
class NewCalculation(MongoBaseModel)
"""Nova klasa kalkulacije koja sada cuva i koliko je kalc trajala"""
def __init__(self, doc):
self.date = doc['date']
self.result = doc['result']
self.duration = doc['duration']
def _to_dict(self):
"""Obrnuto od konstruktora - u ovom primeru nezanimljivo"""
return dict(
date = self.date
result = self.result
duration = self.duration
)
register_version_model(NewCalculation, 2)
Ono sto se ne vidi iz primera je da ako ove dve klase nasledjuju od trece klase koja definise neki interfejs za rad (i pegla nesuglasice) - potpuno si se resio glavobolje tipa - "sta sam to procitao??". Nekad je OO i dobar
Kao sto sam rekao moze ovo mnogo vise da se zakomplikuje i nabudzi (metaprogramiranjem i slicnim cakama), naravno fale provere gresaka itd. ali funkcionalni, mali i lagani ODM ne trazi mnogo vise od ovih 30ak linija u Pythonu, bez da se patis sa nekim bloat-om sa github-a.
Ako nekome neka python-specific caka nije jasna (mada ih bas i nema u gornjem kodu) rado cu pojasniti.