2010-08-11 17 views
61

में कोई नहीं है मैं दो मॉडल इस तरह है: मैं अगर उपयोगकर्ता टाइप 1 या Type2 प्रोफ़ाइल है कुछ करने की ज़रूरत हैचेक अगर OneToOneField Django

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


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

:

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. 

मैं कैसे जांच कर सकते हैं टी उपयोगकर्ता के प्रोफाइल का ype है?

धन्यवाद

उत्तर

9

कोशिश/ब्लॉक को छोड़कर कैसे उपयोग करें?

def get_profile_or_none(user, profile_cls): 

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

    return profile 

फिर, इस तरह उपयोग करें! और एक संबंधित उदाहरण (यहाँ: request.user):

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 उदाहरण प्राप्त करने के लिए एक सामान्य समारोह के रूप में इस का उपयोग कर सकता है, एक प्रारंभिक वर्ग (अपने प्रोफ़ाइल कक्षाएं यहाँ) दिया लगता है। (OneToOne) संबंध है या नहीं अगर

60

जाँच के लिए, आपको hasattr फ़ंक्शन का उपयोग कर सकते हैं:

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

इस समाधान के लिए धन्यवाद। दुर्भाग्यवश, यह हर समय काम नहीं करता है। यदि आप अब 'select_related() 'के साथ या भविष्य में काम करना चाहते हैं - या यहां तक ​​कि यह सुनिश्चित करने के लिए भी कि आप अन्य प्रकार के जादू को भी संभाल लेंगे जो आपको कहीं और हो सकता है - आपको निम्नानुसार परीक्षण का विस्तार करना होगा:' अगर हैट्टर (ऑब्जेक्ट, 'onetoonerevrelattr') और object.onetoonerevrelattr! = कोई भी' –

+6

ध्यान दें कि पायथन <3.2 में, हैशर' डेटाबेस * लुकअप के दौरान होने वाले सभी * अपवादों को निगल जाएगा, न केवल 'DoNotExist'। यह शायद टूटा हुआ है, और आप जो चाहते हैं उसे नहीं। –

+0

पायथन 2.7 के साथ काम नहीं कर रहा है। यहां तक ​​कि यदि OneToOne मौजूद नहीं है, तो यह एक django.db.models.fields.related.RelatedManager ऑब्जेक्ट लौटाता है। – alexpirine

3

उपयोग select_related!

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

मुझे पता है कि यह इस तरह काम करता है, लेकिन वास्तव में चयनित_संबंधित इस व्यवहार का व्यवहार है? – Kos

+1

मैंने अभी Django 1.9.2 में यह कोशिश की, और यह 'relatedObjectDoesNotExist' उठाता है। –

33

यह देखने के लिए बस None सत्ता के लिए मॉडल पर संगत फ़ील्ड परीक्षण से अगर एक नल एक-से-एक संबंध एक विशेष मॉडल के लिए रिक्त है संभव है, लेकिन केवल अगर आप मॉडल जहां पर परीक्षण एक-से-एक संबंध उत्पन्न होता है। उदाहरण के लिए, अगर एक Restaurant एक Place को देखने के लिए इन दो वर्गों ...

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

... दिया, हम निम्नलिखित कोड का उपयोग कर सकते हैं: अगर एक Place एक Restaurant है

>>> 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 संपत्ति का संदर्भ देने के लिए यह समझने के लिए कि 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 के रेजर की तस है, और के बारे में क्या है या नहीं एक Place एक निर्धारण करने के लिए सबसे अच्छा तरीका एक Restautrant एक मानक try/except निर्माण के रूप में वर्णित here होगा है।

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

joctee के सुझाव व्यवहार में hasattr काम करता है का उपयोग करने के है, यह वास्तव में केवल दुर्घटना से काम करता है के बाद से hasattr, का विरोध करने के लिए सिर्फ AttributeError रों रूप को दबा (DoesNotExist सहित) सभी अपवाद की तरह होना चाहिए।जैसा कि पिट डेलपोर्ट ने इंगित किया था, इस व्यवहार को वास्तव में निम्नलिखित टिकट प्रति पाइथन 3.2 में सही किया गया था: http://bugs.python.org/issue9666। इसके अलावा - और विचार की आवाज के जोखिम पर - मेरा मानना ​​है कि उपर्युक्त try/except निर्माण hasattr का उपयोग करते समय, Django काम करता है, इसके निर्माण का अधिक प्रतिनिधि है, जो नए लोगों के लिए समस्या को क्लाउड कर सकता है, जो एफयूडी बना सकता है और बुरी आदतों को फैल सकता है।

11

मुझे joctee's answer पसंद है, क्योंकि यह इतना आसान है।

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

अन्य टिप्पणीकर्ताओं चिंताओं को उठाया है कि यह अजगर या Django के कुछ संस्करणों के साथ काम नहीं है, लेकिन the Django documentation विकल्पों में से एक के रूप में इस तकनीक का पता चलता है:

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

>>> hasattr(p2, 'restaurant') 
False 
बेशक

, प्रलेखन अपवाद पकड़ने की तकनीक भी दिखाता है:

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. 

मैं Joshua से सहमत हूं कि अपवाद को पकड़ने से यह स्पष्ट हो जाता है कि क्या हो रहा है, लेकिन यह मेरे लिए बहुत ही गड़बड़ है। शायद यह एक उचित समझौता है?

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

यह सिर्फ Restaurant वस्तुओं को जगह से पूछताछ कर रहा है। यदि उस स्थान पर कोई रेस्तरां नहीं है तो यह None देता है।

विकल्पों के साथ खेलने के लिए यहां एक निष्पादन योग्य स्निपेट है। यदि आपके पास पाइथन, Django, और SQLite3 स्थापित है, तो इसे अभी चलाना चाहिए। मैंने इसे पायथन 2.7, पायथन 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() 
संबंधित मुद्दे