[python] Django에서 OneToOneField가 None인지 확인하십시오.

다음과 같은 두 가지 모델이 있습니다.

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


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

사용자가 Type1 또는 Type2 프로필을 가지고 있다면 뭔가를해야합니다.

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

그러나 type1 또는 type2 프로필이없는 사용자의 경우 이와 같은 코드를 실행하면 다음 오류가 발생합니다.

Type1Profile matching query does not exist.

사용자의 프로필 유형을 어떻게 확인할 수 있습니까?

감사



답변

(OneToOne) 관계가 있는지 확인하려면 다음 hasattr함수를 사용할 수 있습니다 .

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


답변

단순히 모델의 해당 필드를 테스트하여 특정 모델에 대해 nullable 일대일 관계가 null 인지 확인할 수 None있지만, 일대일 관계가 시작된 모델에서 테스트하는 경우 에만 가능합니다. 예를 들어,이 두 클래스가 주어지면…

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()

… a에 Restaurant가 있는지 확인 Place하기 위해 다음 코드를 사용할 수 있습니다.

>>> 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!

A가 있는지 확인하려면 PlaceA가 들어 Restaurant, 그것을 참조하는 것을 이해하는 것이 중요 restaurant의 인스턴스에 속성을 Place높이는 Restaurant.DoesNotExist해당하는 레스토랑이없는 경우 예외입니다. 이것은 Django가 QuerySet.get(). 예를 들면 :

>>> 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.

이 시나리오에서는 Occam의 면도기가 우세하며 여기에 설명 된대로 a Place가 있는지 여부를 결정하는 가장 좋은 방법 Restautrant은 표준 try/ except구조 입니다.

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

hasattr실제로 작업 을 사용하라는 joctee의 제안 은 실제로는 s가 아닌 모든 예외 (포함 )를 hasattr억제 하므로 우연히 만 작동 합니다. 으로 파이 Delport가 : 지적,이 동작은 실제로는 다음 표에 따라 파이썬 3.2에서 수정되었습니다 http://bugs.python.org/issue9666 . 또한-그리고 의견이있는 것처럼 들릴 위험이 있습니다-위의 / 구조가 장고가 작동하는 방식을 더 잘 대표 한다고 생각하지만 , 사용 하면 FUD를 생성하고 나쁜 습관을 퍼뜨릴 수있는 초보자를위한 문제가 흐려질 수 있습니다.DoesNotExistAttributeErrortryexcepthasattr

편집 Don Kirkby의 합리적인 타협도 나에게 합리적으로 보입니다.


답변

나는 Joctee의 대답을 좋아 합니다. 왜냐하면 너무 간단하기 때문입니다.

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

다른 댓글 작성자는 특정 버전의 Python 또는 Django에서 작동하지 않을 수 있다는 우려를 제기했지만 Django 문서 는이 기술을 옵션 중 하나로 보여줍니다.

hasattr을 사용하여 예외 포착의 필요성을 피할 수도 있습니다.

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

물론 문서는 예외 포착 기술도 보여줍니다.

p2에는 관련 레스토랑이 없습니다.

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

나는 예외를 잡으면 무슨 일이 일어나고 있는지 더 명확하게 만든다는 Joshua의 의견에 동의 하지만, 나에게는 더 지저분 해 보입니다. 아마도 이것은 합리적인 타협입니까?

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

이것은 Restaurant장소별로 객체를 쿼리하는 것입니다. None그 장소에 식당이 없으면 반환 됩니다.

다음은 옵션을 사용할 수있는 실행 가능한 스 니펫입니다. Python, Django 및 SQLite3가 설치되어있는 경우 실행하면됩니다. Python 2.7, Python 3.4, Django 1.9.2 및 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()


답변

try / except 블록을 사용하는 것은 어떻습니까?

def get_profile_or_none(user, profile_cls):

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

    return profile

그럼 이렇게 사용하세요!

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!

원래 클래스 (여기 : 프로필 클래스)와 관련 인스턴스 (여기 : request.user)가 주어지면이를 일반 함수로 사용하여 역 OneToOne 인스턴스를 가져올 수 있다고 가정합니다.


답변

사용 select_related!

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


답변

모델이있는 경우

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

그리고 UserProfile이 존재하거나 존재하지 않는다는 것을 알고 있으면 데이터베이스 관점에서 존재 쿼리 를 사용 하는 가장 효율적인 방법 입니다 .

쿼리 그냥 부울이 아닌 리턴 존재 역 속성 액세스를 같이 hasattr(request.user, 'type1profile')생성되는 – GET 쿼리를 전체 객체 표현을 반환

그렇게하려면 사용자 모델에 속성을 추가해야합니다.

class User(AbstractBaseUser)

@property
def has_profile():
    return UserProfile.objects.filter(user=self.pk).exists()


답변

has_attr 조합을 사용하고 있으며 None입니다.

class DriverLocation(models.Model):
    driver = models.OneToOneField(Driver, related_name='location', on_delete=models.CASCADE)

class Driver(models.Model):
    pass

    @property
    def has_location(self):
        return not hasattr(self, "location") or self.location is None