Django: DB'den bir nesne alın veya hiçbir şey eşleşmezse 'Yok'


102

Veritabanından bir nesne almama izin verecek herhangi bir Django işlevi var mı, yoksa hiçbir şey eşleşmezse Yok mu?

Şu anda şöyle bir şey kullanıyorum:

foo = Foo.objects.filter(bar=baz)
foo = len(foo) > 0 and foo.get() or None

Ama bu çok net değil ve her yerde olması dağınık.


2
Sadece foo = foo [0] kullanabileceğinizi biliyorsunuz eğer foo else Yok
Visgean Skeloru

2
Python'un üçlü bir operatörü vardır, boole operatörlerini kullanmak zorunda değilsiniz. Ayrıca, len(foo)kötü : " Not: Tek yapmak istediğiniz, kümedeki kayıt sayısını belirlemekse, QuerySets üzerinde len () kullanmayın. SQL'in SELECT COUNT kullanarak bir sayımı veritabanı düzeyinde işlemek çok daha etkilidir. () ve Django tam da bu nedenle bir count () yöntemi sağlar. ". Yeniden Yazılan:foo = foo[0] if foo.exists() else None
mustafa. 0x


1
@ mustafa.0x Bunun burada geçerli olduğunu sanmıyorum. Burada, sayım denetimi başka bir sorgu çalıştırır. Daha sonra gerçek verileri almak için tekrar sorgulama yaparız. Bu, filtrelemenin ve sonra saymanın daha anlamlı olacağı bir durumdur ... ancak filtreleme ve aramadan daha anlamlı değildir first(): P
MicronXD

Yanıtlar:


88

Django 1.6'dafirst() Queryset yöntemini kullanabilirsiniz . Sorgu kümesiyle eşleşen ilk nesneyi veya eşleşen nesne yoksa Yok'u döndürür.

Kullanım:

p = Article.objects.order_by('title', 'pub_date').first()

19
Bu iyi bir cevap değildir, eğer birden fazla nesne bulunursa MultipleObjectsReturned(), burada belgelendiği gibi yükseltilmez . Burada
sleepycal

25
@sleepycal Katılmıyorum. first()Eserler tam olarak varsayalım olarak. Sorgu sonucundaki ilk nesneyi döndürür ve birden çok sonuç bulunup bulunmadığının önemi yoktur. Döndürülen birden fazla nesneyi kontrol etmeniz gerekiyorsa, .get()bunun yerine kullanmalısınız .
Cesar Canassa

