Python: usare MongoEngine per MongoDB

Python: usare MongoEngine per MongoDB

MongoEngine è un ORM per MongoDB che ci consente di semplificare la modalità con cui interroghiamo questo database in Python.

La connessione a MongoDB avviene tramite la funzione connect() di MongoEngine.

from mongoengine import connect

mongo_url = 'mongodb://localhost:27017/data'

connect(host=mongo_url)

Il punto di forza di MongoEngine sta nella possibilità di creare modelli con classi che estendono la classe base Document che rappresenta un documento di una collezione di MongoDB.

from mongoengine import Document, ObjectIdField, StringField, IntField, EmailField

class User(Document):
    id = IntField()
    first_name = StringField(max_length=255, required=True)
    last_name = StringField(max_length=255, required=True)
    email = EmailField()
    gender = StringField(max_length=100, required=True)
    ip_address = StringField(max_length=100, required=True)

Avendo usato la classe Document, MongoEngine si aspetta che nello schema definito sopra siano elencati esattamente tutti i campi esistenti e che ogni documento onori sempre lo schema dato.

Se per esigenze implementative potrebbero essere creati documenti con campi variabili, allora si consiglia di usare la classe DynamicDocument invece che Document. L'unica limitazione posta in questo caso è che il nome dei campi non può cominciare con un underscore.

Una volta definito lo schema, MongoEngine ci permette anche di creare query personalizzate che verranno associate al modello di base. Per questo scopo possiamo usare la classe QuerySet che poi assoceremo al modello tramite il suo attributo meta.

from mongoengine import Document, ObjectIdField, StringField, IntField, EmailField, QuerySet

class UserQuerySet(QuerySet):
    def get_latest(self):
        return self.order_by('-id').limit(3)

class User(Document):
    meta = {'queryset_class': UserQuerySet}
    id = IntField()
    first_name = StringField(max_length=255, required=True)
    last_name = StringField(max_length=255, required=True)
    email = EmailField()
    gender = StringField(max_length=100, required=True)
    ip_address = StringField(max_length=100, required=True)

Il metodo get_latest() reperisce gli ultimi tre utenti inseriti ordinati per ID in modo decrescente. Viene invocato in questo modo usando l'oggetto principale di ciascun modello, ossia objects:

users = User.objects.get_latest()

Quello che viene restituito non è una lista, ma un'istanza della classe UserQuerySet:

<class 'models.user.UserQuerySet'>

Possiamo convertire questi risultati in una lista usando list().

users = list(User.objects.get_latest())

Otterremo:

[<User: User object>, <User: User object>, <User: User object>]

Ciascun elemento è un'istanza del nostro modello User. Essendo oggetti, possiamo interrogare i loro attributi per convertirli ad esempio in dizionari.

results = []
for user in users:
    result = {'id': user.id,
              'name': f'{user.first_name} {user.last_name}',
              'email': user.email,
              'ip': user.ip_address,
              'gender': user.gender
              }
    results.append(result)

Per quanto riguarda le query con operatori, MongoEngine adotta un approccio diverso rispetto a quello tradizionale: invece di usare un dizionario avente come chiavi gli operatori di MongoDB, MongoEngine usa la seguente sintassi:

attributo__operatore

Dove attributo è il nome di un attributo di un modello (qui ad esempio first_name), seguito da un doppio underscore e infine dal nome dell'operatore.

Ad esempio, se volessimo trovare tutti i documenti della nostra collezione users il cui nome (first_name) contiene la parola chiave John, scriveremo:

search_results = list(User.objects(first_name__contains='John'))

MongoEngine supporta i metodi tradizionali limit() e skip() di MongoDB per limitare e gestire il numero di risultati restituiti, e offre anche lo slicing come per le liste tradizionali:

results = User.objects[:3]

In questo caso vengono restituiti solo i primi tre documenti della collezione users.

Una query più simile a quelle tradizionali è invece la seguente:

results = User.objects(first_name='John').limit(3)

Rispetto ad altre soluzioni, MongoEngine offre numerosi vantaggi in quanto permettendoci di definire dei modelli ci mette a disposizione una serie di soluzioni molto valide per organizzare e gestire il nostro codice in modo modulare.

Documentazione

MongoEngine

Torna su