जांचें कि OneToOneField Django में कोई नहीं है


86

मेरे पास दो मॉडल हैं:

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

लेकिन, उन उपयोगकर्ताओं के लिए जिनके पास टाइप 1 या टाइप 2 प्रोफाइल नहीं है, कोड को क्रियान्वित करना जैसे कि निम्नलिखित त्रुटि उत्पन्न करता है:

Type1Profile matching query does not exist.

मैं किस प्रकार की प्रोफ़ाइल की जांच कर सकता हूं?

धन्यवाद

जवाबों:


93

यह जांचने के लिए कि क्या (OneToOne) संबंध मौजूद है या नहीं, आप hasattrफ़ंक्शन का उपयोग कर सकते हैं :

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

4
इस समाधान के लिए धन्यवाद। दुर्भाग्य से, यह हर समय काम नहीं करता है। मामले में आप select_related()अभी या भविष्य में काम करना चाहते हैं - या शायद यह भी सुनिश्चित करें कि आप अन्य प्रकार के जादू को भी संभालते हैं जो कहीं और हो सकते हैं - आपको परीक्षण को इस if hasattr(object, 'onetoonerevrelattr') and object.onetoonerevrelattr != None
प्रकार आगे बढ़ाना होगा

7
ध्यान दें कि पायथन <3.2, डेटाबेस लुकअप के दौरान होने वाले सभी अपवादों hasattrको निगल जाएगा , और न केवल । यह शायद टूट गया है, और न कि आप क्या चाहते हैं। DoesNotExist
Pi Delport

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

@ डेलार्ट क्या django वर्जन इस्तेमाल कर रहे हैं?
जौक्टी

Django 1.5। लेकिन मैंने अपने विशेष मुद्दे को पूरी तरह से लागू करने के लिए हल किया जो मैं करना चाहता था।
एलेक्सपिरिन

48

यह देखना संभव है कि क्या एक अशक्त वन-टू-वन रिलेशनशिप किसी विशेष मॉडल के लिए शून्य है 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()

... यह देखने के लिए कि क्या कोई 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, यह समझना महत्वपूर्ण है कि संदर्भित महत्वपूर्ण है 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.

इस परिदृश्य में, ओकाम का रेजर प्रबल होता है, और यहां वर्णित के अनुसार एक मानक / निर्माण होगा या नहीं, Placeइसके बारे में एक निर्धारण करने के लिए सबसे अच्छा तरीका हैRestautranttryexcept

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

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

EDIT डॉन किर्कबी का उचित समझौता भी मुझे उचित लगता है।


19

मुझे जोक्टी का जवाब पसंद है , क्योंकि यह बहुत सरल है।

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

अन्य टिप्पणीकारों ने चिंता व्यक्त की है कि यह पायथन या Django के कुछ संस्करणों के साथ काम नहीं कर सकता है, लेकिन Django प्रलेखन इस तकनीक को एक विकल्प के रूप में दिखाता है:

अपवाद कैचिंग की आवश्यकता से बचने के लिए आप हैसट्रा का उपयोग भी कर सकते हैं:

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

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

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

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

यहाँ आप विकल्पों के साथ खेलने के लिए एक निष्पादन योग्य स्निपेट है। यदि आपके पास पायथन, 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()

10

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

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 उदाहरण प्राप्त करने के लिए एक सामान्य फ़ंक्शन के रूप में इसका उपयोग कर सकते हैं, एक उत्पत्ति वर्ग (यहां: आपकी प्रोफ़ाइल कक्षाएं) और संबंधित उदाहरण (यहां: request.user) दिया गया है।


3

का उपयोग करें select_related!

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

2
मुझे पता है कि यह इस तरह से काम करता है, लेकिन क्या वास्तव में प्रलेखित select_related का यह व्यवहार है?
कोस

3
मैं सिर्फ Django 1.9.2 में यह कोशिश की, और यह उठता है RelatedObjectDoesNotExist
डॉन किर्कबी

1

अगर आपके पास मॉडल है

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

और आपको बस किसी भी उपयोगकर्ता के लिए यह जानना आवश्यक है कि UserProfile मौजूद है या नहीं - डेटाबेस बिंदु से सबसे प्रभावी तरीका मौजूद है क्वेरी का उपयोग करने के लिए ।

मौजूदा क्वेरी केवल बूलियन वापस आएगी, बजाय रिवर्स विशेषता पहुंच जैसे hasattr(request.user, 'type1profile')- जो क्वेरी प्राप्त करेगी और पूर्ण ऑब्जेक्ट प्रतिनिधित्व लौटाएगी

ऐसा करने के लिए - आपको उपयोगकर्ता मॉडल में एक संपत्ति जोड़ने की आवश्यकता है

class User(AbstractBaseUser)

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

0

मैं has_attr के संयोजन का उपयोग कर रहा हूं और कोई नहीं:

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

0

स्मार्ट दृष्टिकोणों में से एक कस्टम फ़ील्ड OneToOneOrNoneField को जोड़ना और उसका उपयोग करना होगा [Django के लिए काम करता है = = 1.9]

from django.db.models.fields.related_descriptors import ReverseOneToOneDescriptor
from django.core.exceptions import ObjectDoesNotExist
from django.db import models


class SingleRelatedObjectDescriptorReturnsNone(ReverseOneToOneDescriptor):
    def __get__(self, *args, **kwargs):
        try:
            return super().__get__(*args, **kwargs)
        except ObjectDoesNotExist:
            return None


class OneToOneOrNoneField(models.OneToOneField):
    """A OneToOneField that returns None if the related object doesn't exist"""
    related_accessor_class = SingleRelatedObjectDescriptorReturnsNone

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('null', True)
        kwargs.setdefault('blank', True)
        super().__init__(*args, **kwargs)

कार्यान्वयन

class Restaurant(models.Model):  # The class where the one-to-one originates
    place = OneToOneOrNoneField(Place)
    serves_hot_dogs = models.BooleanField()
    serves_pizza = models.BooleanField()

प्रयोग

r = Restaurant(serves_hot_dogs=True, serves_pizza=False)
r.place  # will return None

django 1.8 के लिए आपको इस तरह का उपयोग SingleRelatedObjectDescriptorकरने की आवश्यकता हैReverseOneToOneDescriptorfrom django.db.models.fields.related import SingleRelatedObjectDescriptor
pymen
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.