type one many python django-models one-to-one

python - many - one to one django



Comprueba si OneToOneField es None en Django (5)

Tengo dos modelos como este:

class Type1Profile(models.Model): user = models.OneToOneField(User, unique=True) ... class Type2Profile(models.Model): user = models.OneToOneField(User, unique=True) ...

Necesito hacer algo si el usuario tiene un perfil Type1 o Type2:

if request.user.type1profile != None: # do something elif request.user.type2profile != None: # do something else else: # do something else

Pero, para los usuarios que no tienen perfiles de tipo 1 o tipo 2, la ejecución de un código como ese produce el siguiente error:

Type1Profile matching query does not exist.

¿Cómo puedo verificar el tipo de perfil que tiene un usuario?

Gracias


¿Qué hay de usar try / except blocks?

def get_profile_or_none(user, profile_cls): try: profile = getattr(user, profile_cls.__name__.lower()) except profile_cls.DoesNotExist: profile = None return profile

¡Entonces, usa así!

u = request.user if get_profile_or_none(u, Type1Profile) is not None: # do something elif get_profile_or_none(u, Type2Profile) is not None: # do something else else: # d''oh!

Supongo que podría usar esto como una función genérica para obtener cualquier instancia de OneToOne inversa, dada una clase de origen (aquí: sus clases de perfil) y una instancia relacionada (aquí: request.user).


Es posible ver si una relación anulable uno-a-uno es nula para un modelo en particular simplemente probando el campo correspondiente en el modelo para None ness, pero solo si prueba en el modelo donde se origina la relación uno-a-uno. Por ejemplo, dadas estas dos clases ...

class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) class Restaurant(models.Model): # The class where the one-to-one originates place = models.OneToOneField(Place, blank=True, null=True) serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField()

... para ver si un Restaurant tiene un Place , podemos usar el siguiente código:

>>> r = Restaurant(serves_hot_dogs=True, serves_pizza=False) >>> r.save() >>> if r.place is None: >>> print "Restaurant has no place!" Restaurant has no place!

Para ver si un Place tiene un Restaurant , es importante comprender que al hacer referencia a la propiedad del restaurant en una instancia de Place genera una excepción de Restaurant.DoesNotExist si no hay un restaurante correspondiente. Esto sucede porque Django realiza una búsqueda internamente usando QuerySet.get() . Por ejemplo:

>>> p2 = Place(name=''Ace Hardware'', address=''1013 N. Ashland'') >>> p2.save() >>> p2.restaurant Traceback (most recent call last): ... DoesNotExist: Restaurant matching query does not exist.

En este escenario, la navaja de afeitar de Occam prevalece, y el mejor enfoque para hacer una determinación sobre si un Place tiene un Restautrant sería una construcción de try / except estándar como se describe here .

>>> try: >>> restaurant = p2.restaurant >>> except Restaurant.DoesNotExist: >>> print "Place has no restaurant!" >>> else: >>> # Do something with p2''s restaurant here.

Si bien la sugerencia de hasattr para utilizar hasattr funciona en la práctica, realmente solo funciona por accidente, ya que hasattr suprime todas las excepciones (incluido DoesNotExist ) en lugar de simplemente AttributeError , como debería ser. Como señaló Piet Delport, este comportamiento en realidad se corrigió en Python 3.2 por el siguiente ticket: http://bugs.python.org/issue9666 . Además, y a riesgo de sonar obstinado, creo que la construcción try / except es más representativa de cómo funciona Django, mientras que usar hasattr puede nublar el problema para los novatos, lo que puede crear FUD y propagar malos hábitos.


Me gusta la respuesta de joctee , porque es muy simple.

if hasattr(request.user, ''type1profile''): # do something elif hasattr(request.user, ''type2profile''): # do something else else: # do something else

Otros comentaristas han expresado su preocupación de que no funcione con ciertas versiones de Python o Django, pero la documentación de Django muestra esta técnica como una de las opciones:

También puede usar hasattr para evitar la necesidad de captura de excepciones:

>>> hasattr(p2, ''restaurant'') False

Por supuesto, la documentación también muestra la técnica de captura de excepción:

p2 no tiene un restaurante asociado:

