2010-08-11 40 views
61

Tôi có hai mô hình như thế này:Kiểm tra nếu OneToOneField là None trong Django

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


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

tôi cần phải làm một cái gì đó nếu người dùng có Type1 hoặc Type2 hồ sơ:

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

Nhưng, cho người dùng không có hồ sơ type1 hoặc type2, thực thi mã như vậy tạo ra lỗi sau:

Type1Profile matching query does not exist. 

Làm cách nào tôi có thể kiểm tra ype của hồ sơ người dùng có?

Cảm ơn

Trả lời

9

Cách sử dụng 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 

Sau đó, hãy sử dụng như thế này!

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! 

Tôi cho rằng bạn có thể sử dụng điều này như một hàm tổng quát để có được bất kỳ trường hợp OneToOne ngược lại, cho một lớp học có nguồn gốc (ở đây: lớp hồ sơ của bạn) và một trường hợp liên quan (ở đây: request.user).

60

Để kiểm tra xem (OneToOne) mối quan hệ tồn tại hay không, bạn có thể sử dụng chức năng hasattr:

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

Cảm ơn bạn đã giải pháp này. Thật không may, điều này không làm việc tất cả các thời gian. Trong trường hợp bạn muốn làm việc với 'select_related()' ngay bây giờ hoặc trong tương lai - hay thậm chí có thể để chắc chắn bạn cũng xử lý các loại khác của ma thuật có thể xảy ra ở những nơi khác - bạn cần phải mở rộng các thử nghiệm như sau: 'nếu hasattr (đối tượng, 'onetoonerevrelattr') và object.onetoonerevrelattr! = None' –

+6

Lưu ý rằng trong Python <3.2, 'hasattr' sẽ nuốt * tất cả * ngoại lệ xảy ra trong quá trình tra cứu cơ sở dữ liệu, và không chỉ' DoesNotExist'. Điều này có thể bị hỏng, và không phải những gì bạn muốn. –

+0

không hoạt động với python 2.7. Ngay cả khi OneToOne không tồn tại, nó trả về một đối tượng django.db.models.fields.related.RelatedManager. – alexpirine

3

Sử dụng select_related!

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

Tôi biết nó hoạt động như thế này, nhưng hành vi này của select_related thực sự được ghi chép? – Kos

+1

Tôi vừa thử điều này trong Django 1.9.2, và nó tăng 'RelatedObjectDoesNotExist'. –

33

Có thể thấy nếu một nullable one-to-one mối quan hệ là null cho một mô hình cụ thể chỉ đơn giản bằng cách kiểm tra các lĩnh vực tương ứng trên mô hình cho None Ness, nhưng chỉnếu bạn thử nghiệm trên mô hình nơi mối quan hệ một-một bắt nguồn. Ví dụ, cho hai loại cổ phiếu này ...

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

... để xem nếu một RestaurantPlace, chúng ta có thể sử dụng đoạn mã sau:

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

Để xem một PlaceRestaurant, điều quan trọng là để hiểu rằng việc tham chiếu thuộc tính restaurant đối với trường hợp Place nêu ra trường hợp ngoại lệ Restaurant.DoesNotExist nếu không có nhà hàng tương ứng. Điều này xảy ra bởi vì Django thực hiện tra cứu nội bộ bằng cách sử dụng QuerySet.get(). Ví dụ:

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

Trong kịch bản này, dao cạo Occam của chiếm ưu thế, và cách tiếp cận tốt nhất để thực hiện một quyết tâm về việc có hay không một PlaceRestautrant sẽ là một tiêu chuẩn try/except xây dựng như mô tả here.

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

Trong khi đề nghị joctee để sử dụng hasattr công trình trong thực tế, nó thực sự chỉ hoạt động một cách tình cờ từ hasattr ngăn chặn tất cả trường hợp ngoại lệ (bao gồm DoesNotExist) như trái ngược với chỉ AttributeError s, như nó phải.Như Piet Delport chỉ ra, hành vi này đã thực sự được sửa chữa trong Python 3.2 mỗi vé sau: http://bugs.python.org/issue9666. Hơn nữa - và có nguy cơ sounding khăng khăng - Tôi tin rằng trên try/except xây dựng là đại diện hơn về cách Django hoạt động, trong khi sử dụng hasattr thể đám mây vấn đề đối với người mới, có thể tạo ra FUD và lây lan những thói quen xấu.

11

Tôi thích joctee's answer, bởi vì nó đơn giản như vậy.

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

bình luận khác đã dấy lên lo ngại rằng nó có thể không làm việc với một số phiên bản của Python hoặc Django, nhưng the Django documentation cho thấy kỹ thuật này là một trong những lựa chọn:

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

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

Tất nhiên, tài liệu cũng hiển thị kỹ thuật bắt ngoại lệ:

p2 doesn’t have an associated restaurant:

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

Tôi đồng ý với Joshua rằng bắt ngoại trừ làm cho nó rõ ràng hơn những gì đang xảy ra, nhưng nó chỉ có vẻ hỗn độn với tôi. Có lẽ đây là một sự thỏa hiệp hợp lý?

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

Đây chỉ là truy vấn đối tượng Restaurant theo địa điểm. Nó trả về None nếu địa điểm đó không có nhà hàng.

Dưới đây là một đoạn mã thực thi cho bạn để chơi với các tùy chọn. Nếu bạn đã cài đặt Python, Django và SQLite3, nó sẽ chỉ chạy. Tôi đã thử nghiệm nó với Python 2.7, Python 3.4, Django 1.9.2 và 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() 
Các vấn đề liên quan