Opened 11 years ago
Last modified 8 months ago
#22492 assigned New feature
provide a way to prevent database queries on model objects
Reported by: | Chris Jerdonek | Owned by: | Raúl Cumplido |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | model, queryset, defer, only |
Cc: | chris.jerdonek@…, raulcumplido@…, Cesar Canassa, Hannes Ljungberg, Adam Johnson | Triage Stage: | Accepted |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
This is a feature request to provide a way to prevent database queries from happening in a block of code, especially on model objects (e.g. using a context manager, or an internal flag on model objects).
The motivation is for use with the QuerySet method `only()`, for example. (The only()
method is used to prevent unnecessary fields from being loaded from a database when querying.) Consider the case of using only()
to retrieve a list of many model objects, and then subsequently displaying them. If one executes some Django code after obtaining this list (e.g. by looping through the list of objects), it would be bad if this later code accidentally accessed some other field on each object. This could trigger the unintentional execution of many individual database queries (e.g. on a production database), with potentially bad consequences. I don't currently know an easy way to prevent this.
It would be good to have such a way. For example, Django could provide some sort of noQuery()
context manager which would raise an exception if the database were queried inside it. Code after the only()
line could be included in such a context manager. This could prevent accidentally hammering a database.
Alternatively, the QuerySet API could expose a way to return objects with some sort of no_query
flag set. If attribute access on such a model object required a database query, objects with such a flag set could instead raise an exception. This would also suffice to prevent accidental queries.
Change History (11)
comment:1 by , 11 years ago
Cc: | added |
---|
comment:2 by , 11 years ago
Triage Stage: | Unreviewed → Accepted |
---|---|
Version: | 1.6 → master |
comment:3 by , 10 years ago
Cc: | added |
---|---|
Owner: | changed from | to
Status: | new → assigned |
comment:4 by , 10 years ago
Is something like this a possible solution. I am thinking on the API, or do we prefer a solution where we have a context manager?
In [1]: from polls.models import Choice In [2]: p = Choice.objects.defer('body_yes', 'id') In [3]: choice = p[0] In [4]: choice.no_query_deferred = True In [5]: choice.body_yes Traceback (most recent call last): File "<....>", line xxx, in <module> QueryException: no_query flag is set to True and a query has been attempted. In [6]: choice.no_query_deferred = False In [7]: choice.body_yes Out[7]: u'yes'
comment:5 by , 5 years ago
FWIW a third party application implements such queryset sealing capabilities.
In [1]: from polls.models import Choice In [2]: p = Choice.objects.defer('body_yes').seal() In [3]: choice = p[0] In [4]: choice.body_yes Traceback (most recent call last): File "<....>", line xxx, in <module> UnsealedAttributeAccess:: Attempt to fetch deferred field "body_yes" on sealed <Choice instance>.
comment:8 by , 4 years ago
Cc: | added |
---|
comment:9 by , 3 years ago
Cc: | added |
---|
comment:10 by , 22 months ago
I want a feature that is similar to what was original described but for a different use case: I have a Celery task that needs a model object in the state it was when the task was enqueued, not in a state reflecting the corresponding database row(s) when the task is processed. I pass the object to the Celery task by serializing the entity (using django.serializers
) and saving exactly the information I want to make available to the task.
This object model has complex inner workings and multiple foreign keys. I want to be able to use the object model functionality and custom logic we have implemented in the model class, but I do _not_ want it reading (or writing) anything from the database after deserialization in the Celery task.
For this I want something less "smart" than a feature that knows about only
and defer
and QuerySet
objects. For this I want to be able to set a flag on an object that would disable any further database interactions originating from that object.
comment:11 by , 8 months ago
Cc: | added |
---|
FWIW this is being worked on through #28586 which adds modes to configure how deferred fields should behave when accessed.
Seems like an interesting idea to at least explore.