7
[düzenlendi] OP'nin dediği gibi, .get()ihtiyaçlarına uygun değil. Bu sorunun doğru cevabı aşağıda @kaapstorm tarafından bulunabilir ve açıkça daha uygun cevaptır. filter()Bu şekilde kötüye kullanmak beklenmedik sonuçlara yol açabilir, OP'nin muhtemelen fark etmediği bir şey (burada bir şeyi
kaçırmıyorsam

1
@sleepycal Bu yaklaşımın istenmeyen sonuçları nelerdir?
HorseloverFat

12
Veritabanı kısıtlamalarınız nesneler arasında benzersizliği doğru bir şekilde yönetmiyorsa, mantığınızın yalnızca tek bir nesne olmasını beklediği (ilk () ile seçilir), ancak gerçekte birden fazla nesnenin var olduğu uç durumlara çarpabilirsiniz. First () yerine get () kullanmak, yükselterek size ekstra bir koruma katmanı sağlar MultipleObjectsReturned(). Döndürülen sonucun birden çok nesne döndürmesi beklenmiyorsa, bu şekilde değerlendirilmemelidir. Burada
bununla

140

Bunu yapmanın iki yolu vardır;

try:
    foo = Foo.objects.get(bar=baz)
except model.DoesNotExist:
    foo = None

Veya bir sarmalayıcı kullanabilirsiniz:

def get_or_none(model, *args, **kwargs):
    try:
        return model.objects.get(*args, **kwargs)
    except model.DoesNotExist:
        return None

Böyle ara

foo = get_or_none(Foo, baz=bar)

8
İşlevsellik için istisnalar kullanması hoşuma gitmiyor - bu çoğu dilde kötü bir uygulama. Python'da bu nasıl?
pcv

18
Python yinelemesi bu şekilde çalışır (bir StopIteration yükseltir) ve bu, dilin temel bir parçasıdır. Ben de dene / hariç işlevinin büyük bir hayranı değilim, ancak Pythonic olarak görülüyor.
Matt Luongo

4
@pcv: "çoğu" dil hakkında güvenilir bir geleneksel bilgelik yoktur. Sanırım kullandığınız belirli bir dil hakkında düşünüyorsunuz, ancak bu tür nicelik belirteçlerine dikkat edin. İstisnalar, Python'daki herhangi bir şey kadar yavaş (veya bu konuda "yeterince hızlı") olabilir. Soruya geri dönersek - queryset1.6'dan önce bunu yapmak için herhangi bir yöntem sağlamamış olmaları çerçevenin bir eksikliğidir ! Ancak bu yapıya gelince - sadece beceriksiz. Bu "kötü" değil çünkü en sevdiğiniz dilin eğitim yazarı öyle dedi. Google'ı deneyin: "izin yerine af dileyin".
Tomasz Gandor

@TomaszGandor: Evet, en sevdiğiniz dil öğretici ne derse desin, sakar ve okuması zor. Bir istisna gördüğümde, standart olmayan bir şey olduğunu varsayıyorum. Okunabilirlik önemlidir. Ama başka makul bir seçenek olmadığında, bunun için gitmen gerektiğini ve kötü bir şey olmayacağını kabul ediyorum.
pcv

@pcv getYöntemin geri dönmesi beklenmez None, bu nedenle bir istisna mantıklıdır. Nesnenin orada olacağından emin olduğunuz / nesnenin orada "olmasının" söz konusu olduğu durumlarda kullanmanız gerekiyor. Ayrıca bunun gibi istisnaların kullanılması 'tamam' olarak kabul edilir çünkü bu mantıklıdır. getFarklı bir sözleşmeye sahip olmak istiyorsunuz , bu yüzden istisnayı yakalayıp bastırıyorsunuz, bu da peşinde olduğunuz değişikliktir.
Dan Passaro

83

Sorki'nin cevabına bazı örnek kod eklemek için (bunu bir yorum olarak eklerdim, ancak bu benim ilk yazım ve yorum bırakmak için yeterli itibarım yok), şöyle bir get_or_none özel yöneticisi uyguladım:

from django.db import models

class GetOrNoneManager(models.Manager):
    """Adds get_or_none method to objects
    """
    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except self.model.DoesNotExist:
            return None

class Person(models.Model):
    name = models.CharField(max_length=255)
    objects = GetOrNoneManager()

Ve şimdi bunu yapabilirim:

bob_or_none = Person.objects.get_or_none(name='Bob')

Bu çok zarif.
NotSimon

13

Ayrıca django'yu sinir bozucu kullanmayı deneyebilirsiniz (başka kullanışlı işlevleri vardır!)

şununla kurun:

pip install django-annoying

from annoying.functions import get_object_or_None
get_object_or_None(Foo, bar=baz)

9

Foo'ya özel yöneticisini verin . Oldukça kolay - sadece kodunuzu özel yöneticide işleve koyun, modelinizde özel yönetici ayarlayın ve çağırın Foo.objects.your_new_func(...).

Genel bir işleve ihtiyacınız varsa (bunu sadece özel yönetici ile değil herhangi bir modelde kullanmak için) kendiniz yazın ve onu python yolunuzda bir yere koyun ve içe aktarın, artık dağınık değil.


4

Bunu bir yönetici veya genel işlev yoluyla yapsanız da, kwarg'larınız birden fazla nesne alırsa get () işlevi bunu artıracağından, TRY deyiminde 'MultipleObjectsReturned' öğesini yakalamak isteyebilirsiniz.

Genel işleve dayalı olarak:

def get_unique_or_none(model, *args, **kwargs):
    try:
        return model.objects.get(*args, **kwargs)
    except (model.DoesNotExist, model.MultipleObjectsReturned), err:
        return None

ve yöneticide:

class GetUniqueOrNoneManager(models.Manager):
    """Adds get_unique_or_none method to objects
    """
    def get_unique_or_none(self, *args, **kwargs):
        try:
            return self.get(*args, **kwargs)
        except (self.model.DoesNotExist, self.model.MultipleObjectsReturned), err:
            return None

Bu, diğer yanıtların en iyi noktalarını yakalıyor gibi görünüyor. Güzel :)
Edward Newell

@emispowder, Ya Hiçbiri döndürmek istemiyorsam, AYNI sayfada görüntülenen "eşleşen veri yok" gibi bir uyarı iletisi döndürmek istiyorum?
Héléna

0

Burada isteğe bağlı olarak bir de geçmesine izin verir yardımcı işlevini varyasyonu olan QuerySetörnek durumda modelin dışında bir sorgu kümesi gelen (varsa) benzersiz nesne almak istiyorum, allnesneler sorgu kümesi (örneğin çocuk öğeleri bir alt kümesi a ait dan üst örnek):

def get_unique_or_none(model, queryset=None, *args, **kwargs):
    """
        Performs the query on the specified `queryset`
        (defaulting to the `all` queryset of the `model`'s default manager)
        and returns the unique object matching the given
        keyword arguments.  Returns `None` if no match is found.
        Throws a `model.MultipleObjectsReturned` exception
        if more than one match is found.
    """
    if queryset is None:
        queryset = model.objects.all()
    try:
        return queryset.get(*args, **kwargs)
    except model.DoesNotExist:
        return None

Bu iki şekilde kullanılabilir, örneğin:

  1. obj = get_unique_or_none(Model, *args, **kwargs) daha önce tartışıldığı gibi
  2. obj = get_unique_or_none(Model, parent.children, *args, **kwargs)

-1

Çoğu durumda şunları kullanabileceğinizi düşünüyorum:

foo, created = Foo.objects.get_or_create(bar=baz)

Sadece Foo tablosuna yeni bir girişin eklenmesi kritik değilse (diğer sütunlar Yok / varsayılan değerlere sahip olacaktır)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.