ArgumentError in joinedload - python

I have these models:
class User(UserMixin, db.Model):
__tablename__ = 'users_user'
...
country = db.Column(db.Integer, db.ForeignKey('countries.id'))
class Country(db.Model):
__tablename__ = 'countries'
id = db.Column(db.Integer, primary_key=True)
...
user_country = db.relationship('User', backref='user_country', lazy='joined')
I am trying this query:
User.query.options(joinedload(Country.user_country)).filter_by(id=current_user.get_id()).first()
That will throw this error:
ArgumentError: Can't find property 'user_country' on any entity specified in this Query.
Note the full path from root (Mapper|User|users_user) to target entity must be specified.
What is wrong here?

The joinedload here is unnecessary.
By default relationships are lazily-loaded. This causes additional SELECT queries to be issued to retrieve the data. joinedload is one of the ways to force the relationship to be eagerly loaded by using a JOIN instead.
In this case, however, you've defaulted the relationship between User and Country to use eager loading by specifying lazy='joined'. This would reduce your query to
User.query.filter(id=current_user.get_id()).first()
While this will help you with the ArgumentError, we can go a little further. The query itself is unnecessary as well. current_user already has the data for its related Country because of the eager join. Accessing current_user.user_country will not send any additional queries to the database.

Related

What is missing in the TaggedBlogRecord model for inheritance in sqlalchemy for it to work?

I am trying modify the pyramid_blogr example. The only closest QA thread is this one and the link mentioned in that thread. I went through both of them. I am using concrete inheritance, by which I understand that separate tables will be created for each model. Still, when I query records from the second model, I get errors saying that the model do have columns like title or created in TaggedBlogRecord.
Errors
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: ====> entries.created <======
[SQL: SELECT entrieslanguage.id AS entrieslanguage_id
FROM entrieslanguage ORDER BY entries.created DESC
LIMIT ? OFFSET ?]
The sample code as follows
class BlogRecord(Base):
__tablename__ = 'entries'
id = Column(Integer, primary_key=True)
title = Column(Unicode(255), unique=True, nullable=False)
body = Column(UnicodeText, default=u'')
created = Column(DateTime, default=datetime.datetime.utcnow)
edited = Column(DateTime, default=datetime.datetime.utcnow)
class TaggedBlogRecord(BlogRecord):
__tablename__ = 'taggedentries'
__mapper_args__ = {'concrete':True}
id = Column(Integer, primary_key=True)
tags = Column(Unicode(255))
Read https://docs.sqlalchemy.org/en/latest/orm/inheritance.html#concrete-table-inheritance more closely (emphasis added):
Two critical points should be noted:
We must define all columns explicitly on each subclass, even those of
the same name. A column such as Employee.name here is not copied out
to the tables mapped by Manager or Engineer for us.
while the Engineer
and Manager classes are mapped in an inheritance relationship with
Employee, they still do not include polymorphic loading. Meaning, if
we query for Employee objects, the manager and engineer tables are not
queried at all.

flask many to many join as done by prefetch_related from django

