If I have an sqlalchemy-mapped instance. Can I get an underlying dynamic query object corresponding to an attribute of said instance?
For example:
e = Employee()
e.projects
#how do I get a query object loaded with the underlying sql of e.projects
I think you're describing the lazy="dynamic" property of relationship(). something like
class Employee(Base):
__table_name__ = "employees"
...
projects = relationship(..., lazy="dynamic")
which will cause Employee().project to return a sqlalchemy.orm.Query instance instead of a collection containing the related items. However, that means there's no (simple) way to access the collection directly. If you still need that (most likely you really do want it to be lazily loaded, set up two relationship()s instead.
class Employee(Base):
__table_name__ = "employees"
...
projects_query = relationship(..., lazy="dynamic")
projects = relationship(..., lazy="select")
edit: You said
I need somehow to get the dynamic query object of an already lazy relationship mapped property.
Supposing we have an instance i of class Foo related to a class Bar by the property bars. First, we need to get the property that handles the relationship.
from sqlalchemy.orm.attributes import manager_of_class
p = manager_of_class(Foo).mapper.get_property('bars')
We'd like an expression that and_s together all of the columns on i that relate it to bars. If you need to operate on Foo through an alias, substitute it in here.
e = sqlalchemy.and_(*[getattr(Foo, c.key) == getattr(i, c.key)
for c in p.local_side])
Now we can create a query that expresses this relationship. Substitute aliases for Foo and Bar here as needed.
q = session.query(Foo) \
.filter(e) \
.join(Foo.bars) \
.with_entities(Bar)
Not sure about the question in general, but you definitely can enable SQL logging by setting echo=True, which will log the SQL statement as soon as you try to get value of the attribute.
Depending on your relationship configuration, it might have been eagerly pre-loaded.
Related
Say I have a model like this.
class Job(models.Model):
client = models.ForeignKey(Contacts, null=True)
and lets say I have job j. I know I can access the client belonging to j like this
j.client
but there is also
j.client_id
So my question is how does accessing j.client work?
Does django store client__id then when j.client is called it does a query to find the correct object?
Or is the object reference stored to j and accessing client__id is getting the id from the Contact object?
I've looked around the source code a bit but couldn't find the answer to my question
What you are probably talking about is client and client_id (single underscore).
The client_id attribute is a regular (integer) attribute. This is the foreign key that is saved to the database. You will only ever see a client_id column in the database, even though you specify the ForeignKey as client.
The client attribute is an object descriptor instance. It is a special class that overrides the __get__ and __set__ methods, so settings and accessing that attributes invokes that class's methods. This is the magic that gives you access to the actual related model instance. __get__ will retrieve the correct model instance from the database if it isn't loaded already, based on the client_id attribute. __set__ will also set the client_id attribute to the primary key of the related object, so that client_id is always up-to-date.
Note that this attribute is also available in query lookups, and is quite handy. E.g., if you have just the primary key of a foreign object, and not the model instance itself, the following queries look very similar:
job = Job.objects.filter(client__id=pk)
job = Job.objects.filter(client_id=pk)
However, underneath the first query accesses an attribute on the related object (double underscore) and performs an OUTER JOIN. The second query only ever accesses a local attribute, thus not having to perform the OUTER JOIN statement and saving performance.
This is explained in the docs:
https://docs.djangoproject.com/en/dev/ref/models/fields/#database-representation
In the database there is only client_id field (single underscore)
On the model instance you will have client attribute, when you access it this will cause Django to load the related object from the db and instantiate as another model instance.
You will also have client_id attribute (one underscore) which has the primary key value of the related object, as stored in the db field.
When doing ORM queries you are able to use client__id (double underscore) syntax to lookup against fields on the related model, eg you could also do client__name if Client model had a name field. This will become a SQL JOIN query across both models.
eg
Job.objects.get(client__id=1)
Job.objects.filter(client__name='John')
client = Client.objects.get(pk=1)
Job.objects.get(client=client)
j.client gives you the models.Model object. You can access it's properties like ...
client = j.client
id = client.id
name = client.name
But there should not be a j.client__id field. You should use j.client.id to get the id field. Although you can use j.client__id field to do filters and such.
So,
id = j.client.id # good
id = j.client__id # bad
and
job = Job.objects.get(client__id=1) # good
job = Job.objects.get(client.id=1) # bad
I have a class:
class ExampleClass(Base):
__tablename__ = 'chart'
id = Column(Integer, primary_key=True)
element_id = Column(Integer, ForeignKey('anotherTable.id'))
element = relationship(AnotherClass)
element2_id = Column(Integer, ForeignKey('anotherTable2.id'))
element2 = relationship(AnotherClass2)
I want to do a lookup based on the element_id and element2_id :
class ExampleClass(Base):
...
def get_with_element2(self, element2):
return session.query(ExampleClass).\
filter_by(element_id = self.element_id,
element2_id = element2.id).first()
The problem I find is that if I instantiate a new ExampleClass object and assign it an element, the element_id field is not being set:
a = ExampleClass(element=element_obj)
a.element_id => None
How can I solve this? What's the best way to deal with this kind of situation?
First off, all the examples below assume that your ExampleClass instance is at least in the pending state if not the "persistent" state (that is, session.add(a)). In other words, if you aren't yet interacting with a Session and have not added the ExampleClass object to one, then you won't get any of the database-level behavior of relationship(), of which maintaining foreign key column values is the primary feature. You are of course free to make this assignment directly:
a = ExampleClass(element_id=element_obj.id)
but this is obviously not making use of the automation provided by the relationship() construct.
The assignment of foreign key attributes by relationship() occurs during a flush, which is a process that only occurs when interaction with the database is necessary, such as before you emit a SQL statement using session.query() or before you complete your transaction using session.commit().
Generally, the philosophy of relationship() is that you'd deal only with the "element" and "element2" attributes here, and let the foreign key attributes be handled behind the scenes. You can write your query like this:
session.query(ExampleClass).\
filter_by(element=self.element).\
filter_by(element2=element2)
The ORM will take a comparison such as SomeClass.somerelationship=someobject and convert that into the foreign-key expression SomeClass.some_fk=some_id, but the difference is, the evaluation of the ultimate value of "some_id" is deferred until the right before the query is executed. Before the query is executed, the Query() object tells the Session to "autoflush", which will have the effect of your ExampleClass row being inserted along with the primary key identifier of element_obj being assigned to the element_id attribute on the ExampleClass object.
you could get a similar effect while still using the FK attributes like this, this is mostly just to understand how it works though:
session.query(ExampleClass).\
filter_by(element_id=bindparam(callable_=lambda: self.element_id)).\
filter_by(element2_id=element2.id)
or even more explicit, do the flush first:
session.flush()
session.query(ExampleClass).\
filter_by(element_id=self.element_id).\
filter_by(element2_id=element2.id)
So to the degree you'd want to refer to foreign-key attributes like element_id explicitly, you'd also need to do the things relationship() does for you explicitly, as well. If you deal strictly with object instances and the relationship()-bound attribute, and leave typical defaults like autoflush enabled, it will generally do the "right thing" and make sure attributes are ready when needed.
I'm a beginner in SQLAlchemy and found query can be done in 2 method:
Approach 1:
DBSession = scoped_session(sessionmaker())
class _Base(object):
query = DBSession.query_property()
Base = declarative_base(cls=_Base)
class SomeModel(Base):
key = Column(Unicode, primary_key=True)
value = Column(Unicode)
# When querying
result = SomeModel.query.filter(...)
Approach 2
DBSession = scoped_session(sessionmaker())
Base = declarative_base()
class SomeModel(Base):
key = Column(Unicode, primary_key=True)
value = Column(Unicode)
# When querying
session = DBSession()
result = session.query(SomeModel).filter(...)
Is there any difference between them?
In the code above, there is no difference. This is because, in line 3 of the first example:
the query property is explicitly bound to DBSession
there is no custom Query object passed to query_property
As #petr-viktorin points out in the answer here, there must be a session available before you define your model in the first example, which might be problematic depending on the structure of your application.
If, however, you need a custom query that adds additional query parameters automatically to all queries, then only the first example will allow that. A custom query class that inherits from sqlalchemy.orm.query.Query can be passed as an argument to query_property. This question shows an example of that pattern.
Even if a model object has a custom query property defined on it, that property is not used when querying with session.query, as in the last line in the second example. This means something like the first example the only option if you need a custom query class.
I see these downsides to query_property:
You cannot use it on a different Session than the one you've configured (though you could always use session.query then).
You need a session object available before you define your schema.
These could bite you when you want to write tests, for example.
Also, session.query fits better with how SQLAlchemy works; query_property looks like it's just added on top for convenience (or similarity with other systems?).
I'd recommend you stick to session.query.
An answer (here) to a different SQLAlchemy question might help. That answer starts with:
You can use Model.query, because the Model (or usually its base class, especially in cases where declarative extension is used) is assigned Session.query_property. In this case the Model.query is equivalent to Session.query(Model).
For the moments bars represents a String with all the Bar ids with ';' separator
like 6;5;9;15
class Foo(db.Model):
id = db.StringProperty(multiline=True)
name = db.StringProperty(multiline=True)
bars = db.TextProperty()
class Bar(db.Model):
id = db.StringProperty(multiline=True)
name = db.StringProperty(multiline=True)
What must I do to explicit Foo contains some bars in db?
And also how is going to look my :
f = Foo()
f.name= 'aName'
f.id = '3615'
f.bars = '6;5;9;15'
f.put()
For a start, you never want to store multiple values inside a single textfield. AppEngine's datastore supports multi-valued properties (such as ListProperty): you should use those.
That said, there's no multi-valued equivalent of ReferenceProperty built in. This article on the AppEngine documentation site gives a good example of how to model a list of keys.
Assuming this is a many-to-many relationship, you can use db.ListProperty(db.Key) for a list of the Bars' Keys, or, if you really want to just store the integer IDs, db.ListProperty(int).
If each Bar can only be a bar of a single foo, it's probably better to use a ReferenceProperty in the Bar entity, then use the automatic backreference in Foo to get a query for all of the bars.
By the way, naming a property "id" and making it a multiline string is probably a Bad Idea.
I'm using sqlalchemy with Elixir and have some troubles trying to make a query..
I have 2 entities, Customer and CustomerList, with a many to many relationship.
customer_lists_customers_table = Table('customer_lists_customers',
metadata,
Column('id', Integer, primary_key=True),
Column('customer_list_id', Integer, ForeignKey("customer_lists.id")),
Column('customer_id', Integer, ForeignKey("customers.id")))
class Customer(Entity):
[...]
customer_lists = ManyToMany('CustomerList', table=customer_lists_customers_table)
class CustomerList(Entity):
[...]
customers = ManyToMany('Customer', table=customer_lists_customers_table)
I'm tryng to find CustomerList with some customer:
customer = [...]
CustomerList.query.filter_by(customers.contains(customer)).all()
But I get the error:
NameError:
global name 'customers' is not defined
customers seems to be unrelated to the entity fields, there's an special query form to work with relationships (or ManyToMany relationships)?
Thanks
You can use regular filter: query.filter(CustomerList.customers.contains(customer)). See SQLAlchemy documentation for more examples. It's actually filter_by that's a special case. The query.filter_by(**kwargs) shorthand works only for simple equality comparisons. Under the cover query.filter_by(foo="bar", baz=42) is delegated to the equivalent of query.filter(and_(MyClass.foo == "bar", MyClass.baz == 42)). (There's actually slightly more magic to figure out which property you meant you have many entities, but it still uses simple delegation)
Read the error message with attention, it points to the source of problem. Did you mean
CustomerList.query.filter_by(CustomerList.customers.contains(customer)).all()?
Update: When using declarative definition you can use just defined relation in class scope, but these properties are not visible outside class:
class MyClass(object):
prop1 = 'somevalue'
prop2 = prop1.upper() # prop1 is visible here
val2 = MyClass.prop1 # This is OK
val1 = prop1.lower() # And this will raise NameError, since there is no
# variable `prop1` is global scope
CustomerList.query.filter_by(CustomerList.customers.contains(customer)).all() should work fine.