4 votes

Comment gérer une base de données peewee dans un module séparé ?

Je veux que l'implémentation de ma base de données se fasse dans un module ou une classe séparé. Mais je me heurte à quelques détails. Un exemple simple :

from peewee import *

db = SqliteDatabase(':memory:')

class BaseModel(Model):
    class Meta:
        database = db

class User(BaseModel):
    name = CharField()

db.connect()
db.create_tables([User,])
db.commit()

@db.atomic()
def add_user(name):
    User.create(name=name).save()

@db.atomic()
def get_user(name):
    return User.get(User.name == name)

Jusqu'à présent, cela fonctionne bien. Je peux implémenter mon interface avec la base de données ici et l'importer en tant que module.

Maintenant, je veux pouvoir choisir le fichier de la base de données au moment de l'exécution. J'ai donc besoin d'un moyen de définir les classes du modèle sans définir les classes de la base de données. SqliteDatabase('somefile') avant. J'ai essayé de tout encapsuler dans une nouvelle classe de base de données, que je peux ensuite importer et dont je peux créer une instance :

from peewee import *

class Database:

    def __init__(self, dbfile):
        self.db = SqliteDatabase(dbfile)

        class BaseModel(Model):
            class Meta:
                database = self.db

        class User(BaseModel):
            name = CharField()

        self.User = User

        self.db.connect()
        self.db.create_tables([User,])
        self.db.commit()

    @self.db.atomic()    # Error as self is not known on this level
    def add_user(self, name):
        self.User.create(name=name).save()

    @self.db.atomic()    # Error as self is not known on this level
    def get_user(self, name):
        return self.User.get(self.User.name == name)

Maintenant je peux appeler par exemple database = Database('database.db') ou choisir n'importe quel autre nom de fichier. Je peux même utiliser plusieurs instances de base de données dans le même programme, chacune ayant son propre fichier.

Toutefois, cette approche pose deux problèmes :

  • Je dois encore spécifier le pilote de la base de données ( SqliteDatabase ) avant de définir les classes du modèle. Pour résoudre ce problème, je définis les classes de modèle dans la section __init__() et ensuite créer un alias avec self.User = User . Je n'aime pas vraiment cette approche (cela ne ressemble pas à du code soigné), mais au moins cela fonctionne.
  • Je ne peux pas utiliser le @db.atomic() depuis que le décorateur self n'est pas connu au niveau de la classe, je voudrais un exemple ici.

Cette approche par classe ne semble donc pas fonctionner très bien. Existe-t-il un meilleur moyen de définir les classes du modèle sans avoir à choisir d'abord où vous voulez stocker votre base de données ?

7voto

tarashypka Points 3182

Si vous devez changer de pilote de base de données au moment de l'exécution, alors Proxy est un chemin à parcourir

# database.py
import peewee as pw

proxy = pw.Proxy()

class BaseModel(pw.Model):
  class Meta:
    database = proxy

class User(BaseModel):
  name = pw.CharField()

def add_user(name):
  with proxy.atomic() as txn:
    User.create(name=name).save()

def get_user(name):
  with proxy.atomic() as txn:
    return User.get(User.name == name)

A partir de maintenant, chaque fois que vous chargez le module, il n'aura pas besoin d'une base de données pour être initialisé. Au lieu de cela, vous pouvez l'initialiser au moment de l'exécution et basculer entre plusieurs bases de données comme suit

# main.py

import peewee as pw
import database as db

sqlite_1 = pw.SqliteDatabase('sqlite_1.db')
sqlite_2 = pw.PostgresqlDatabase('sqlite_2.db')

db.proxy.initialize(sqlite_1)
sqlite_1.create_tables([db.User], safe=True)
db.add_user(name="Tom")

db.proxy.initialize(sqlite_2)
sqlite_2.create_tables([db.User], safe=True)
db.add_user(name="Jerry")

Mais si la connexion est la seule chose qui compte, alors init() sera suffisante.

0voto

MrPandav Points 1052

Maintenant, je veux pouvoir choisir le fichier de la base de données au moment de l'exécution. J'ai donc besoin d'un moyen de définir les classes de modèle sans définir SqliteDatabase('somefile') avant. J'ai essayé de tout encapsuler dans une nouvelle classe Database, que je peux ensuite importer et créer une instance à partir de

Peewee utilise la classe méta pour définir le nom de la table ( Model.Meta.db_table ) et la base de données ( Model.Meta.database )

Définissez ces attributs avant d'appeler un code spécifique au modèle (soit pour créer une table, soit pour des instructions DML).

Permettre de définir la base de données dynamiquement

0voto

stovfl Points 7200

Question : Je ne peux pas utiliser le décorateur @db.atomic() puisque self n'est pas connu au niveau de la classe.

Faites-le, comme vous le faites avec self.User .
Je me demande atomic() au lieu de atomic mais vous dites fonctionne bien .

class Database:

    def __init__(self, dbfile):
        self.db = SqliteDatabase(dbfile)

        ...
        @self.db.atomic()
        def __add_user(self, name):
            self.User.create(name=name).save()
        self.add_user = __add_user

        @self.db.atomic()
        def __get_user(self, name):
            return self.User.get(self.User.name == name)
        self.get_user = __get_user

En rapport : Définir les modèles séparément de l'initialisation de Database()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X