Làm thế nào để tôi có được đối tượng nếu nó tồn tại hoặc Không có nếu nó không tồn tại?


222

Khi tôi yêu cầu người quản lý mô hình lấy một đối tượng, nó sẽ tăng lên DoesNotExistkhi không có đối tượng phù hợp.

go = Content.objects.get(name="baby")

Thay vì DoesNotExist, làm thế nào tôi có gothể được Nonethay thế?

Câu trả lời:


332

Không có cách 'xây dựng' để làm điều này. Django sẽ tăng ngoại lệ DoesNotExist mỗi lần. Cách thành ngữ để xử lý vấn đề này trong python là bọc nó trong một thử bắt:

try:
    go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
    go = None

Những gì tôi đã làm là tạo safe_getcác mô hình lớp con. Quản lý, tạo một mã giống như ở trên và sử dụng trình quản lý đó cho các mô hình của tôi. Bằng cách đó bạn có thể viết : SomeModel.objects.safe_get(foo='bar').


7
Sử dụng tốt một sốModel.DoesNotExist thay vì nhập ngoại lệ, quá.
siêu thị

195
Giải pháp này dài bốn dòng. Đối với tôi điều này là quá nhiều. Với django 1.6, bạn có thể sử dụng SomeModel.objects.filter(foo='bar').first()điều này trả về trận đấu đầu tiên hoặc Không có gì. Sẽ không thất bại nếu có một vài trường hợp nhưqueryset.get()
guettli

9
Tôi nghĩ rằng đó là phong cách xấu để lạm dụng các ngoại lệ để xử lý các trường hợp mặc định. Đúng, "xin tha thứ thì dễ hơn xin phép". Nhưng một ngoại lệ vẫn nên được sử dụng, trong mắt tôi, cho các trường hợp ngoại lệ.
Konstantin Schubert

8
Rõ ràng là tốt hơn so với ngầm. Trừ khi có một lý do hiệu suất để sử dụng, filter().first()tôi nghĩ rằng ngoại lệ là cách để đi.
christianbundy

6
Sử dụng First () chỉ là một ý tưởng tốt nếu bạn không quan tâm khi có bội số. Mặt khác, giải pháp này là ưu việt hơn, bởi vì nó vẫn sẽ ném Ngoại lệ nếu bạn bất ngờ tìm thấy nhiều đối tượng, đó là điều bình thường bạn muốn xảy ra trong trường hợp đó.
rossdavidh

181

Vì django 1.6, bạn có thể sử dụng phương thức First () như vậy:

Content.objects.filter(name="baby").first()

26
Trong trường hợp này, không có Lỗi nào được nêu ra nếu có nhiều hơn một trận đấu.
Konstantin Schubert

7
'FeroxTL' bạn cần ghi có @guettli cho câu trả lời này, vì anh ấy đã nhận xét điều này về câu trả lời được chấp nhận một năm trước bài đăng của bạn.
colm.anseo

7
@colminator Tôi muốn nói rằng guettli nên biết rằng một câu trả lời mới không thuộc về một bình luận nếu anh ta muốn nâng cao reputaiton của mình lên trên :) Nhận xét của bạn là đủ tín dụng cho guettli tôi nghĩ và không nên thêm vào câu trả lời nếu đó là đề xuất của bạn.
Joakim

3
@Joakim Tôi không có vấn đề gì với việc đăng một "câu trả lời" mới - chỉ để cung cấp tín dụng khi đến hạn :-)
colm.anseo

3
Điều gì về hiệu suất của phương pháp này so với câu trả lời được chấp nhận?
MaxCore

36

Từ tài liệu django

get()đưa ra một DoesNotExistngoại lệ nếu không tìm thấy một đối tượng cho các tham số đã cho. Ngoại lệ này cũng là một thuộc tính của lớp mô hình. Các DoesNotExist kế thừa ngoại lệ từdjango.core.exceptions.ObjectDoesNotExist

Bạn có thể bắt ngoại lệ và chỉ định Noneđi.

