Python 检查 Django 中的 OneToOneField 是否为 None

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3463240/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 11:11:41  来源:igfitidea点击:

Check if OneToOneField is None in Django

pythondjangodjango-modelsone-to-one

提问by John Bright

I have two models like this:

我有两个这样的模型:

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


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

I need to do something if the user has Type1 or Type2 profile:

如果用户有 Type1 或 Type2 配置文件,我需要做一些事情:

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

But, for users that don't have either type1 or type2 profiles, executing code like that produces the following error:

但是,对于没有 type1 或 type2 配置文件的用户,执行这样的代码会产生以下错误:

Type1Profile matching query does not exist.

How can I check the type of profile a user has?

如何检查用户的个人资料类型?

Thanks

谢谢

回答by Geradeausanwalt

How about using try/except blocks?

使用 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

Then, use like this!

那就这样用吧!

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!

I suppose you could use this as a generic function to get any reverse OneToOne instance, given an originating class (here: your profile classes) and a related instance (here: request.user).

我想您可以使用它作为通用函数来获取任何反向 OneToOne 实例,给定一个原始类(此处:您的配置文件类)和一个相关实例(此处:request.user)。

回答by joctee

To check if the (OneToOne) relation exists or not, you can use the hasattrfunction:

要检查 (OneToOne) 关系是否存在,您可以使用该hasattr函数:

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

回答by ivan133

Use select_related!

使用select_related

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

回答by Joshua Pokotilow

It's possible to see if a nullable one-to-one relationship is null for a particular model simply by testing the corresponding field on the model for Noneness, but onlyif you test on the model where the one-to-one relationship originates. For example, given these two classes…

这是可能的,看看是否可空的一个一对一的关系是空特定模型简单地通过在模型中测试相应的领域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()

… to see if a Restauranthas a Place, we can use the following code:

...要查看 aRestaurant是否有 a 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!

To see if a Placehas a Restaurant, it's important to understand that referencing the restaurantproperty on an instance of Placeraises a Restaurant.DoesNotExistexception if there is no corresponding restaurant. This happens because Django performs a lookup internally using QuerySet.get(). For example:

要查看 aPlace是否有 a 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.

In this scenario, Occam's razor prevails, and the best approach for making a determination about whether or not a Placehas a Restautrantwould be a standard try/ exceptconstruct as described here.

在这种情况下,奥卡姆剃刀占上风,确定 a 是否Place具有 a的最佳方法是此处所述Restautrant的标准try/except构造。

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

While joctee's suggestion to use hasattrworks in practice, it really only works by accident since hasattrsuppresses allexceptions (including DoesNotExist) as opposed to just AttributeErrors, like it should. As Pi Delportpointed out, this behavior was actually corrected in Python 3.2 per the following ticket: http://bugs.python.org/issue9666. Furthermore — and at the risk of sounding opinionated — I believe the above try/ exceptconstruct is more representative of how Django works, while using hasattrcan cloud the issue for newbies, which may create FUD and spread bad habits.

虽然 joctee 建议hasattr在实践中使用作品,但它确实只是偶然地起作用,因为hasattr抑制了所有异常(包括DoesNotExist),而不仅仅是AttributeErrors,就像它应该的那样。正如Pi Delport指出的那样,根据以下票证,这种行为实际上在 Python 3.2 中得到了纠正:http: //bugs.python.org/issue9666。此外——冒着听起来自以为是的风险——我相信上面的try/except结构更能代表 Django 的工作方式,而使用hasattr可能会给新手带来问题,这可能会造成 FUD 并传播坏习惯。

EDITDon Kirkby'sreasonable compromise also seems reasonable to me.

编辑Don Kirkby 的合理妥协对我来说似乎也合理。

回答by Don Kirkby

I like joctee's answer, because it's so simple.

我喜欢joctee 的回答,因为它太简单了。

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

Other commenters have raised concerns that it may not work with certain versions of Python or Django, but the Django documentationshows this technique as one of the options:

其他评论者担心它可能不适用于某些版本的 Python 或 Django,但Django 文档将此技术显示为选项之一:

You can also use hasattr to avoid the need for exception catching:

您还可以使用 hasattr 来避免异常捕获的需要:

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

Of course, the documentation also shows the exception catching technique:

当然,文档中还展示了异常捕获技术:

p2 doesn't have an associated restaurant:

p2 没有关联的餐厅:

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

I agree with Joshuathat catching the exception makes it clearer what's happening, but it just seems messier to me. Perhaps this is a reasonable compromise?

我同意Joshua 的观点,即捕获异常可以更清楚地了解正在发生的事情,但对我来说似乎更混乱。也许这是一个合理的妥协?

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

This is just querying the Restaurantobjects by place. It returns Noneif that place has no restaurant.

这只是Restaurant按地点查询对象。None如果那个地方没有餐厅,它就会返回。

Here's an executable snippet for you to play with the options. If you have Python, Django, and SQLite3 installed, it should just run. I tested it with Python 2.7, Python 3.4, Django 1.9.2, and SQLite3 3.8.2.

这是一个可执行的片段,供您使用这些选项。如果您安装了 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()

回答by FreeWorlder

I am using a combination of has_attr and is None:

我正在使用 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

回答by pymen

in case you have the Model

如果你有模型

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

And you just need to know for any User that UserProfile exists/or not - the most efficient wayfrom the database point of view to use exists query.

而且您只需要知道 UserProfile 存在/不存在的任何用户 -从数据库的角度来看,使用存在查询最有效方法

Exists query will return just boolean, rather than reverse attribute accesslike hasattr(request.user, 'type1profile')- which will generate get queryand return full object representation

存在的查询将只返回布尔值,而不是像反向属性访问那样hasattr(request.user, 'type1profile')- 这将生成获取查询并返回完整的对象表示

To do it - you need to add a property to the User model

要做到这一点 - 您需要向 User 模型添加一个属性

class User(AbstractBaseUser)

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