I have following Group and Contact model in flask with Sql Alchemy ORM
group_contact = db.Table(
'group_contact',
db.Column('group_id', db.Integer, db.ForeignKey(
'group.id')),
db.Column('contact_id', db.Integer, db.ForeignKey(
'contact.id')),
db.PrimaryKeyConstraint('group_id', 'contact_id')
)
class Group(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
class Contact(db.Model):
id = db.Column(db.Integer, primary_key=True)
phone = db.Column(db.String(15), nullable=False, unique=True)
groups = db.relationship(
"Group", secondary=group_contact, backref='contacts')
Now I need to query Contact with groups:
contacts = Contact.query.join(Group, Contact.groups).all()
for contact in contacts:
print(contact.groups)
Here the problem is number of SQL query increases as number of contact increases when I execute above code.
Django ORM has prefetch_related() with queryset which does the following according to django docs.
prefetch_related, on the other hand, does a separate lookup for each relationship, and does the ‘joining’ in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related, in addition to the foreign key and one-to-one relationships that are supported by select_related.
Now I am trying to do the same thing with Sql Alchemy by the following code:
contacts = Contact.query.all()
contact_groups = group_contact.query.join(
Group
).filter(group_contact.contact_id.in_([item.id for item in contacts]))
But this gives me this error:
AttributeError: 'Table' object has no attribute 'query'
How can I get prefetch_related like feature from django with SqlAlchemy?
You want to tell SQLAlchemy to eagerly load related objects by using a relationship loading technique. SQLAlchemy can be told to load the groups together with the contacts in a single query.
For just this one query, you can add joinedload() option (it is available via the Flask-SQLAlchemy db object):
contacts = Contact.query.options(db.joinedload(Contact.groups)).all()
This pre-loads the Contact.groups attribute on each matched contact:
for contact in contacts:
# no new query issued to fetch groups, the data for the groups
# is already available
print(contact.groups)
The query executed looks like this:
SELECT
contact.id AS contact_id,
contact.phone AS contact_phone,
group_1.id AS group_1_id,
group_1.name AS group_1_name
FROM contact
LEFT OUTER JOIN (
group_contact AS group_contact_1
JOIN "group" AS group_1 ON group_1.id = group_contact_1.group_id
) ON contact.id = group_contact_1.contact_id
You can also set a default loading strategy for the relationship on the model; to always eagerly load groups, use lazy='joined' on the relationship:
class Contact(db.Model):
# ...
groups = db.relationship(
"Group", secondary=group_contact, backref='contacts',
lazy='joined')

SQLAlchemy not detecting ForeignKeys on _determine_joins, NoForeignKeysError

I'm having an issue where SQLAlchemy throws a NoForeignKeysError when it attempts to determine the primary join for two tables. The odd thing is this issue only occurs on the second instantiation of the models (i.e. if I drop the tables and recreate them it works the first time I run the script but not the subsequent times). Even odder is if I use inspect to view the ForeignKeys of the child tables I can clearly see that they exist (psql and just looking at the declarative base's metadata confirms this as well). My models are generated by a script and all other tables with similar or more complex relations work as expected however it is only this particular set of tables that has this issue.
The specific errors it throws are:
sqlalchemy.exc.NoForeignKeysError: Can't find any foreign key relationships between 'task_statuses' and 'task_data'.
and
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship task_statuses.task_data - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
Code snippet:
class TaskData(db_base):
__tablename__ = 'task_data'
__table_args__ = ({'extend_existing' : True})
id = Column(Integer, primary_key=True)
tags = relationship('TaskTags', backref='task_data', cascade='all, delete-orphan', passive_deletes=True)
statuses = relationship('TaskStatuses', backref='task_data', cascade='all, delete-orphan', passive_deletes=True)
class TaskTags(db_base):
__tablename__ = 'task_tags'
__table_args__ = ({'extend_existing' : True})
id = Column(Integer, primary_key=True)
taskdata_id = Column(Integer, ForeignKey('task_data.id', ondelete='CASCADE'))
class TagStatuses(db_base):
__tablename__ = 'task_statuses'
__table_args__ = ({'extend_existing' : True})
id = Column(Integer, primary_key=True)
taskdata_id = Column(Integer, ForeignKey('task_data.id', ondelete='CASCADE'))
Update:
After rereading the error message, I've realized that SQLAlchemy seems to think that task_statuses is the parent table when in fact it's the child. That still explains nothing and is actually more confusing but may be noteworthy. Even if a primaryjoin is specified the error persists.
The problem ended up being with Graphene-SQLAlchemy
For whatever reason, defining the SQLAlchemy model and the Graphene-SQLAlchemy equivalent in the same file was causing SQLAlchemy to have some unexpected behavior.
I resolved the issue by placing the models in a different file then generating the required Graphene-SQLAlchemy objects in the original file

How can I create a many-to-many relationship with SQLAlchemy using a SQL function?

I want to create a relationship between two models that have Geometry columns. For example:
from geoalchemy2.types import Geometry
from flask.ext.sqlalchemy import SQLAlchemy
from myapp import app
db = SQLAlchemy(app)
class Property(db.Model):
id = db.Column(db.Integer, primary_key=True)
street_address = db.Column(db.Text)
geom = db.Column(Geometry(geometry_type='POINT'))
service_areas = db.relationship(
'ServiceArea',
primaryjoin='ServiceArea.geom.ST_Contains(Geocode.geom)',
lazy='joined',
uselist=True,
viewonly=True,
)
class ServiceArea (db.Model):
name = db.Column(db.Text)
value = db.Column(db.Text)
geom = db.Column(Geometry(geometry_type='MULTIPOLYGON'))
In this example, a Property may be associated with many ServiceAreas, and a ServiceArea may be associated with many properties. However, there's not a secondary table for me to use for the relationship -- it is all determined by the ST_Contains function.
Whenever I run this code, I get an sqlalchemy.exc.ArgumentError exception telling me to "Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or are annotated in the join condition with the foreign() annotation."
When I add foreign around the ServiceArea.geom (even though it's not a foreign key), I get an error suggesting that I "Consider using the remote() annotation to accurately mark those elements of the join condition that are on the remote side of the relationship."
I've tried using foreign and remote separately as well as together (e.g., foreign(remote(ServiceArea.geom)) and remote(foreign(ServiceArea.geom))) but always get back one of the above errors. What am I doing wrong?

How to declared one-to-many if there are 2 fields for a same foreign key

I'm new to python(sqlalchemy), and I'm learning to build web site with pylons and sqlalchemy.
I have a problem when I declare the relationship between models. I've tried it several hours, but failed. But I think it should be a basic question.
I have two classes: User and Article, user can create articles, and modified the other people's article(like wiki).
So a user has created-articles and edited-articles.
class Article(Base):
__tablename__ = 'articles'
id = Column(Integer, primary_key=True)
title = ...
user_id = Column(Integer, ForeignKey('users.id'))
editor_id = Column(Integer, ForeignKey('users.id'))
# relations
user = relationship('User', backref='articles') # -> has error
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(20))
def __init__(self):
pass
But there is an error displayed:
InvalidRequestError: One or more mappers failed to compile. Exception was probably suppressed within a hasattr() call. Message was: Could not determine join condition between parent/child tables on relationship Article.user. Specify a 'primaryjoin' expression. If this is a many-to-many relationship, 'secondaryjoin' is needed as well.
I tried to add primaryjoin to the line('has error'), but don't know what it should be. I tried some codes, but none works.
Thank you in advance!
Ah, thats obvious one.
Article class has two references to User, user_id and editor_id, so SQLA does not know which one of them to use for your relation. Just use explicit primaryjoin:
user = relation('User', backref='articles', primaryjoin="Article.user_id==User.id")

Categories