I am writing some python gui app (PySide to be exact) and I am using my own class to handling DB. What's the correct way to use models? Currently I have something like this:
class DB(object):
def __init__(self, dbfile):
some db connect work
def updateEntry(entryid):
some update query etc
def getEntry(entryid):
fetching entry from db
def createEntry(entryvalue):
insert entry
class EntryModel(object):
def __init__(db,entryid=None,entryvalue=None):
self.db=db
self.entryid=entryid
self.entryvalue=entryvalue
if entryid is None:
self.db.createEntry(self.entryvalue)
elif self.entryvalue is None:
self.db.getEntry(self.entryid)
def some_func(self):
some other work
And it's working just fine... But I have a feeling that something is wrong here... I mean, I have to pass DB to each model, I don't think that's correct way. How to do it in proper way without using frameworks like SQLAlchemy and so on?
You can at least create a base class, let's called it Model (like in Django, or Base as it is called in SQLAlchemy)
We'll keep a reference to the db object as a class attribute so it is the same for all instances, and inherited so you don't have to pass it around
class Model(object):
db = None # This var is a class attribute
#classmethod
def init_db(cls):
cls.db = your_code_to_create_db()
class Entry(Model):
def __init__(self, entry_id, entry_value):
self.entry_id = entry_id
self.entry_value = entry_value
super(Entry, self).__init__()
def save(self):
# Use db here
self.db
# To use
Model.init_db() # Inits the one db var for the class
entry = Entry(...)
entry.save()
I hope you see the idea and adapt it to your needs!
Related
I am trying to build a framework and I am having problems with inheritance of properties between classes. I have a folder called framework with a init.py as follows:
from management import projects
from models import model
projects = projects()
model = model()
Then management looks like:
class projects(object):
def __init__(self):
self._current = None
#property
def current(self):
return self._current
def set_current(self, name):
"""setting a project by name, then self._current = name"""
Finally models:
class model(projects):
def __init__(self):
super(model, self).__init__()
#property
def current(self):
return super().current
Then when I try to use the framework from other script:
import framework as fw
fw.projects.set_current('foo')
fw.model.current
It returns None instead of 'foo'. I am pretty sure that the error is in the init file from framework where the instances are done before setting a project, I try some differents things but I don't have a clue. How can models class notice the changes in projects class?? Furthermore, I do not really want that model has the property current but I need its value for other methods (anyways I do not care if models has this property if it works as I expected). Thanks!!
EDIT:
For now I will store some metadata in the backend to access it from any class. But I am still wondering if there is a more elegant solution.
From my humble experience dealing intensively with Python in these last months, I would say it is absurd trying to access a children's property from the parent class because in this case, they are different instances.
projects = projects()
model = model()
Here you are creating two different instances and they do not share anything, so changes made in project won't be visible to model. I would recommend using composition over inheritance and inject a Project dependency onto Model.
Model class
class Model:
def __init__(self, project):
self.project = project
def get_current(self):
return self.project.get_current()
Project class
class Project:
# in Python 3 there is no need to use the implicit-object inheritance
def __init__(self):
self.current = None
def get_current(self):
return self.current
def set_current(self, value):
self.current = value
Playing with them
from framework.Model import Model
from framework.Project import Project
project = Project()
model = Model(project)
project.set_current("Example")
print(model.get_current()) # Example
I hope this helps.
Tomás.
Some calculations became too complex to maintain inside my model, so I decided to move them out and break the code in several classes and modules.
There's a single class serving as a facade which I would like to have available in a model instance to pass data to it.
It is being constructed from model instance:
class Calculator:
def __init__(self, field1: date, field2: float):
self.field1= field1
self.field2 = field2
#classmethod
def from_django_model(cls, django_model_instance):
field1 = django_model_instance.field1
field2 = float(django_model_instance.field2)
Currently I call it inside each property on my model like so:
class DjangoModel(models.Model):
# initialize the calculator
def calculator(self, group):
return calculator.Calculator.from_django_model(self)
# use it
#cached_property
def calculated_field(self):
try:
return self.calculator().calculation_method
except AttributeError:
return "Field not set!"
I feel this is not a good solution, since now on multiple methods I'm initializing the calculator object multiple times.
I would like to construct it once when the model is initialized and then pass it to the model instance.
I tried doing this with model manager, but the model instance is not available with it.
I am setting up a Django application with a lot of databases, and some of them are using the same models (they are not replicas). I already configured my routers and everything is working great. The problem appears when making the tests as I want to use factory-boy.
On a different project I could setup the database inside Meta but now, I have to select on which database to create an instance dynamically (if not, I would have to create a DjangoModelFactory for each database, which wouldn't be pretty).
Is there an (easier) way to specify the database dynamically for each creation?
As far as I know factory_boy (version <=2.10.0) doesn't provide anything like that.
Though, your problem is the perfect use case to use a context manager. It will allow you to set the database dynamically wherever you need and only under the desired scope, and also DRY!:
# factoryboy_utils.py
#classmethod
def _get_manager(cls, model_class):
return super(cls, cls)._get_manager(model_class).using(cls.database)
class DBAwareFactory(object):
"""
Context manager to make model factories db aware
Usage:
with DBAwareFactory(PersonFactory, 'db_qa') as personfactory_on_qa:
person_on_qa = personfactory_on_qa()
...
"""
def __init__(self, cls, db):
# Take a copy of the original cls
self.original_cls = cls
# Patch with needed bits for dynamic db support
setattr(cls, 'database', db)
setattr(cls, '_get_manager', _get_manager)
# save the patched class
self.patched_cls = cls
def __enter__(self):
return self.patched_cls
def __exit__(self, type, value, traceback):
return self.original_cls
and then, in your tests, you can do something like:
from factoryboy_utils import DBAwareFactory
class MyTest(TestCase):
def test_mymodel_on_db1(self):
...
with DBAwareFactory(MyModelFactory, 'db1') as MyModelFactoryForDB1:
mymodelinstance_on_db1 = MyModelFactoryForDB1()
# whatever you need with that model instance
...
# something else here
def test_mymodel_on_db2(self):
...
with DBAwareFactory(MyModelFactory, 'db2') as MyModelFactoryForDB2:
mymodelinstance_on_db2 = MyModelFactoryForDB2()
# whatever you need with that model instance
...
# something else here
I want to have my database implementation in a separate module or class. But I am struggling with a few details. A simple example:
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)
So far this is working fine. I can implement my interface to the database here and import this as a module.
Now I want to be able to choose the database file at runtime. So I need a way to define the Model classes without defining SqliteDatabase('somefile') before. I tried to encapsulate everything in a new Database class, which I can later import and create an instance from:
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)
Now I can call for example database = Database('database.db') or choose any other file name. I can even use multiple database instance in the same program, each with its own file.
However, there are two problems with this approach:
I still need to specify the database driver (SqliteDatabase) before defining the Model classes. To solve this I define the Model classes within the __init__() method, and then create an alias to with self.User = User. I don't really like this approach (it just doesn't feel like neat code), but at least it works.
I cannot use the #db.atomic() decorator since self is not known at class level, I would an instance here.
So this class approach does not seem to work very well. Is there some better way to define the Model classes without having to choose where you want to store your database first?
If you need to change database driver at the runtime, then Proxy is a way to go
# 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)
From now on each time you load the module, it won't need a database to be initialized. Instead, you can initialize it at the runtime and switch between multiple as follows
# 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")
But if the connection is the only thing that matters, then init() method will be enough.
Now I want to be able to choose the database file at runtime. So I
need a way to define the Model classes without defining
SqliteDatabase('somefile') before. I tried to encapsulate everything
in a new Database class, which I can later import and create an
instance from
Peewee uses the meta class to define the name of the table (Model.Meta.db_table) and database( Model.Meta.database)
set these attribute before calling a Model specific code ( either to create a table or to DML statements)
'Allow to define database dynamically'
Question: I cannot use the #db.atomic() decorator since self is not known at class level
Do it, as you do it with self.User.
I wonder about atomic() instead of atomic, but you tell is working fine.
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
Related: Define models separately from Database() initialization
I saw a code as follows (from https://github.com/daydayfree/diggit/blob/master/model/model.py) :
from database import Database
...
class Model(object):
#property
def db(self): return Database()
def insert(self, documents):
return self.db.insert(self.table, documents)
...
The main aim for #property is to provide access to the methods in Database() instance, am I correct?
So can I rewrite it as:
from database import Database
...
class Model(object):
def __init__(self):
self.db = Database()
def insert(self, documents):
return self.db.insert(self.table, documents)
and
from database import Database
...
class Model(object):
def db(self):
return Database()
def insert(self, documents):
return self.db().insert(self.table, documents)
...
? If not, what are the differences between them?
There are differences...
Method 1: property decorator
class Model(object):
#property
def db(self): return Database()
o = Model()
db1 = o.db #a database instance. No brackets
db2 = o.db #another database instance
o.db = foo #error due to read only property
Every time db is called it creates a new database instance.
Method 2: db set on initialization
class Model(object):
def __init__(self):
self.db = Database()
o = Model()
db1 = o.db #a database instance
db2 = o.db #the same database instance
o.db = foo #works fine so long as foo is defined
Every time db is accessed it returns the same database instance.
Method 3: db as a function
class Model(object):
def db(self):
return Database()
o = Model()
db1 = o.db() #a database instance. note the brackets
db2 = o.db() #another database instance
o.db = foo #works fine so long as foo is defined
Every time db is called it creates a new database instance.
The #property decorator is used to make calling a method look like calling an instance.
So, if you had a Model instance, you could get a new database object by calling the db what looks like the db attribute, but is really the db method:
>>> a = Model()
>>> a.db
Database()
In your first "rewrite" example, you create a db attribute in the __init__ method of your class. Now, every time you call the db attribute, you will get the same Database object each time (the one created during the __init__ call), not a new one as before.
To imagine this, you could replace return Database() with return random.random() from the python standard library. In the original implementation, a new number will be returned each time you call db. In your suggested implementation, the same number will be returned each time because random.random() was only called once (in the __init__ method), and it's output was saved in db.
Your second "rewrite" is essentially the same as the original implementation, except that you would call db as a method (i.e. with the open and close parentheses).
>>> a = Model()
>>> a.db()
Database()
It creates a new Database instance when it is called similar to your second alternative. This means every call to insert creates a new Database instance, inserts and then deletes the Database instance because there is no reference left pointing to it.
In your first alternative you will always acess the same instance. This means after a call to insert, the Database object is still there.