>>> from django.core.exceptions import ObjectDoesNotExist >>> try: >>> p2.restaurant >>> except ObjectDoesNotExist: >>> print("There is no restaurant here.") There is no restaurant here.

Estoy de acuerdo con Joshua que la captura de la excepción aclara lo que está sucediendo, pero me parece más desordenado. Tal vez este es un compromiso razonable?

>>> print(Restaurant.objects.filter(place=p2).first()) None

Esto es solo consultar los objetos del Restaurant por lugar. Devuelve None si ese lugar no tiene restaurante.

Aquí hay un fragmento ejecutable para que juegues con las opciones. Si tiene Python, Django y SQLite3 instalados, debería ejecutarse. Lo probé con Python 2.7, Python 3.4, Django 1.9.2 y SQLite3 3.8.2.

# Tested with Django 1.9.2 import sys import django from django.apps import apps from django.apps.config import AppConfig from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.db import connections, models, DEFAULT_DB_ALIAS from django.db.models.base import ModelBase NAME = ''udjango'' def main(): setup() class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) def __str__(self): # __unicode__ on Python 2 return "%s the place" % self.name class Restaurant(models.Model): place = models.OneToOneField(Place, primary_key=True) serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False) def __str__(self): # __unicode__ on Python 2 return "%s the restaurant" % self.place.name class Waiter(models.Model): restaurant = models.ForeignKey(Restaurant) name = models.CharField(max_length=50) def __str__(self): # __unicode__ on Python 2 return "%s the waiter at %s" % (self.name, self.restaurant) syncdb(Place) syncdb(Restaurant) syncdb(Waiter) p1 = Place(name=''Demon Dogs'', address=''944 W. Fullerton'') p1.save() p2 = Place(name=''Ace Hardware'', address=''1013 N. Ashland'') p2.save() r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False) r.save() print(r.place) print(p1.restaurant) # Option 1: try/except try: print(p2.restaurant) except ObjectDoesNotExist: print("There is no restaurant here.") # Option 2: getattr and hasattr print(getattr(p2, ''restaurant'', ''There is no restaurant attribute.'')) if hasattr(p2, ''restaurant''): print(''Restaurant found by hasattr().'') else: print(''Restaurant not found by hasattr().'') # Option 3: a query print(Restaurant.objects.filter(place=p2).first()) def setup(): DB_FILE = NAME + ''.db'' with open(DB_FILE, ''w''): pass # wipe the database settings.configure( DEBUG=True, DATABASES={ DEFAULT_DB_ALIAS: { ''ENGINE'': ''django.db.backends.sqlite3'', ''NAME'': DB_FILE}}, LOGGING={''version'': 1, ''disable_existing_loggers'': False, ''formatters'': { ''debug'': { ''format'': ''%(asctime)s[%(levelname)s]'' ''%(name)s.%(funcName)s(): %(message)s'', ''datefmt'': ''%Y-%m-%d %H:%M:%S''}}, ''handlers'': { ''console'': { ''level'': ''DEBUG'', ''class'': ''logging.StreamHandler'', ''formatter'': ''debug''}}, ''root'': { ''handlers'': [''console''], ''level'': ''WARN''}, ''loggers'': { "django.db": {"level": "WARN"}}}) app_config = AppConfig(NAME, sys.modules[''__main__'']) apps.populate([app_config]) django.setup() original_new_func = ModelBase.__new__ @staticmethod def patched_new(cls, name, bases, attrs): if ''Meta'' not in attrs: class Meta: app_label = NAME attrs[''Meta''] = Meta return original_new_func(cls, name, bases, attrs) ModelBase.__new__ = patched_new def syncdb(model): """ Standard syncdb expects models to be in reliable locations. Based on https://github.com/django/django/blob/1.9.3 /django/core/management/commands/migrate.py#L285 """ connection = connections[DEFAULT_DB_ALIAS] with connection.schema_editor() as editor: editor.create_model(model) main()


Para verificar si la relación (OneToOne) existe o no, puede usar la función hasattr :

if hasattr(request.user, ''type1profile''): # do something elif hasattr(request.user, ''type2profile''): # do something else else: # do something else


Use select_related !

>>> user = User.objects.select_related(''type1profile'').get(pk=111) >>> user.type1profile None