Deepwalker / trafaret

Ultimate transformation library that supports validation, contexts and aiohttp.
http://trafaret.readthedocs.org/en/latest/
BSD 2-Clause "Simplified" License
177 stars 31 forks source link

added is_valid method to check if value is correct without raising error #103

Closed ilyachch closed 4 years ago

asvetlov commented 4 years ago

The PR seems redundant. On the other side, requests has response.ok along with response.raise_for_status(). I personally never used this api but I know people who prefer this way.

ilyachch commented 4 years ago

I faced a problem, that I watned to check, if value is suitable to some trafaret. And if there are only 2 options, it's ok:

t1 = Trafater()
t2 = Trafaret()

try:
    t1.check(value)
    func1(value)
except DataError:
    t2.check(value)
    func2(value)

But if there is more than 3 options, it becomes not so comfortable:

t1 = Trafater()
t2 = Trafaret()
t3 = Trafaret ()

try:
    t1.check(value)
    func1(value)
except DataError:
    try:
        t2.check(value)
        func2(value)
    except DataError:
        t3.check(value)
        func3(value)

I think, it's more obvious to make it such way:

t1 = Trafater()
t2 = Trafaret()
t3 = Trafaret ()

if t1.is_valid(value):
    func1(value)
elif t2.is_valid(value):
    func2(value)
if t3.is_valid(value):
    func3(value)
Deepwalker commented 4 years ago

Aside usability of is_valid shortcut – why not to use trafaret like this?

t1 = Trafater() & func1
t2 = Trafaret() & func2
t3 = Trafaret() & func3

data = (t1 | t2 | t3).check(value)
ilyachch commented 4 years ago

That was only example. In real life there is more than one function call

I'm using it in Django admin get_search_result and i need to filter Queryset based on data i get.

def is_valid(contract: Type[t.Trafaret], value: Any) -> bool:
    try:
        contract.check(value)
        return True
    except t.DataError:
        return False

class SomeAdmin(admin.Admin):
    ...
    def get_search_results(self, request, queryset, search_term):
        if not search_term:
            return queryset, False
        search_term = search_term.lower()

        if is_valid(UuidTrafaret, search_term):
            uuid = UUID(search_term)
            queryset = queryset.filter(uuid=uuid)
        if is_valid(PhoneNumberTrafaret, search_term):
            queryset = queryset.filter(user__phone=search_term)
        elif is_valid(EmailTrafaret, search_term):
            queryset = queryset.filter(user__email=search_term)

        return queryset, False
Deepwalker commented 4 years ago

I, myself, prefer it other way

def queryset_t(name):
    def adjust_queryset(search_term, context=None)
        return context.filter(**{name: search_term})
    return adjust_queryset

search_queryset_t = (
    UuidTrafaret & queryset_t('uuid')
    | PhoneNumberTrafaret & queryset_t('user__phone')
    | EmailTrafaret & queryset_t('user__email')
)

class SomeAdmin(admin.Admin):
    ...
    def get_search_results(self, request, queryset, search_term):
        if not search_term:
            return queryset, False
        search_term = search_term.lower()

        queryset = search_queryset_t(search_term, context=queryset)

        return queryset, False

But I think your way perfectly legit too, so let's merge it. If you will have time, I will be very glad if you will add this method to documentation.