My SQLAlchemy models:
class Cover(db.Model):
# ... a bunch of other fields ...
#hybrid_property
def number_of_requests(self):
if self.requests:
return len(self.requests)
return 0
#number_of_requests.expression
def number_of_requests(cls):
return func.count(cls.requests)
class Request(db.Model):
# ... a bunch of other fields ...
# Cover that this request is requesting
cover_id = db.Column(db.Integer, db.ForeignKey('cover.id')
cover = db.relationship('Cover',
backref=backref("requests", cascade="all, delete-orphan"))
So, a simple one-to-many relationship between Cover and Request. The number_of_requests hybrid property should return the number of Requests associated with that particular Cover.
Now, in one of my Flask routes, I'm trying to grab the top 5 Covers by number of Requests. Here's what that looks like now:
# Get top cover requests
covers = Cover.query.order_by(Cover.number_of_requests).limit(5).all()
Unfortunately, this gives
ProgrammingError: (ProgrammingError) missing FROM-clause entry for table "request"
I suspect this is because in number_of_requests(cls) I'm trying to count the size of the requests list but SQLAlchemy hasn't included the request table in the original query. Any ideas on how to do that to avoid getting this error?
Change your expression part to:
#number_of_requests.expression
def number_of_requests(cls):
return (select([func.count(Request.id)])
.where(Request.cover_id == cls.id))
and read Correlated Subquery Relationship Hybrid again.
Related
I am using flask-restful this is
My class I want to insert
class OrderHistoryResource(Resource):
model = OrderHistoryModel
schema = OrderHistorySchema
order = OrderModel
product = ProductModel
def post(self):
value = req.get_json()
data = cls.schema(many=True).load(value)
data.insert()
In my model
def insert(self):
db.session.add(self)
db.session.commit()
schema
from config.ma import ma
from model.orderhistory import OrderHistoryModel
class OrderHistorySchema(ma.ModelSchema):
class Meta:
model = OrderHistoryModel
include_fk = True
Example Data I want to insert
[
{
"quantity":99,
"flaskSaleStatus":true,
"orderId":"ORDER_64a79028d1704406b6bb83b84ad8c02a_1568776516",
"proId":"PROD_9_1568779885_64a79028d1704406b6bb83b84ad8c02a"
},
{
"quantity":89,
"flaskSaleStatus":true,
"orderId":"ORDER_64a79028d1704406b6bb83b84ad8c02a_1568776516",
"proId":"PROD_9_1568779885_64a79028d1704406b6bb83b84ad8c02a"
}
]
this is what i got after insert method has started
TypeError: insert() takes exactly 2 arguments (0 given)
or there is another way to do this action?
Edited - released marshmallow-sqlalchemy loads directly to instance
You need to loop through the OrderModel instances in your list.
You can then use add_all to add the OrderModel objects to the session, then bulk update - see the docs
Should be something like:
db.session.add_all(data)
db.session.commit()
See this post for brief discussion on why add_all is best when you have complex ORM relationships.
Also - not sure you need to have all your models/schemas as class variables, it's fine to have them imported (or just present in the same file, as long as they're declared before the resource class).
You are calling insert on list cause data is list of model OrderHistoryModel instances.
Also post method doesn't need to be classmethod and you probably had an error there as well.
Since data is list of model instances you can use db.session.add_all method to add them to session in bulk.
def post(self):
value = req.get_json()
data = self.schema(many=True).load(value)
db.session.add_all(data)
db.session.commit()
I am having problems deleting records from PostTag table that contains Post and Tag column. This is my relation table:
tags = db.Table('PostTag',
db.Column('Tag', db.Integer, db.ForeignKey('Tag.Id')),
db.Column('Post', db.Integer, db.ForeignKey('Post.Id'))
)
and
tags = db.relationship(Tag, secondary=tags, backref=db.backref('Post', lazy='dynamic'))
When I do this:
from models.Post import Post
posts = Post.query.join(Post.tags).filter(Post.Id == id).all()
if(posts):
return posts
return False
and then
for posttag in post[0].tags:
db.session.delete(posttag)
db.session.flush()
Rows from many-to-many relation are deleted but also records from Tag table.
I just need to delete records from PostTag table for some condition (Post=1 for example)
I searched the internet but I did not find anything conclusive. I do not need cascade on many-to-many relationship.
This is sql log:
297 Query DELETE FROM `PostTag` WHERE `PostTag`.`Tag` = 14 AND `PostTag`.`Post` = 3
297 Query DELETE FROM `PostTag` WHERE `PostTag`.`Tag` = 14 AND `PostTag`.`Post` = 18
297 Query DELETE FROM `Tag` WHERE `Tag`.`Id` = 14
Last row 297 Query DELETE FROMTagWHERETag.Id= 14 should not be there.
UPDATE / maybe solution
I kind of solved this with:
sql = 'DELETE FROM PostTag WHERE Post=%s'
db.engine.execute(sql, post, ())
But that is not just ORM way. Same goes for insert. I will try to get this resolved ORM way. I will post answer if I make this problem go away.
Try this:
post = db.session.query(Post).get(1)
post.tags = []
db.session.commit()
Here we are redefining the collection post.tags to the empty array and committing the changes. To explain this I'll refer to the SQLAlchemy docs:
Collections in SQLAlchemy are transparently instrumented. Instrumentation means that normal operations on the collection are tracked and result in changes being written to the database at flush time.
So SQLAlchemy keeps track of the changes we make to the collection post.tags and updates it on commit.
If we had only one tag (say sometag) we could use the remove method like this:
post = db.session.query(Post).get(1)
post.tags.remove(sometag)
db.session.commit()
Try to remove objects from the collection instead:
post[0].tags.clear()
You could also remove individual items:
post[0].tags.remove(posttag)
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 am trying to figure out the best method of accessing all the matching microseries found in the Assessment class (table) in my database. I am trying to create a link that will send a user to those matching microseries. I am new and want to better understand the nuts and bolts of front-end engineering. I also have not seen a similar question asked here on Stacks.
I am not using JSON or XML yet, so this is a static HTML process. I have a few routes that access assessments at the moment, e.g.:
config.add_route('assessments', '/assessments')
config.add_route('assessment', '/assessments/{id:\d+}')
What I would like to better understand while implementing a method of finding matching microseries in the Assessment table and sending the user to a new page with those matching series:
How routes work, especially when accessing an attribute of a class, e.g. Assessment.microseries.
The goal of View code to convey the method mentioned above.
Pyramid links and Pyramid on Routes and URL Dispatch
Using: Python 2.7, SQLAlchemy, Pyramid
Assessment table:
class Assessment(Base):
__tablename__ = 'assessments'
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True)
text = Column(String(2000))
microseries = Column(Integer)
# more code
def __init__(self, name, text, user, video, categories, microseries):
# more code
API for interacting with an assessment is based on CRUD- create, retrieve, update and delete.
View Code:
this is not doing what I need it to do as I don't have a form of link to send the user to the matching series, e.g. Link1 would send a user to a new view with GET for all subseries of Link1: 1a, 1b, 1c....
#view_config(route_name='assessments', request_method='GET', renderer='templates/unique_assessments.jinja2', permission='create')
def view_unique_microseries_group(request):
logged_in_userid = authenticated_userid(request)
if logged_in_userid is None:
raise HTTPForbidden()
all_assessments = api.retrieve_assessments() #all assessments in a list
assessments_by_microseries = {} #dictonary
for x in all_assessments:
if x.microseries in assessments_by_microseries:
print("Already seen this microseries: %s" % x.microseries)
else:
assessments_by_microseries[x.microseries] = x
unique_assessments = sorted(assessments_by_microseries.values()) #.values() method to get the, err, values of the dict.
print 'unique_assessments:', unique_assessments
#a = HTTPSeeOther(location=request.route_url('view_microseries'))
return {'logged_in': logged_in_userid, 'unique_assessments': unique_assessments} #need to send some kind of list that can be shown on the template to send a user to the appropriately matching set of microseries
I have a data structure that declares relationships like this (pseudocode):
class User:
...
class Rating:
rater = User
post = Post
class Post:
ratings = hasmany(Rating)
page_id = ...
I'm building a website using these models, and I'd I'm lazy, so I pass my template the current logged in User, and a bunch of Posts on the current page. My page needs to know what rating the logged in user gave to each Post, so I use SQLAlchemy's one-instance-per-session feature:
posts = session.query(Post).filter(Post.page_id==current_pageid)
ratings = session.query(Post, Rating)\
.filter(Rating.rater==user.id)\
.filter(Post.page_id==current_pageid)
for post in posts:
post.user_rating = None # default value
for post, rating in ratings:
post.user_rating = rating
Then I pass my template the posts list. Is this ugly awful practice? Can I do it some better way?
What you are doing is good enough, except that your query is lacking a WHERE clause between the Post and Rating:
# ...
.filter(Post.id==Rating.post_id)\
But you can also get the result in one query:
qry = (session.query(Post, Rating).
outerjoin(Rating, and_(Post.id==Rating.post_id, Rating.user_id==user.id)).
filter(Post.page_id==current_pageid)
)
res = qry.all() # you can return *res* already to a view, but to get to your results, do below as well:
for post, rating in res:
post.user_rating = rating
posts = [post for post, rating in res]
return posts
Note that in your case posts is not really a list, but a query, and if you iterate over it second time, you might lose the user_rating attribute. You should be cautious returning session-bound objects like query to a view. It is safer to return lists like in the same code above. To fix your code, just add .all() to the query:
posts = session.query(Post).filter(Post.page_id==current_pageid).all()
Yes, it's bad practice. And it even might (in theory) beat you at some moment, e.g. when you query from the same session without clearing it for some post SQLAlchemy will return you the same cached object with already filled rating for some user unrelated to the current context. In practice it will work find in most cases.
Why not just pass a list of (post, rating) pairs to template? Most modern template engines available for Python can iterate over the list of pairs.
BTW, you can fetch both posts and ratings with single query (rating object will be None for OUTER JOIN when it's missing):
session.query(Post, Rating).select_from(Post)\
.outerjoin(Rating, (Post.id==Rating.post_id) & (Rating.rater==…))\
.filter(Post.page_id==…)