onetoonefield one many foreign python django django-models

python - many - Django: ¿Devuelve ''Ninguno'' desde OneToOneField si el objeto relacionado no existe?



onetoonefield django (6)

Tengo una clase de Django como esta:

class Breakfast(m.Model): # egg = m.OneToOneField(Egg) ... class Egg(m.Model): breakfast = m.OneToOneField(Breakfast, related_name="egg")

¿Es posible breakfast.egg == None si no hay un Egg relacionado con el Breakfast ?

Editar : Olvidé mencionar: prefiero no cambiar el related_name a algo como related_name="_egg" , luego tengo algo como:

@property def egg(self): try: return self.egg except ...: return None

Porque uso el nombre de egg en las consultas, y prefiero no tener que cambiar las consultas para usar _egg .


Acabo de encontrarme con este problema y encontré una solución extraña para él: si selecciona_related (), entonces el atributo será None si no existe una fila relacionada, en lugar de generar un error.

>>> print Breakfast.objects.get(pk=1).egg Traceback (most recent call last): ... DoesNotExist: Egg matching query does not exist >>> print Breakfast.objects.select_related("egg").get(pk=1).egg None

No tengo idea si esto puede considerarse una característica estable.


Django 1.10 solución como por Fedor en la respuesta aceptada:

from django.core.exceptions import ObjectDoesNotExist from django.db.models.fields.related import OneToOneField from django.db.models.fields.related_descriptors import ReverseOneToOneDescriptor class ReverseOneToOneOrNoneDescriptor(ReverseOneToOneDescriptor): def __get__(self, instance, cls=None): try: return super(ReverseOneToOneOrNoneDescriptor, self).__get__(instance=instance, cls=cls) except ObjectDoesNotExist: return None class OneToOneOrNoneField(models.OneToOneField): """A OneToOneField that returns None if the related object doesn''t exist""" related_accessor_class = ReverseOneToOneOrNoneDescriptor


Este campo de django personalizado hará exactamente lo que quieras:

class SingleRelatedObjectDescriptorReturnsNone(SingleRelatedObjectDescriptor): def __get__(self, *args, **kwargs): try: return super(SingleRelatedObjectDescriptorReturnsNone, self).__get__(*args, **kwargs) except ObjectDoesNotExist: return None class OneToOneOrNoneField(models.OneToOneField): """A OneToOneField that returns None if the related object doesn''t exist""" related_accessor_class = SingleRelatedObjectDescriptorReturnsNone

Para usarlo:

class Breakfast(models.Model): pass # other fields class Egg(m.Model): breakfast = OneToOneOrNoneField(Breakfast, related_name="egg") breakfast = Breakfast() assert breakfast.egg == None


OmerGertel ya señaló la opción null . Sin embargo, si entiendo tu modelo lógico correcto, entonces lo que realmente necesitas es una clave externa única y con nomenclatura de Breakfast to Egg. Entonces, un desayuno puede o no tener un huevo, y un huevo en particular solo puede asociarse con un desayuno.

Usé este modelo:

class Egg(models.Model): quality = models.CharField(max_length=50) def __unicode__(self): return self.quality class Breakfast(models.Model): dish = models.TextField() egg = models.ForeignKey(Egg, unique=True, null=True, blank=True) def __unicode__(self): return self.dish[:30]

y esta definición de administrador:

class EggAdmin(admin.ModelAdmin): pass class BreakfastAdmin(admin.ModelAdmin): pass admin.site.register(Egg, EggAdmin) admin.site.register(Breakfast, BreakfastAdmin)

Entonces podría crear y asignar un huevo en la página de edición para un desayuno, o simplemente no asignar uno. En este último caso, la propiedad del huevo del desayuno fue Ninguna. Un huevo particular ya asignado a un desayuno no pudo ser seleccionado para otro.

EDITAR:

Como OmerGertel ya dijo en su comentario, alternativamente podrías escribir esto:

egg = models.OneToOneField(Egg, null=True, blank=True)


Sé que en ForeignKey puede tener null=True cuando desea permitir que el modelo no apunte a ningún otro modelo. OneToOne es solo un caso especial de ForeignKey:

class Place(models.Model) address = models.CharField(max_length=80) class Shop(models.Model) place = models.OneToOneField(Place, null=True) name = models.CharField(max_length=50) website = models.URLField() >>>s1 = Shop.objects.create(name=''Shop'', website=''shop.com'') >>>print s1.place None


Yo recomendaría usar try / except Egg.DoesNotExist cada vez que necesite acceder a Breakfast.egg ; al hacerlo, deja muy claro lo que está sucediendo para las personas que leen su código, y esta es la manera canónica de manejar registros inexistentes en Django.

Si realmente quiere evitar saturar su código con try / except s, podría definir un método get_egg en Breakfast así:

def get_egg(self): """ Fetches the egg associated with this `Breakfast`. Returns `None` if no egg is found. """ try: return self.egg except Egg.DoesNotExist: return None

Esto hará que sea más claro para las personas que leen su código que se derivan los huevos, y puede insinuar el hecho de que se realiza una búsqueda cuando se llama a Breakfast.get_egg() .

Personalmente, optaría por el enfoque anterior para mantener las cosas lo más claras posible, pero pude ver por qué uno puede inclinarse a usar el último enfoque en su lugar.