from django.core.exceptions import ObjectDoesNotExist
try:
    go  = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    go = None

33

Bạn có thể tạo một chức năng chung cho việc này.

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

Sử dụng như thế này:

go = get_or_none(Content,name="baby")

go sẽ là Không nếu không có mục nào trùng khớp sẽ trả về mục Nội dung.

Lưu ý: Nó sẽ tăng ngoại lệ ManyObjectsReturned nếu có nhiều mục nhập được trả về cho name = "baby"



14

Để làm cho mọi thứ dễ dàng hơn, đây là một đoạn mã tôi đã viết, dựa trên các đầu vào từ các câu trả lời tuyệt vời ở đây:

class MyManager(models.Manager):

    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except ObjectDoesNotExist:
            return None

Và sau đó trong mô hình của bạn:

class MyModel(models.Model):
    objects = MyManager()

Đó là nó. Bây giờ bạn có MyModel.objects.get () cũng như MyModel.objetcs.get_or_none ()


7
đồng thời, đừng quên nhập: từ django.core.exceptions nhập ObjectDoesNotExist
Moti Radomski

13

bạn có thể sử dụng existsvới bộ lọc:

Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS

chỉ là một thay thế cho nếu bạn chỉ muốn biết nếu nó tồn tại


4
Điều đó sẽ gây ra một cuộc gọi cơ sở dữ liệu bổ sung khi tồn tại. Không phải là một ý tưởng tốt
Christoffer

@Christoffer không chắc chắn tại sao đó sẽ là một cuộc gọi db thêm. Theo các tài liệu :Note: If you only want to determine if at least one result exists (and don’t need the actual objects), it’s more efficient to use exists().
Anupam

2
@Christoffer Tôi nghĩ bạn đúng. Bây giờ tôi đọc lại câu hỏi và OP thực sự muốn trả lại đối tượng thực tế. Vì vậy, exists()sẽ được sử dụng với ifmệnh đề trước khi tìm nạp đối tượng do đó gây ra một cú đúp cho db. Tôi vẫn sẽ giữ bình luận xung quanh trong trường hợp nó giúp được người khác.
Anupam

7

Xử lý các trường hợp ngoại lệ tại các điểm khác nhau trong chế độ xem của bạn có thể thực sự cồng kềnh..Những gì về việc xác định Trình quản lý mô hình tùy chỉnh, trong tệp mô hình, như,

class ContentManager(model.Manager):
    def get_nicely(self, **kwargs):
        try:
            return self.get(kwargs)
        except(KeyError, Content.DoesNotExist):
            return None

và sau đó đưa nó vào lớp Mô hình nội dung

class Content(model.Model):
    ...
    objects = ContentManager()

Bằng cách này, nó có thể dễ dàng được xử lý trong các khung nhìn tức là

post = Content.objects.get_nicely(pk = 1)
if post:
    # Do something
else:
    # This post doesn't exist

1
Tôi thực sự thích giải pháp này, nhưng không thể làm cho nó hoạt động như khi sử dụng python 3.6. Muốn để lại một ghi chú sửa đổi lợi nhuận trong Trình quản lý nội dung return self.get(**kwargs)để làm cho nó hoạt động với tôi. Không nói bất cứ điều gì sai với câu trả lời, chỉ là một mẹo cho bất cứ ai cố gắng sử dụng nó với các phiên bản mới hơn (hoặc với bất cứ điều gì khác khiến nó không hoạt động cho tôi).
skagzilla

7

Đây là một trong những chức năng gây phiền nhiễu mà bạn có thể không muốn thực hiện lại:

from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")

Tôi đã kiểm tra mã của get_object_or_Nonenhưng thấy rằng nó vẫn tăng MultipleObjectsReturnednếu có nhiều hơn một đối tượng. Vì vậy, người dùng có thể xem xét xung quanh với một try-except(mà chính chức năng này đã có try-except).
John Pang

4

