2009-10-02 17 views
87

Có chức năng Django nào sẽ cho phép tôi lấy một biểu mẫu đối tượng cơ sở dữ liệu hoặc Không nếu không có gì khớp?Django: Nhận dạng đối tượng DB hoặc 'Không' nếu không có gì khớp với

Ngay bây giờ tôi đang sử dụng một cái gì đó như:

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

Nhưng đó không phải là rất rõ ràng, và nó lộn xộn có ở khắp mọi nơi.

+2

Bạn biết bạn chỉ có thể sử dụng foo = foo [0] nếu foo khác Không –

+2

Python có toán tử bậc ba, bạn không phải sử dụng toán tử boolean. Ngoài ra, ['len (foo)' là xấu] (https://docs.djangoproject.com/en/dev/ref/models/querysets/): "* Lưu ý: Không sử dụng len() trên QuerySets nếu tất cả Việc bạn muốn làm là xác định số lượng bản ghi trong tập hợp. Việc xử lý đếm ở cấp cơ sở dữ liệu hiệu quả hơn nhiều, sử dụng SELECT COUNT() và Django cung cấp phương thức count() vì lý do chính xác này. * ". Viết lại: 'foo = foo [0] nếu foo.exists() else None ' –

+1

có thể trùng lặp của [Trong Django, làm thế nào để object.get, nhưng trả về None khi không có gì được tìm thấy?] (Http://stackoverflow.com/question/3090302/in-django-how-do-i-đối tượng-get-nhưng-return-none-khi-không-gì-là-tìm thấy) – sleepycal

Trả lời

78

Trong Django 1,6 bạn có thể sử dụng phương pháp first() QuerySet. Nó trả về đối tượng đầu tiên được kết hợp bởi queryset, hoặc None nếu không có đối tượng phù hợp.

Cách sử dụng:

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

Đây không phải là một câu trả lời hay, nếu nhiều đối tượng được tìm thấy thì 'MultipleObjectsReturned()' không được nêu lên, như được ghi lại [ở đây] (https://docs.djangoproject.com/en/1.2/ref/exceptions/#multipleobjectsreturned) . Bạn có thể tìm thấy một số câu trả lời hay hơn [ở đây] (http://stackoverflow.com/questions/3090302/in-django-how-do-i-objects-get-but-return-none-when-nothing-is-found) – sleepycal

+21

@sleepycal Tôi không đồng ý. 'First()' hoạt động chính xác như nó giả định. Nó trả về đối tượng đầu tiên trong kết quả truy vấn và không quan tâm nếu tìm thấy nhiều kết quả. Nếu bạn cần kiểm tra nhiều đối tượng, bạn nên sử dụng '.get()' để thay thế. –

+6

[sửa] Như OP đã nói, '.get()' không phù hợp với nhu cầu của anh ta. Câu trả lời chính xác cho câu hỏi này có thể được tìm thấy bên dưới bởi @kaapstorm và rõ ràng là câu trả lời phù hợp hơn. Lạm dụng 'filter()' theo cách này có thể dẫn đến những hậu quả bất ngờ, cái gì mà OP có thể không nhận ra (trừ khi tôi bỏ sót điều gì đó ở đây) – sleepycal

129

Có hai cách để thực hiện việc này;

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

Hoặc bạn có thể sử dụng một wrapper:

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

Gọi nó như thế này

foo = get_or_none(Foo, baz=bar) 
+6

Tôi không giống như thực tế là nó sử dụng ngoại lệ cho chức năng - đây là một thực hành không tốt trong hầu hết các ngôn ngữ. Làm thế nào là trong python? – pcv

+14

Đó là cách Python lặp lại hoạt động (tăng StopIteration), và đó là một phần cốt lõi của ngôn ngữ. Tôi không phải là một fan hâm mộ lớn của chức năng thử/ngoại trừ, nhưng nó dường như được coi là Pythonic. –

+3

@pcv: không có sự khôn ngoan thông thường đáng tin cậy về các ngôn ngữ "hầu hết". Tôi cho rằng bạn nghĩ về một số ngôn ngữ cụ thể mà bạn tình cờ sử dụng, nhưng hãy cẩn thận với các định lượng đó. Các ngoại lệ có thể chậm (hoặc "đủ nhanh" cho vấn đề đó), như bất kỳ thứ gì trong Python. Quay trở lại câu hỏi - đó là một thiếu sót của khuôn khổ, rằng họ đã không cung cấp bất kỳ phương thức 'queryset' nào để làm điều này trước 1.6! Nhưng đối với cấu trúc này - nó chỉ là vụng về. Nó không phải là "ác" bởi vì một số tác giả hướng dẫn của ngôn ngữ yêu thích của bạn nói như vậy. Hãy thử google: "yêu cầu sự tha thứ chứ không phải là sự cho phép". –

9

Cho Foo its custom manager. Nó khá dễ dàng - chỉ cần đặt mã của bạn vào hàm trong trình quản lý tùy chỉnh, đặt trình quản lý tùy chỉnh trong mô hình của bạn và gọi nó bằng Foo.objects.your_new_func(...).

Nếu bạn cần chức năng chung (để sử dụng nó trên bất kỳ mô hình nào không chỉ với trình quản lý tùy chỉnh) hãy viết của riêng bạn và đặt nó ở đâu đó trên đường dẫn và nhập của bạn, không lộn xộn nữa.

77

Để thêm một số mã mẫu vào câu trả lời của sorki (tôi sẽ thêm nhận xét này làm nhận xét, nhưng đây là bài đăng đầu tiên của tôi và tôi không có đủ nhận xét), tôi đã triển khai trình quản lý tùy chỉnh get_or_none :

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

Và bây giờ tôi có thể làm điều này:

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

Ah, hay. Cảm ơn. –

+0

Xin chào, điều này rất hay. – Glycerine

+4

Đây phải là câu trả lời chính xác/được chấp nhận. – sleepycal

4

cho dù làm việc đó thông qua một người quản lý hoặc chức năng chung chung, bạn cũng có thể muốn bắt 'MultipleObjectsReturned' trong báo cáo TRY, như get () chức năng sẽ nâng cao điều này nếu kwarg của bạn lấy nhiều hơn một đối tượng.

xây dựng trên một hàm tổng quát:

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

và trong bộ quản lý:

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 
+0

Điều này dường như nắm bắt tất cả các điểm tốt nhất của các câu trả lời khác. Nice one :) –

+0

@emispowder, nếu tôi không muốn trả lại Không, tôi muốn trả lại một thông báo cảnh báo như "không có dữ liệu phù hợp" nào hiển thị trên trang CÙNG? –

13

Bạn cũng có thể cố gắng sử dụng django gây phiền nhiễu (nó có một chức năng hữu ích!)

cài đặt nó với:

pip install django-annoying 

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

https://github.com/skorokithakis/django-annoying –

0

Dưới đây là một biến thể của helper chức năng cho phép bạn tùy chọn vượt qua trong một trường hợp QuerySet, trong trường hợp bạn muốn lấy đối tượng duy nhất (nếu có) từ một bộ truy vấn khác với mô hình all obj của mô hình ect queryset (ví dụ:từ một tập hợp con của mục con thuộc một trường hợp mẹ):

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 

Điều này có thể được sử dụng theo hai cách, ví dụ:

  1. obj = get_unique_or_none(Model, *args, **kwargs) như previosuly thảo luận
  2. obj = get_unique_or_none(Model, parent.children, *args, **kwargs)
+0

Tôi thấy rằng 'django annoying' đã xử lý trường hợp này. – Gary

-1

Tôi nghĩ rằng trong hầu hết các trường hợp, bạn chỉ có thể sử dụng:

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

Chỉ khi nó không phải là quan trọng mà một mục mới sẽ được thêm vào trong bảng Foo (cột khác sẽ có giá trị None/default)

Các vấn đề liên quan