python - foreign - ¿Cómo funciona use_for_related_fields en Django?
how to create django models (2)
¿Es un escenario global? Entonces, si especifico este atributo en uno de los gestores de modelo, ¿será utilizado globalmente por todas las clases de modelo?
Si he entendido lo que quiere decir con global, la respuesta es no. Solo se utilizará para una clase si el administrador predeterminado (el primer administrador especificado en la clase) lo tiene configurado. Podría reutilizar el administrador en varios modelos, y el atributo solo tendría un efecto en aquellas clases para las que era el administrador predeterminado.
Esto es algo que pensé que un ejemplo ayudaría a comprender. Permite usar los siguientes modelos de perfil y miembro, vinculados en una relación uno a uno:
from django.db import models
class Member(models.Model):
name = models.CharField(max_length=100)
active = models.BooleanField()
def __unicode__(self):
return self.name
class Profile(models.Model):
member = models.OneToOneField(Member)
age = models.PositiveIntegerField()
def __unicode__(self):
return str(self.age)
Crearemos un par de miembros, el John activo y el Phil inactivo, y configuraremos un perfil para cada uno de ellos:
>>> m1 = Member(name=''John'', active=True)
>>> m1.save()
>>> p1 = Profile(member=m1, age=18)
>>> p1.save()
>>> m2 = Member(name=''Phil'', active=False)
>>> m2.save()
>>> p2 = Profile(member=m2, age=35)
>>> p2.save()
Cómo Django almacena las relaciones.
Primero, veamos cómo Django almacena una relación. Tomaremos el perfil de John y veremos su espacio de nombres:
>>> p = Profile.objects.get(id=1)
>>> p.__dict__
{''age'': 18, ''_state'': <django.db.models.base.ModelState object at 0x95d054c>, ''id'': 1, ''member_id'': 1}
Aquí podemos ver que la relación se define almacenando el valor de ID de la instancia miembro. Cuando hacemos referencia al atributo de member
, Django usa un administrador para recuperar los detalles del miembro de la base de datos y crear la instancia. Por cierto, esta información se almacena en caché en caso de que queramos usarla nuevamente:
>>> p.member
<Member: John>
>>> p.__dict__
{''age'': 18, ''_member_cache'': <Member: John>, ''_state'': <django.db.models.base.ModelState object at 0x95d054c>, ''id'': 1, ''member_id'': 1}
¿Qué administrador utilizar?
A menos que se indique lo contrario, Django usa un administrador estándar para esta búsqueda de relación en lugar de cualquier administrador personalizado agregado al modelo. Por ejemplo, supongamos que creamos el siguiente administrador para devolver solo los miembros activos:
class ActiveManager(models.Manager):
def get_query_set(self):
return super(ActiveManager, self).get_query_set().filter(active=True)
Y luego lo agregamos a nuestro modelo de miembro como el administrador predeterminado (en la vida real, esta es una mala idea ™ como una serie de utilidades, como el dumpdata
gestión de dumpdata
, utilizamos exclusivamente el administrador predeterminado y, por lo tanto, un administrador predeterminado que filtra (En algunos casos, la pérdida de datos o efectos secundarios desagradables similares):
class Member(models.Model):
name = models.CharField(max_length=100)
active = models.BooleanField()
objects = ActiveManager()
def __unicode__(self):
return self.name
Ahora que el administrador filtra a los usuarios inactivos, solo podemos recuperar la membresía de John de la base de datos:
>>> Member.objects.all()
[<Member: John>]
Sin embargo, ambos perfiles están disponibles:
>>> Profile.objects.all()
[<Profile: 18>, <Profile: 35>]
Y, por lo tanto, podemos llegar al miembro inactivo a través del perfil, ya que la búsqueda de relaciones aún utiliza un administrador estándar en lugar de nuestro administrador personalizado:
>>> p = Profile.objects.get(id=2)
>>> p.member
<Member: Phil>
Sin embargo, si ahora establecemos el atributo use_for_related_fields
en nuestro administrador, esto le dirá a Django que debe usar este administrador para cualquier búsqueda de relación:
class ActiveManager(models.Manager):
use_for_related_fields = True
def get_query_set(self):
return super(ActiveManager, self).get_query_set().filter(active=True)
Por lo tanto, ya no podemos obtener el registro de membresía de Phil de su perfil:
>>> p = Profile.objects.get(id=2)
>>> p.member
---------------------------------------------------------------------------
DoesNotExist Traceback (most recent call last)
/home/blair/<ipython console> in <module>()
/usr/lib/pymodules/python2.6/django/db/models/fields/related.pyc in __get__(self, instance, instance_type)
298 db = router.db_for_read(self.field.rel.to, instance=instance)
299 if getattr(rel_mgr, ''use_for_related_fields'', False):
--> 300 rel_obj = rel_mgr.using(db).get(**params)
301 else:
302 rel_obj = QuerySet(self.field.rel.to).using(db).get(**params)
/usr/lib/pymodules/python2.6/django/db/models/query.pyc in get(self, *args, **kwargs)
339 if not num:
340 raise self.model.DoesNotExist("%s matching query does not exist."
--> 341 % self.model._meta.object_name)
342 raise self.model.MultipleObjectsReturned("get() returned more than one %s -- it returned %s! Lookup parameters were %s"
343 % (self.model._meta.object_name, num, kwargs))
DoesNotExist: Member matching query does not exist.
Tenga en cuenta que esto solo tiene efecto si el administrador personalizado es el administrador predeterminado para el modelo (es decir, es el primer administrador definido). Entonces, intentemos usar el administrador estándar como el administrador predeterminado y nuestro administrador personalizado como administrador secundario:
class Member(models.Model):
name = models.CharField(max_length=100)
active = models.BooleanField()
objects = models.Manager()
active_members = ActiveManager()
def __unicode__(self):
return self.name
Los dos gerentes trabajan como se espera cuando miran directamente a los miembros:
>>> Member.objects.all()
[<Member: John>, <Member: Phil>]
>>> Member.active_members.all()
[<Member: John>]
Y como el administrador predeterminado puede recuperar todos los objetos, la búsqueda de relaciones también es exitosa:
>>> Profile.objects.get(id=2)
>>> p.member
<Member: Phil>
La realidad
Habiendo llegado tan lejos, ahora descubres por qué elegí una relación uno a uno para los modelos de ejemplo. Resulta que en realidad (y en contradicción con la documentación) el atributo use_for_related_fields
solo se usa para relaciones uno a uno. La clave externa y las relaciones de muchos a muchos lo ignoran. Este es el boleto # 14891 en el rastreador de Django.
¿Es posible tener un administrador modelo para una relación y otro para otra relación con el mismo modelo?
No. Aunque, en esta discusión del error mencionado anteriormente, esto surgió como una posibilidad en el futuro.
No puedo entender eso de los documentos. Es totalmente incierto para mí, más específicamente:
- ¿Es un escenario global? Entonces, si especifico este atributo en uno de los gestores de modelo, ¿será utilizado globalmente por todas las clases de modelo?
- Si no es un entorno global, ¿qué relaciones se verán afectadas exactamente?
- ¿Es posible tener un administrador modelo para una relación y otro para otra relación con el mismo modelo?
Lo más importante de todo es que agradecería cualquier buen ejemplo de uso, ya que la documentación carece de estos afaik. Gracias.
La respuesta corta es: hasta que se solucione este error , el ''uso para campos relacionados'' no funciona en django, excepto en las relaciones de uno a uno, así que no se moleste si su caso de uso es m2m o m2one, o te decepcionará.