Nếu bạn muốn một giải pháp một dòng đơn giản không liên quan đến xử lý ngoại lệ, các câu lệnh có điều kiện hoặc yêu cầu của Django 1.6+, thay vào đó, hãy làm điều này:

x = next(iter(SomeModel.objects.filter(foo='bar')), None)

4

Tôi nghĩ rằng nó không phải là ý tưởng tồi để sử dụng get_object_or_404()

from django.shortcuts import get_object_or_404

def my_view(request):
    my_object = get_object_or_404(MyModel, pk=1)

Ví dụ này tương đương với:

from django.http import Http404

def my_view(request):
    try:
        my_object = MyModel.objects.get(pk=1)
    except MyModel.DoesNotExist:
        raise Http404("No MyModel matches the given query.")

Bạn có thể đọc thêm về get_object_or_404 () trong tài liệu trực tuyến django.


2

Từ django 1.7 trở đi bạn có thể làm như sau:

class MyQuerySet(models.QuerySet):

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


class MyBaseModel(models.Model):

    objects = MyQuerySet.as_manager()


class MyModel(MyBaseModel):
    ...

class AnotherMyModel(MyBaseModel):
    ...

Ưu điểm của "MyQuerySet.as_manager ()" là cả hai điều sau đây sẽ hoạt động:

MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()

1

Đây là một biến thể của hàm trợ giúp cho phép bạn tùy ý vượt qua trong một QuerySettrường hợp, 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 bộ truy vấn allđối tượng của mô hình (ví dụ: từ một tập hợp con của các mục con thuộc về một ví dụ cha mẹ):

def get_unique_or_none(model, queryset=None, **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(**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, **kwargs) như đã thảo luận
  2. obj = get_unique_or_none(Model, parent.children, **kwargs)

1

Không có ngoại lệ:

if SomeModel.objects.filter(foo='bar').exists():
    x = SomeModel.objects.get(foo='bar')
else:
    x = None

Sử dụng một ngoại lệ:

try:
   x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
   x = None

Có một chút tranh cãi về việc khi nào nên sử dụng một ngoại lệ trong python. Một mặt, "xin tha thứ dễ hơn là xin phép". Mặc dù tôi đồng ý với điều này, tôi tin rằng một ngoại lệ nên vẫn còn, ngoại lệ, và "trường hợp lý tưởng" sẽ chạy mà không cần nhấn một.


1

Chúng ta có thể sử dụng ngoại lệ dựng sẵn Django gắn liền với các mô hình có tên là .DoesNotExist. Vì vậy, chúng tôi không phải nhập ObjectDoesNotExistngoại lệ.

Thay vì làm:

from django.core.exceptions import ObjectDoesNotExist

try:
    content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    content = None

Chung ta co thể lam được việc nay:

try:
    content = Content.objects.get(name="baby")
except Content.DoesNotExist:
    content = None

0

Đây là một bản sao từ get_object_or_404 của Django ngoại trừ phương thức trả về Không có. Điều này cực kỳ hữu ích khi chúng ta phải sử dụng only()truy vấn để chỉ truy xuất một số trường nhất định. Phương pháp này có thể chấp nhận một mô hình hoặc một bộ truy vấn.

from django.shortcuts import _get_queryset


def get_object_or_none(klass, *args, **kwargs):
    """
    Use get() to return an object, or return None if object
    does not exist.
    klass may be a Model, Manager, or QuerySet object. All other passed
    arguments and keyword arguments are used in the get() query.
    Like with QuerySet.get(), MultipleObjectsReturned is raised if more than
    one object is found.
    """
    queryset = _get_queryset(klass)
    if not hasattr(queryset, 'get'):
        klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
        raise ValueError(
            "First argument to get_object_or_none() must be a Model, Manager, "
            "or QuerySet, not '%s'." % klass__name
        )
    try:
        return queryset.get(*args, **kwargs)
    except queryset.model.DoesNotExist:
        return None

0

Có lẽ tốt hơn là bạn sử dụng:

User.objects.filter(username=admin_username).exists()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.