not - Django: ¿relacionado con Force Select?
filter django (3)
Aquí también hay un truco divertido:
class DefaultSelectOrPrefetchManager(models.Manager):
def __init__(self, *args, **kwargs):
self._select_related = kwargs.pop(''select_related'', None)
self._prefetch_related = kwargs.pop(''prefetch_related'', None)
super(DefaultSelectOrPrefetchManager, self).__init__(*args, **kwargs)
def get_queryset(self, *args, **kwargs):
qs = super(DefaultSelectOrPrefetchManager, self).get_queryset(*args, **kwargs)
if self._select_related:
qs = qs.select_related(*self._select_related)
if self._prefetch_related:
qs = qs.prefetch_related(*self._prefetch_related)
return qs
class Sandwich(models.Model):
bread = models.ForeignKey(Bread)
extras = models.ManyToManyField(Extra)
# ...
objects = DefaultSelectOrPrefetchManager(select_related=(''bread'',), prefetch_related=(''extras'',))
Luego puede reutilizar el administrador fácilmente entre clases modelo. Como caso de uso de ejemplo, esto sería apropiado si tuviera un método __unicode__
en el modelo que representara una cadena que incluyera cierta información de un modelo relacionado (o cualquier otra cosa que implique que casi siempre se requiere un modelo relacionado).
... y si realmente quieres volverte loco, aquí tienes una versión más generalizada. Le permite llamar a cualquier secuencia de métodos en el conjunto de consultas predeterminado con cualquier combinación de kwargs
o kwargs
. Puede haber algunos errores en el código, pero entiendes la idea.
from django.db import models
class MethodCalls(object):
"""
A mock object which logs chained method calls.
"""
def __init__(self):
self._calls = []
def __getattr__(self, name):
c = Call(self, name)
self._calls.append(c)
return c
def __iter__(self):
for c in self._calls:
yield tuple(c)
class Call(object):
"""
Used by `MethodCalls` objects internally to represent chained method calls.
"""
def __init__(self, calls_obj, method_name):
self._calls = calls_obj
self.method_name = method_name
def __call__(self, *method_args, **method_kwargs):
self.method_args = method_args
self.method_kwargs = method_kwargs
return self._calls
def __iter__(self):
yield self.method_name
yield self.method_args
yield self.method_kwargs
class DefaultQuerysetMethodCallsManager(models.Manager):
"""
A model manager class which allows specification of a sequence of
method calls to be applied by default to base querysets.
`DefaultQuerysetMethodCallsManager` instances expose a property
`default_queryset_method_calls` to which chained method calls can be
applied to indicate which methods should be called on base querysets.
"""
def __init__(self, *args, **kwargs):
self.default_queryset_method_calls = MethodCalls()
super(DefaultQuerysetMethodCallsManager, self).__init__(*args, **kwargs)
def get_queryset(self, *args, **kwargs):
qs = super(DefaultQuerysetMethodCallsManager, self).get_queryset(*args, **kwargs)
for method_name, method_args, method_kwargs in self.default_queryset_method_calls:
qs = getattr(qs, method_name)(*method_args, **method_kwargs)
return qs
class Sandwich(models.Model):
bread = models.ForeignKey(Bread)
extras = models.ManyToManyField(Extra)
# Other field definitions...
objects = DefaultQuerysetMethodCallsManager()
objects.default_queryset_method_calls.filter(
bread__type=''wheat'',
).select_related(
''bread'',
).prefetch_related(
''extras'',
)
El objeto MethodCalls
inspirado en python-mock es un intento de hacer que la API sea más natural. Algunos pueden encontrar eso un poco confuso. Si es así, puede __init__
ese código para un __init__
o kwarg que solo acepta una tupla de información de llamada de método.
Creé un modelo y estoy representando el modelo predeterminado / no modificado para él. Esto solo genera 64 consultas SQL porque tiene bastantes claves externas, y éstas a su vez tienen más claves foráneas.
¿Es posible obligarlo a que siempre (de forma predeterminada) realice una select_related
cada vez que se devuelve uno de estos modelos?
Cree un models.Manager
personalizado. models.Manager
y anule todos los métodos ( filter
, get
, etc.) y añada select_related a cada consulta. Luego configure este administrador como el atributo de objects
en el modelo.
Yo recomendaría simplemente select_related
su código y agregar select_related
donde sea necesario, porque hacer select_related en todo va a causar problemas graves de rendimiento más adelante (y no estaría del todo claro de dónde viene).
Puede crear un administrador personalizado y simplemente anular get_queryset
para que se aplique en todas partes. Por ejemplo:
class MyManager(models.Manager):
def get_queryset(self):
return super(MyManager, self).get_queryset().select_related(''foo'', ''bar'')
(Antes de Django 1.6, era get_query_set
).