检查Django中的OneToOneField是否为None
我有两个这样的模型:
class Type1Profile(models.Model): user = models.OneToOneField(User, unique=True) ... class Type2Profile(models.Model): user = models.OneToOneField(User, unique=True) ...
如果用户有Type1或Type2configuration文件,我需要做一些事情:
if request.user.type1profile != None: # do something elif request.user.type2profile != None: # do something else else: # do something else
但是,对于没有type1或type2configuration文件的用户,执行这样的代码会产生以下错误:
Type1Profile matching query does not exist.
我如何检查用户的个人资料types?
谢谢
要检查(OneToOne)关系是否存在,可以使用hasattr
函数:
if hasattr(request.user, 'type1profile'): # do something elif hasattr(request.user, 'type2profile'): # do something else else: # do something else
可以通过简单地通过在模型上testing相应的字段为None
来检查一个特定模型是否为null,一对一关系是空的,但是只有在模型中testing了一对一关系时才可以。 例如,给定这两个类…
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()
…看看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!
要查看某个Place
是否有Restaurant
,重要的是要明白,如果没有相应的餐厅,则在Place
的实例上引用restaurant
属性会引发一个Restaurant.DoesNotExist
exception。 发生这种情况是因为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的razor盛行,确定某个Place
是否拥有Restautrant
的最佳方法是标准的try
/ except
构造,如此处所述。
>>> try: >>> restaurant = p2.restaurant >>> except Restaurant.DoesNotExist: >>> print "Place has no restaurant!" >>> else: >>> # Do something with p2's restaurant here.
虽然jocteebuild议在实践中使用hasattr
,但它确实只是偶然的工作,因为hasattr
所有情况一样,抑制所有exception(包括DoesNotExist
)而不是AttributeError
。 正如Piet Delport所指出的那样,这个行为实际上是在Python 3.2中通过以下标签更正的: http : //bugs.python.org/issue9666 。 此外,我认为上述try
/ except
构造更像是Django工作的代表,而使用hasattr
可能会使新手受到影响,从而产生FUD并传播坏习惯。
我喜欢joctee的答案 ,因为它非常简单。
if hasattr(request.user, 'type1profile'): # do something elif hasattr(request.user, 'type2profile'): # do something else else: # do something else
其他评论者担心,它可能不适用于某些版本的Python或Django,但Django文档将此技术显示为以下选项之一:
你也可以使用hasattr来避免exception捕获的需要:
>>> hasattr(p2, 'restaurant') False
当然,文档也显示了exception捕获技术:
p2没有关联的餐厅:
>>> from django.core.exceptions import ObjectDoesNotExist >>> try: >>> p2.restaurant >>> except ObjectDoesNotExist: >>> print("There is no restaurant here.") There is no restaurant here.
我同意约书亚的说法,抓住这个例外使事情变得更加清晰,但对我来说似乎更加混乱。 也许这是一个合理的妥协?
>>> 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.2testing了它。
# 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!
我想你可以使用它作为一个通用的函数来获得任何反向OneToOne实例,给定一个起源类(这里:您的configuration文件类)和一个相关的实例(这里:request.user)。
使用select_related
!
>>> user = User.objects.select_related('type1profile').get(pk=111) >>> user.type1profile None