Agreed about concatenation functions calls. It would be easy to achieve
once filter will accept object-based lookups (previously we discussed
expression objects).
I think it would be even better to accept objects of expression builder
instead (versus expressions directly) - it will separate builder interface
from combinable expressions:

# interface

>>> class ExpressionBuilder:

...    def get_expression(self):

...         pass


# calling get_expression function of the instance below

# should raise error as right hand expression is not created yet

>>> Builder.user.first_name.lower()

<ExpressionBuilder object at 0x7f3605145080>


*# calling get_esxpression method of the instance below*

*# will return a full expression tree *

>>> Builder.user.first_name.lower().collate('fi') == 'anssi'

<ExpressionBuilder object at 0x7f3604f96400>


# query itself

qs = MyModel.objects.filter(ThirdParty.user.first_name.collate('fi').lower()
== 'anssi')


It's not a huge difference, but will make it easier to extend. Do you think
it's worth to put as DEP?

Regards,
Alexey


On Wed, Oct 12, 2016 at 3:41 PM, Anssi Kääriäinen <akaar...@gmail.com>
wrote:

> +1 from me, too.
>
> I still think we should be able to get into a point where you can run
> queries like:
>     Q.user.firstname.collate('fi').lower() == 'anssi'
>
> So, not only can you call regular transforms (like lower), but also
> transforms that take arguments. Other use cases are for example substring:
>     Q.user.firstname.substring(from=5) == 'akaariai'
>
> And of course, this would be super useful for things like drilling into
> JSON data.
>
>  - Anssi
>
> On Friday, October 7, 2016 at 6:20:29 PM UTC+3, Robert Roskam wrote:
>>
>> +1 from me. I really like this approach to help making my queries more
>> DRY.
>>
>> I also like how lightweight the library is altogether. Well done!
>>
>>
>> For those looking for a direct link, here you go:
>> https://github.com/Nepherhotep/django-orm-sugar
>>
>>
>>
>> On Thursday, October 6, 2016 at 1:04:56 PM UTC-4, Alexey Zankevich wrote:
>>>
>>> Hey all,
>>>
>>> Just want to announce recent changes in Django ORM Sugar library, which
>>> might be useful in future Django query syntax enhancement (if ever happens).
>>> 1. Now it supports indexes and slices:
>>>
>>> >>> Q.data.owner.other_pets[0].name='Fishy'
>>> Q(data__owner__other_pets__0__name='Fishy')
>>> >>> Q.tags[0] == 'thoughts'
>>> Q(tags__0='thoughts')
>>> >>> Q.tags[0:2].contains(['thoughts'])
>>> Q(tags__0_2__contains=['thoughts'])
>>>
>>>
>>> 2. Previously, it was only possible to call defined method (like it is
>>> done for *is_not_null()* function) or registered with decorator. Now if
>>> you try to call unknown function of sugar Q, it will internally pass
>>> function name and append it as *__functionname *to lookup field:
>>>
>>> >>> Q.user.username.contains('Rodriguez')
>>> Q(user__username__contains='Rodriguez')
>>>
>>>
>>> There is no such function "contains" anymore in sugar Q, however, it
>>> just adds *__contains* to lookup field and passes parameter to it.
>>>
>>> 3. It will pass a tuple to lookup if multiple arguments passed:
>>>
>>> >>> Q.user.create_datetime.range(d1, d2)
>>> Q(user__create_datetime__range=(d1, d2))
>>>
>>>
>>> I think the library is at the final state now and isn't going to get new
>>> substantial features, but responses are highly appreciated.
>>>
>>> Regards,
>>> Alexey
>>>
>>>
>>> On Sunday, August 16, 2015 at 4:18:26 PM UTC+3, Alexey Zankevich wrote:
>>>>
>>>> Hi all,
>>>>
>>>> This topic is related to the current ORM query syntax with underscores.
>>>> There are lots of arguing related to it, anyway it has pros and cons.
>>>>
>>>> Let's take a concrete example of querying a model:
>>>>
>>>> >>> GameSession.objects.filter(user__profile__last_login_date__g
>>>> te=yesterday)
>>>>
>>>>
>>>> Pros:
>>>>
>>>> 1. The syntax is easy to understand
>>>> 2. Can be extended with custom transforms and lookups
>>>>
>>>> However, there are several cons:
>>>>
>>>> 1. Long strings is hard to read, especially if we have fields with
>>>> underscores.
>>>> It's really easy to make a mistake by missing one:
>>>>
>>>> >>> GameSession.objects.filter(user_profile__last_login_date__gt
>>>> e=yesterday)
>>>>
>>>> Not easy to catch missing underscore between user and profile, is it?
>>>> Even
>>>> though, it's not easy to say whether it should be "user_profile"
>>>> attribute or
>>>> user.profile foreign key.
>>>>
>>>> 2. Query strings can't be reused, thus the approach violates DRY
>>>> principle.
>>>> For example, we need to order results by last_login_date:
>>>>
>>>> >>> GameSession.objects.filter(user__profile__last_login_date__gte=yesterday)
>>>> \
>>>> .order_by('user__profile__last_login_date')
>>>>
>>>> We can't keep user__profile_login_date as a variable as in the first
>>>> part of the
>>>> expression we use a keyword argument, meanwhile in the second part -
>>>> just a
>>>> string. And thus we just have to type query path twice.
>>>>
>>>> 3. Lookup names not natural to Python language and require to be
>>>> remembered or
>>>> looked up in documentation. For example, "__gte" or "__lte" lookups
>>>> tend to be
>>>> confused with "ge" and "le" due to similarity to methods "__ge__" and
>>>> "__le__".
>>>>
>>>> 4. Lookup keywords limited to a single argument only, very inconvenient
>>>> when
>>>> necessary to filter objects by range.
>>>>
>>>> I was thinking a lot trying to solve those issues, keeping in mind
>>>> Django
>>>> approaches. Finally I came up with solution to extend Q objects with dot
>>>> expression syntax:
>>>>
>>>> >>> GameSession.objecs.filter(Q.user.profile.last_login_date >=
>>>> yesterday)
>>>>
>>>> Q is a factory instance for old-style Q objects. Accessing attribute by
>>>> dot
>>>> returns a child factory, calling factory will instantiate old-style Q
>>>> object.
>>>>
>>>> >>> Q
>>>> <QFactory object at 0x7f407298ee10>
>>>>
>>>> >>> Q.user.profile
>>>> <QFactory object at 0x7f40765da310>
>>>>
>>>> >>> Q(user__name='Bob')
>>>> <Q: (AND: ('user__name', 'Bob'))>
>>>>
>>>> It overrides operators, so comparing factory with value returns a
>>>> related Q
>>>> object:
>>>>
>>>> >>> Q.user.name == 'Bob'
>>>> <Q: (AND: ('user__name', 'Bob'))>
>>>>
>>>> Factory has several helper functions for lookups which aren't related
>>>> to any
>>>> Python operators directly:
>>>>
>>>> >>> Q.user.name.icontains('Bob')
>>>> <Q: (AND: ('user__name__icontains', 'Bob'))>
>>>>
>>>> And helper to get query path as string, which requred by order_by or
>>>> select_related queryset methods:
>>>>
>>>> >>> Q.user.profile.last_login_date.get_path()
>>>> 'user__profile__last_login_date'
>>>>
>>>> You can check implementation and more examples here
>>>> https://github.com/Nepherhotep/django-orm-sugar
>>>>
>>>> How it solves issues:
>>>>
>>>> #1. Dots hard to confuse with underscores
>>>> #2. Query paths can be reused:
>>>>
>>>> >>> factory = Q.user.profile.last_login_date
>>>> >>> query = GameSession.objects.filter(factory >= yesterday)
>>>> >>> query = query.order_by(factory.get_path())
>>>>
>>>> #3. Not neccessary to remember most of lookup names and use comparison
>>>> operators
>>>> instead.
>>>> #4. Possible to use multiple keyword arguments:
>>>>
>>>> >>> Q.user.profile.last_login_date.in_range(from_date, to_date)
>>>> <Q: (AND: ('user__profile__last_login_date__lte', from_date),
>>>> ('user__profile__last_login_date__gte', to_date))>
>>>>
>>>>
>>>> This approach looked the best for me due to several reasons:
>>>>
>>>> 1. It's explicit - it doesn't do anything but generating appropriate Q
>>>> object.
>>>> The result of comparison can be saved as Q object variable.
>>>>
>>>> 2. It's short - variants with using model for that will look much
>>>> longer, when
>>>> joining two or more filters:
>>>>
>>>> >>> GameSession.objects.user.profile_last_login_date >= yesterday  #
>>>> awkward
>>>>
>>>> 3. Implementation will not require to change querset manager or model
>>>> classes
>>>>
>>>> 4. Will still allow to use filters and Q class in the old way:
>>>>
>>>> >>> q = Q(user__profile__last_login_date__gte=yesterday)
>>>>
>>>> or
>>>>
>>>> >>> GameSession.objects.filter(user__profile__last_login_date__g
>>>> te=yesterday)
>>>>
>>>> I'd like to make it as a part of Django ORM syntax and it will not be
>>>> hard to
>>>> do, especially taking into account the library is already done and
>>>> working.
>>>> Anyway, I need your thought about the idea in general, as well as about
>>>> particular things like chosen method names - "get_path", "in_range" and
>>>> etc.
>>>> As next step I can create a ticket in the issue tracker, or prepare DEP
>>>> first.
>>>> In latter case I need to find a shepherd to work with.
>>>>
>>>> Best regards,
>>>> Alexey
>>>>
>>> --
> You received this message because you are subscribed to a topic in the
> Google Groups "Django developers (Contributions to Django itself)" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/to
> pic/django-developers/W0OYXhavY68/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> django-developers+unsubscr...@googlegroups.com.
> To post to this group, send email to django-developers@googlegroups.com.
> Visit this group at https://groups.google.com/group/django-developers.
> To view this discussion on the web visit https://groups.google.com/d/ms
> gid/django-developers/b0e30b7f-524e-4cbf-9bba-37cbb8425edc%4
> 0googlegroups.com
> <https://groups.google.com/d/msgid/django-developers/b0e30b7f-524e-4cbf-9bba-37cbb8425edc%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/CAJuVRL2Wk8wFHOpzpDUfTaxbHgVGPEHDmZ2cVLiuCiXj341Ryw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to