Search code examples
pythontypesdjango-querysettyping

Types & exceptions for a generic function to return an object or raise exception from QuerySet with a single item?


I have a pattern in my project where I need to re-use filtering logic for returning multiple results and a single result (depending on the input and needs of the caller). I'd like to make some generic function that takes a QuerySet as its input and either returns the only item in it or raises an exception like Model.objects.get() does if the number of results is not 1.

def single_result_from_qset(qset: "QuerySet") -> Any:
    """TODO: make this work somehow"""
    if len(qset) > 1:
        raise qset.MultipleObjectsReturned
    try:
        return qset[0]
    except IndexError:  # this is not unreachable, no matter what PyCharm tells you
        raise qset.DoesNotExist

The problem I have with the above code I've tried arises when I need to raise an error. Instead of raising the error I asked it to, I get an AttributeError because neither MultipleObjectsReturned nor DoesNotExist are attributes of a QuerySet. How do I raise these errors from the objects the QuerySet is acting on?

As a bonus question, what is the proper type signature for the function definition?


Solution

  • The better solution is to use .get() from QuerySet rather than implement a new helper function. .get() returns and throws exactly what I was asking the above helper function to do.

    Instead of calling rabbit = single_result_from_qset(search_animals_by_meat("Haßenpferrer)), I can use rabbit = search_animals_by_meat("Haßenpferrer).get().
    search_animals_by_meat(q: str) -> "QuerySet[Animal]" could be a wrapper for something like Animals.objects.filter(meat_name__icontains=q) or a far more complex filtering scheme.