I met an transaction problem when I used the python orm peewee these days. I save two book instances using this orm, and beween the two savings I raise an exception so I except that none of them are saved to database, but it doesn't work. Could anyone explain why? I am new to python, thanks.
this code is below:
from peewee import *
def get_db():
return SqliteDatabase("test.db")
class Book(Model):
id = PrimaryKeyField()
name = CharField()
class Meta:
database = get_db()
def test_transaction():
book1 = Book(name="book1")
book2 = Book(name="book2")
db = get_db()
db.create_tables([Book], safe=True)
try:
with db.transaction() as tran:
book1.save()
raise ProgrammingError("test")
book2.save()
except:
pass
for book in Book.select():
print(book.name)
if __name__ == '__main__':
test_transaction()
The problem is that when you are calling "get_db()" you are instantiating new database objects. Databases are stateful, in that they manage the active connection for a given thread. So what you've essentially got is two different databases, one that your models are associated with, and one that has your connection and transaction. When you call db.transaction() a transaction is taking place, but not on the connection you think it is.
Change the code to read as follows and it will work like you expect.
book1 = Book(name='book1')
book2 = Book(name='book2')
db = Book._meta.database
# ...
Related
I am trying to put data into a database using flask and peewee, and I have come across the following error: peewee.OperationalError: no such table: post
My models.py file is below:
from peewee import *
import datetime
db = SqliteDatabase('posts.db') #create database to interact with
#create a class for blogposts
class Post(Model):
id = PrimaryKeyField()
date = DateTimeField(default = datetime.datetime.now)
title = CharField()
text = TextField()
class Meta:
database = db
def initialize_db():
db.connect()
db.create_tables([Post], safe = True)
db.close()
I have Googled this, and for most people the lack of 'db.create_tables()' seems to be the problem. Obviously, it's in my code, so I am really not sure where the error is coming from. Some advice would be much appreciated. The problem seems to arise specifically when I try to populate the 'text' field using another .py file.
I adapted your code into the following snippet and it works for me:
from peewee import *
import datetime
db = SqliteDatabase('posts.db') #create database to interact with
#create a class for blogposts
class Post(Model):
id = PrimaryKeyField()
date = DateTimeField(default = datetime.datetime.now)
title = CharField()
text = TextField()
class Meta:
database = db
def initialize_db():
db.connect()
db.create_tables([Post], safe = True)
db.close()
initialize_db() #if db tables are not created, create them
post = Post.create(id=4, title="Some title", text="some text1") #add a new row
post.save() #persist it to db, not necessarily needed
You'll need to call the create method when creating a new Post (i.e. a new row in your database). Other than that, initialize_db() seems to work just fine.
If you are unable to perform any writes on the database, make sure you have write access in the directory where you are trying to do that (in this case, it would be your working directory)
Despite numerous recipes and examples in peewee's documentation; I have not been able to find how to accomplish the following:
For finer-grained control, check out the Using context manager / decorator. This allows you to specify the database to use with a given list of models for the duration of the wrapped block.
I assume it would go something like...
db = MySQLDatabase(None)
class BaseModelThing(Model):
class Meta:
database = db
class SubModelThing(BaseModelThing):
'''imagine all the fields'''
class Meta:
db_table = 'table_name'
runtime_db = MySQLDatabase('database_name.db', fields={'''imagine field mappings here''', **extra_stuff)
#Using(runtime_db, [SubModelThing])
#runtime_db.execution_context()
def some_kind_of_query():
'''imagine the queries here'''
but I have not found examples, so an example would be the answer to this question.
Yeah, there's not a great example of using Using or the execution_context decorators, so the first thing is: don't use the two together. It doesn't appear to break anything, just seems to be redundant. Logically that makes sense as both of the decorators cause the specified model calls in the block to run in a single connection/transaction.
The only(/biggest) difference between the two is that Using allows you to specify the particular database that the connection will be using - useful for master/slave (though the Read slaves extension is probably a cleaner solution).
If you run with two databases and try using execution_context on the 'second' database (in your example, runtime_db) nothing will happen with the data. A connection will be opened at the start of the block and closed and the end, but no queries will be executed on it because the models are still using their original database.
The code below is an example. Every run should result in only 1 row being added to each database.
from peewee import *
db = SqliteDatabase('other_db')
db.connect()
runtime_db = SqliteDatabase('cmp_v0.db')
runtime_db.connect()
class BaseModelThing(Model):
class Meta:
database = db
class SubModelThing(Model):
first_name = CharField()
class Meta:
db_table = 'table_name'
db.create_tables([SubModelThing], safe=True)
SubModelThing.delete().where(True).execute() # Cleaning out previous runs
with Using(runtime_db, [SubModelThing]):
runtime_db.create_tables([SubModelThing], safe=True)
SubModelThing.delete().where(True).execute()
#Using(runtime_db, [SubModelThing], with_transaction=True)
def execute_in_runtime(throw):
SubModelThing(first_name='asdfasdfasdf').save()
if throw: # to demo transaction handling in Using
raise Exception()
# Create an instance in the 'normal' database
SubModelThing.create(first_name='name')
try: # Try to create but throw during the transaction
execute_in_runtime(throw=True)
except:
pass # Failure is expected, no row should be added
execute_in_runtime(throw=False) # Create a row in the runtime_db
print 'db row count: {}'.format(len(SubModelThing.select()))
with Using(runtime_db, [SubModelThing]):
print 'Runtime DB count: {}'.format(len(SubModelThing.select()))
I've got a question about transactions in flask-sqlalchemy.
def funca():
instance = MyModel.query.get(5)
try:
instance.some_field = 'test'
instance.do_something()
instance.do_something_other() # Let's pretend that this causes exception
db.session.commit()
except Exception:
db.session.rollback()
# My model method
def do_something(self):
external_id = self.make_request_to_external_service()
self.external_id = external_id
# My model method
def do_something_other(self):
external_details = self.make_another_external_request()
self.external_details = external_details
How can I achieve now, that on Exception this will rollback only some_field and external_details changes and external_id is still saved in db?
Can I use nested transaction or can I start manually independent transactions for these 3 operations?
Problem here is that do_something will get external_id and it must be saved in db, because I can't do it second time, also if I won't rollback this transaction some_field will cause desynchronization between my db and external service.
For this example's purposes let's pretend that I can't do do_something before instance.some_field = 'test', because I could run this and commit this independently, but I can't do that in my real life app.
I am using peewee along with flask to populate my database with user's inputs and various other stats. How can I retrieve the data in sqlite3 database?
Here is the skeleton:
from peewee import *
database = SqliteDatabase('app4.db', autocommit=True)
class BaseModel(Model):
class Meta:
database = database
class Model1(BaseModel):
class Model2(BaseModel):
class Model3(BaseModel):
app = Flask(__name__)
#app.before_request
def func1():
database.connect()
#app.after_request
def func2(response):
database.close()
return response
#app.route('/', method= ['GET'])
def some_func():
#update pluto and create instances of Model2 and Model3 here
if __name__ == "__main__":
database.connect()
database.create_tables([Model1, Model2, Model3], True)
pluto, created = Model1.get_or_create(key = 2, key2 = 0)
My app is running and showing the updated instances of Model1 and Model2 which means that it has created the models and is continually updating the database.
But I don't know how to retrieve this data offline.
When I enter my sqllite database by sqlite3 app4.db and type .tables it shows me all the tables i.e. Model1, Model2, Model3 but typing in the query : select * from model1; gives nothing. I tried retrieving the data in Ipython shell too making a connection to database as above and querying list(Model1.select()) but it gives an empty list.
It looks like your database filename is being set to app.py:
database = SqliteDatabase('app.py', autocommit=True)
It should probably be app4.db if that's what you want to use:
database = SqliteDatabase('app4.db', autocommit=True)
The commands you are using look like the correct ones for doing it at the command line. Sounds to me like either 1 of 2 things are happening:
You are not actually putting data into the databases
You are not viewing the same database that you are populating.
Try renaming the app4.db file, and re-running your app and see if it creates a new one. This way you can make sure you are using sqlite3 on the correct one. If you are still not seeing any data using the commands you listed, I would assume that your app is not actually putting the data into the tables.
I have read the Peewee MySQL API documentation and this question; however, what I do not understand is how to connect to a specified table in a db using Peewee. Essentially all I'm trying to do is connect to a a table called Persons in a db called as_schema, set up some sort of basic object-relational mapping, and print out all entries' aNum column values.
My table Persons that I'm trying to read from has the following columns:
varchar called aNum
bool called access
bool called ajar
bool called ebr
date called weekof
My code consists of the following:
import peewee
from peewee import *
db = MySQLDatabase('as_schema', user='root',passwd='')#this connection path works perfectly, tried it using the standard MySQLdb.connect
class Person(Model):
class Meta:
database = db
class User(Person):
aNum = CharField()
Person.create_table()
person = User(aNum = 'a549189')
person.save();
for person in Person:
print person.aNum
The error I'm getting is:
class Person(Model):
class Meta:
database = db
db_table = 'Persons' # Add this.
In the docs, you can find a list of supported meta options:
http://docs.peewee-orm.com/en/latest/peewee/models.html#model-options-and-table-metadata