Re: Proposal: relationships based on arbitrary predicates

2018-11-05 Thread charettes
Hello Alex,

While investigating an issue with reverse known related objects 
assignment[0] I stumbled
upon a little something that might interest you.

It looks like there's currently some tested cases of abusing the 
ForeignObject interface
to achieve something similar to what you are suggesting here[1][2].

The contenttypes.GenericRelation object happens to implement a similar 
interface
while it's actually a many-to-many field. I assume it's not setting 
many_to_many=True
because a ton of internal Django checks duck-type this attribute to figure 
whether or
not "through" exists but it should really be marked this way.

I'd say giving a shot at reimplementing GenericRelation on top of your 
suggested
Relationship object would be a good way to test whether or not it's 
flexible enough
and be a strong argument for inclusion in Django core to get rid of the 
current hacks
GenericRelation currently employs.

Simon

[0] https://code.djangoproject.com/ticket/29908
[1] 
https://github.com/django/django/blob/ecac6d7a2a510cb0a5d772deeca633c99c9687e5/tests/foreign_object/models/empty_join.py#L22-L84
[2] 
https://github.com/django/django/blob/ecac6d7a2a510cb0a5d772deeca633c99c9687e5/tests/foreign_object/models/empty_join.py#L98-L103

Le mardi 18 septembre 2018 21:43:56 UTC-4, Alex Hill a écrit :
>
> Hi Silvio,
>
> Thanks for your feedback. David Sanders brought up something similar to 
> ComputedField on GitHub[0] - a ForwardedField which can traverse 
> relationships and present a field from a remote instance as if it were a 
> local field. As long as it can traverse relationships, that use case would 
> be covered by ComputedField, so that's another tick for that idea.
>
> I think these two features are quite distinct - except that they may both 
> require a similar change to the ORM to allow traversing relations in the 
> referred-to fields. One limitation of relativity now is that you can only 
> form conditions with local fields of your models. I'd like to be able to 
> traverse relationships in those conditions.
>
> Anybody who's interested - please try out relativity and see if it works 
> for you. The mptt and treebeard helpers are a good place to start :)
>
> Simon - any further thoughts on this before I start working up a patch?
>
> Thanks,
> Alex
>
> [0] 
> https://github.com/AlexHill/django-relativity/pull/1#issuecomment-418239406
>
> On Mon, 10 Sep 2018 at 4:59 am, Silvio > 
> wrote:
>
>> Alex,
>>
>> This is a very useful pattern, that like many others, I've also 
>> implemented in an ad-hoc fashion using a ton of undocumented internal APIs. 
>> So I fully agree standardizing it would be great. Something similar is:
>>
>> https://groups.google.com/forum/#!topic/django-developers/ADSuUUuZp3Q
>>
>> Essentially, I've ended up with the need for:
>>
>> ComputedField
>>
>> and
>>
>> ComputedRelationship
>>
>> where both have all of the niceties that regular fields and foreign 
>> relationships have.
>>
>> So I'd love to see this in Django.
>>
>> -
>> Silvio
>>
>>
>> On Sunday, September 2, 2018 at 10:55:58 PM UTC-4, Alex Hill wrote:
>>>
>>> Hi Simon,
>>>
>>> Thanks for looking at this and for providing some context - I had looked 
>>> at FilteredRelation but I hadn't seen reverse-unique. It makes me more 
>>> confident that this is a good direction to take. I've reimplemented 
>>> ReverseUnique using Relationship [0] and the tests pass, with the only code 
>>> carried over that for discovery of the FK link.
>>>
>>> > I'm not totally sold on the API but having an analog of what 
>>> ForeignObject is to ForeignKey for ManyToManyField would definitely be 
>>> useful.
>>>
>>> I'm not tied to the API, but I think passing a Q as a predicate makes 
>>> sense especially given that it's what both FilteredRelation and 
>>> ReverseUnique do. The core of the idea is that we can express a 
>>> relationship as a combination of predicate and arity. In practise I don't 
>>> think this would be used all that much by users directly - more by 
>>> third-party apps like mptt, and perhaps Django internally.
>>>
>>> > From what I can see in relativity.fields[0] most of the additional 
>>> logic revolves around the extra filtering capabilites through Restriction.
>>>
>>> Yeah that's what it boils down to. We return no columns to join against, 
>>> and return a compilable Restriction from get_extra_restriction to provide 
>>> all the ON conditions. The rest of it is making the descriptors, rels, 
>>> prefetch, etc work.
>>>
>>> > Do you have an idea of what the fields.related inheritance chain would 
>>> look like if it was part of core?
>>>
>>> The least intrusive, and probably a good starting point, would be to 
>>> introduce Relationship alongside the other relation fields as a standalone 
>>> feature, modifying the ORM to allow the implementation to be less hacky. It 
>>> would remain a subclass of ForeignObject (or perhaps RelatedField - I'll 
>>> give that a try).
>>>
>>> In the future there's potential for a nice refactor o

Re: Proposal: relationships based on arbitrary predicates

2018-09-18 Thread Alexander Hill
Hi Silvio,

Thanks for your feedback. David Sanders brought up something similar to
ComputedField on GitHub[0] - a ForwardedField which can traverse
relationships and present a field from a remote instance as if it were a
local field. As long as it can traverse relationships, that use case would
be covered by ComputedField, so that's another tick for that idea.

I think these two features are quite distinct - except that they may both
require a similar change to the ORM to allow traversing relations in the
referred-to fields. One limitation of relativity now is that you can only
form conditions with local fields of your models. I'd like to be able to
traverse relationships in those conditions.

Anybody who's interested - please try out relativity and see if it works
for you. The mptt and treebeard helpers are a good place to start :)

Simon - any further thoughts on this before I start working up a patch?

Thanks,
Alex

[0]
https://github.com/AlexHill/django-relativity/pull/1#issuecomment-418239406

On Mon, 10 Sep 2018 at 4:59 am, Silvio  wrote:

> Alex,
>
> This is a very useful pattern, that like many others, I've also
> implemented in an ad-hoc fashion using a ton of undocumented internal APIs.
> So I fully agree standardizing it would be great. Something similar is:
>
> https://groups.google.com/forum/#!topic/django-developers/ADSuUUuZp3Q
>
> Essentially, I've ended up with the need for:
>
> ComputedField
>
> and
>
> ComputedRelationship
>
> where both have all of the niceties that regular fields and foreign
> relationships have.
>
> So I'd love to see this in Django.
>
> -
> Silvio
>
>
> On Sunday, September 2, 2018 at 10:55:58 PM UTC-4, Alex Hill wrote:
>>
>> Hi Simon,
>>
>> Thanks for looking at this and for providing some context - I had looked
>> at FilteredRelation but I hadn't seen reverse-unique. It makes me more
>> confident that this is a good direction to take. I've reimplemented
>> ReverseUnique using Relationship [0] and the tests pass, with the only code
>> carried over that for discovery of the FK link.
>>
>> > I'm not totally sold on the API but having an analog of what
>> ForeignObject is to ForeignKey for ManyToManyField would definitely be
>> useful.
>>
>> I'm not tied to the API, but I think passing a Q as a predicate makes
>> sense especially given that it's what both FilteredRelation and
>> ReverseUnique do. The core of the idea is that we can express a
>> relationship as a combination of predicate and arity. In practise I don't
>> think this would be used all that much by users directly - more by
>> third-party apps like mptt, and perhaps Django internally.
>>
>> > From what I can see in relativity.fields[0] most of the additional
>> logic revolves around the extra filtering capabilites through Restriction.
>>
>> Yeah that's what it boils down to. We return no columns to join against,
>> and return a compilable Restriction from get_extra_restriction to provide
>> all the ON conditions. The rest of it is making the descriptors, rels,
>> prefetch, etc work.
>>
>> > Do you have an idea of what the fields.related inheritance chain would
>> look like if it was part of core?
>>
>> The least intrusive, and probably a good starting point, would be to
>> introduce Relationship alongside the other relation fields as a standalone
>> feature, modifying the ORM to allow the implementation to be less hacky. It
>> would remain a subclass of ForeignObject (or perhaps RelatedField - I'll
>> give that a try).
>>
>> In the future there's potential for a nice refactor of the ORM to
>> generalise join conditions from key-equality to arbitrary predicates of
>> which key equality is just one case, at which point Relationship could sit
>> comfortably as a base class of all the other relations. The assumption that
>> join==key-equality is pervasive and I think that's an unnecessarily large
>> chunk of work to take on at this point - it would be better to get the
>> feature in, then have a release cycle or so to think about the best way to
>> approach that problem and if we even want to.
>>
>> I would be happy to write up a DEP expanding on an implementation plan
>> and potential future work.
>>
>> Thanks,
>> Alex
>>
>> [0]
>> https://github.com/AlexHill/django-reverse-unique/blob/624c8b19406a40b8e1a2c969c23a6b45bed5a896/reverse_unique/fields.py
>>
>>
>>
>>
>>
>>
>> On Fri, 31 Aug 2018 at 12:12 am, charettes  wrote:
>>
> Hello Alex!
>>>
>>> Thanks for your work on this project, this is definitely something that
>>> I believe would be useful in Django's core based on the number of times I
>>> implemented a filtered queryset getter on Models.
>>>
>>> I'm not totally sold on the API but having an analog of what
>>> ForeignObject is to ForeignKey for ManyToManyField would definitely be
>>> useful.
>>>
>>> From what I can see in relativity.fields[0] most of the additional logic
>>> revolves around the extra filtering capabilites through Restriction.
>>>
>>> Do you have an idea of what the fields.related inheri

Re: Proposal: relationships based on arbitrary predicates

2018-09-09 Thread Silvio
Alex,

This is a very useful pattern, that like many others, I've also implemented 
in an ad-hoc fashion using a ton of undocumented internal APIs. So I fully 
agree standardizing it would be great. Something similar is:

https://groups.google.com/forum/#!topic/django-developers/ADSuUUuZp3Q

Essentially, I've ended up with the need for:

ComputedField

and

ComputedRelationship

where both have all of the niceties that regular fields and foreign 
relationships have.

So I'd love to see this in Django.

-
Silvio


On Sunday, September 2, 2018 at 10:55:58 PM UTC-4, Alex Hill wrote:
>
> Hi Simon,
>
> Thanks for looking at this and for providing some context - I had looked 
> at FilteredRelation but I hadn't seen reverse-unique. It makes me more 
> confident that this is a good direction to take. I've reimplemented 
> ReverseUnique using Relationship [0] and the tests pass, with the only code 
> carried over that for discovery of the FK link.
>
> > I'm not totally sold on the API but having an analog of what 
> ForeignObject is to ForeignKey for ManyToManyField would definitely be 
> useful.
>
> I'm not tied to the API, but I think passing a Q as a predicate makes 
> sense especially given that it's what both FilteredRelation and 
> ReverseUnique do. The core of the idea is that we can express a 
> relationship as a combination of predicate and arity. In practise I don't 
> think this would be used all that much by users directly - more by 
> third-party apps like mptt, and perhaps Django internally.
>
> > From what I can see in relativity.fields[0] most of the additional logic 
> revolves around the extra filtering capabilites through Restriction.
>
> Yeah that's what it boils down to. We return no columns to join against, 
> and return a compilable Restriction from get_extra_restriction to provide 
> all the ON conditions. The rest of it is making the descriptors, rels, 
> prefetch, etc work.
>
> > Do you have an idea of what the fields.related inheritance chain would 
> look like if it was part of core?
>
> The least intrusive, and probably a good starting point, would be to 
> introduce Relationship alongside the other relation fields as a standalone 
> feature, modifying the ORM to allow the implementation to be less hacky. It 
> would remain a subclass of ForeignObject (or perhaps RelatedField - I'll 
> give that a try).
>
> In the future there's potential for a nice refactor of the ORM to 
> generalise join conditions from key-equality to arbitrary predicates of 
> which key equality is just one case, at which point Relationship could sit 
> comfortably as a base class of all the other relations. The assumption that 
> join==key-equality is pervasive and I think that's an unnecessarily large 
> chunk of work to take on at this point - it would be better to get the 
> feature in, then have a release cycle or so to think about the best way to 
> approach that problem and if we even want to.
>
> I would be happy to write up a DEP expanding on an implementation plan and 
> potential future work.
>
> Thanks,
> Alex
>
> [0] 
> https://github.com/AlexHill/django-reverse-unique/blob/624c8b19406a40b8e1a2c969c23a6b45bed5a896/reverse_unique/fields.py
>
>
>
>
>
>
> On Fri, 31 Aug 2018 at 12:12 am, charettes  > wrote:
>
>> Hello Alex!
>>
>> Thanks for your work on this project, this is definitely something that I 
>> believe would be useful in Django's core based on the number of times I 
>> implemented a filtered queryset getter on Models.
>>
>> I'm not totally sold on the API but having an analog of what 
>> ForeignObject is to ForeignKey for ManyToManyField would definitely be 
>> useful.
>>
>> From what I can see in relativity.fields[0] most of the additional logic 
>> revolves around the extra filtering capabilites through Restriction.
>>
>> Do you have an idea of what the fields.related inheritance chain would 
>> look like if it was part of core? I feel like having 
>> Relation(RelatedField), ForeignObject(Relation), ManyToManyField(Relation) 
>> and adding the filtering logic to Relation could work but I'd be interested 
>> to hear what you think here. FWIW Anssi implemented something similar[1] 
>> for reverse unique relationship before FilteredRelation() was introduced.
>>
>> In a sense Relation would be form of virtual field like ForeignObject 
>> since it's not in charge of any database field handling.
>>
>> Simon
>>
>> [0] 
>> https://github.com/AlexHill/django-relativity/blob/3802608c64e86c62ab6268efc37a3f5ca8539221/relativity/fields.py
>> [1] https://github.com/akaariai/django-reverse-unique
>>
>>
>>
>> Le jeudi 30 août 2018 11:38:16 UTC-4, Alex Hill a écrit :
>>>
>>> Hi all,
>>>
>>> I've run into many situations during my time using Django where I've 
>>> wanted to be able to express relations based on some other criteria than 
>>> foreign key equality. A few examples:
>>> - descendants or children of a node in a tree structure
>>> - saved search terms to search results
>>> - a model containing a dat

Re: Proposal: relationships based on arbitrary predicates

2018-09-02 Thread Alexander Hill
Hi Simon,

Thanks for looking at this and for providing some context - I had looked at
FilteredRelation but I hadn't seen reverse-unique. It makes me more
confident that this is a good direction to take. I've reimplemented
ReverseUnique using Relationship [0] and the tests pass, with the only code
carried over that for discovery of the FK link.

> I'm not totally sold on the API but having an analog of what
ForeignObject is to ForeignKey for ManyToManyField would definitely be
useful.

I'm not tied to the API, but I think passing a Q as a predicate makes sense
especially given that it's what both FilteredRelation and ReverseUnique do.
The core of the idea is that we can express a relationship as a combination
of predicate and arity. In practise I don't think this would be used all
that much by users directly - more by third-party apps like mptt, and
perhaps Django internally.

> From what I can see in relativity.fields[0] most of the additional logic
revolves around the extra filtering capabilites through Restriction.

Yeah that's what it boils down to. We return no columns to join against,
and return a compilable Restriction from get_extra_restriction to provide
all the ON conditions. The rest of it is making the descriptors, rels,
prefetch, etc work.

> Do you have an idea of what the fields.related inheritance chain would
look like if it was part of core?

The least intrusive, and probably a good starting point, would be to
introduce Relationship alongside the other relation fields as a standalone
feature, modifying the ORM to allow the implementation to be less hacky. It
would remain a subclass of ForeignObject (or perhaps RelatedField - I'll
give that a try).

In the future there's potential for a nice refactor of the ORM to
generalise join conditions from key-equality to arbitrary predicates of
which key equality is just one case, at which point Relationship could sit
comfortably as a base class of all the other relations. The assumption that
join==key-equality is pervasive and I think that's an unnecessarily large
chunk of work to take on at this point - it would be better to get the
feature in, then have a release cycle or so to think about the best way to
approach that problem and if we even want to.

I would be happy to write up a DEP expanding on an implementation plan and
potential future work.

Thanks,
Alex

[0]
https://github.com/AlexHill/django-reverse-unique/blob/624c8b19406a40b8e1a2c969c23a6b45bed5a896/reverse_unique/fields.py






On Fri, 31 Aug 2018 at 12:12 am, charettes  wrote:

> Hello Alex!
>
> Thanks for your work on this project, this is definitely something that I
> believe would be useful in Django's core based on the number of times I
> implemented a filtered queryset getter on Models.
>
> I'm not totally sold on the API but having an analog of what ForeignObject
> is to ForeignKey for ManyToManyField would definitely be useful.
>
> From what I can see in relativity.fields[0] most of the additional logic
> revolves around the extra filtering capabilites through Restriction.
>
> Do you have an idea of what the fields.related inheritance chain would
> look like if it was part of core? I feel like having
> Relation(RelatedField), ForeignObject(Relation), ManyToManyField(Relation)
> and adding the filtering logic to Relation could work but I'd be interested
> to hear what you think here. FWIW Anssi implemented something similar[1]
> for reverse unique relationship before FilteredRelation() was introduced.
>
> In a sense Relation would be form of virtual field like ForeignObject
> since it's not in charge of any database field handling.
>
> Simon
>
> [0]
> https://github.com/AlexHill/django-relativity/blob/3802608c64e86c62ab6268efc37a3f5ca8539221/relativity/fields.py
> [1] https://github.com/akaariai/django-reverse-unique
>
>
>
> Le jeudi 30 août 2018 11:38:16 UTC-4, Alex Hill a écrit :
>>
>> Hi all,
>>
>> I've run into many situations during my time using Django where I've
>> wanted to be able to express relations based on some other criteria than
>> foreign key equality. A few examples:
>> - descendants or children of a node in a tree structure
>> - saved search terms to search results
>> - a model containing a date range to timestamped items falling within
>> that date range
>>
>> Currently to do this kind of thing, you might write a getter which
>> returns a queryset - think for example mptt's get_descendants(). But you
>> don't get any of the nice things a real relation field gives you - you
>> can't use that relationship in filters, you can't select/prefetch_related()
>> or values(), there's no reverse relationship, etc.
>>
>> I've written a Relationship field[0] that lets you define relations in
>> terms of arbitrary Q filters containing objects of a new type, L. An L is
>> like an F, but represents a field on the "local" or "left" side of the
>> relation, where the Q is filtering against the remote "to" side of the
>> relation. For example, in a materialised path tree, this 

Re: Proposal: relationships based on arbitrary predicates

2018-08-30 Thread charettes
Hello Alex!

Thanks for your work on this project, this is definitely something that I 
believe would be useful in Django's core based on the number of times I 
implemented a filtered queryset getter on Models.

I'm not totally sold on the API but having an analog of what ForeignObject 
is to ForeignKey for ManyToManyField would definitely be useful.

>From what I can see in relativity.fields[0] most of the additional logic 
revolves around the extra filtering capabilites through Restriction.

Do you have an idea of what the fields.related inheritance chain would look 
like if it was part of core? I feel like having Relation(RelatedField), 
ForeignObject(Relation), ManyToManyField(Relation) and adding the filtering 
logic to Relation could work but I'd be interested to hear what you think 
here. FWIW Anssi implemented something similar[1] for reverse unique 
relationship before FilteredRelation() was introduced.

In a sense Relation would be form of virtual field like ForeignObject since 
it's not in charge of any database field handling.

Simon

[0] 
https://github.com/AlexHill/django-relativity/blob/3802608c64e86c62ab6268efc37a3f5ca8539221/relativity/fields.py
[1] https://github.com/akaariai/django-reverse-unique



Le jeudi 30 août 2018 11:38:16 UTC-4, Alex Hill a écrit :
>
> Hi all,
>
> I've run into many situations during my time using Django where I've 
> wanted to be able to express relations based on some other criteria than 
> foreign key equality. A few examples:
> - descendants or children of a node in a tree structure
> - saved search terms to search results
> - a model containing a date range to timestamped items falling within that 
> date range
>
> Currently to do this kind of thing, you might write a getter which returns 
> a queryset - think for example mptt's get_descendants(). But you don't get 
> any of the nice things a real relation field gives you - you can't use that 
> relationship in filters, you can't select/prefetch_related() or values(), 
> there's no reverse relationship, etc.
>
> I've written a Relationship field[0] that lets you define relations in 
> terms of arbitrary Q filters containing objects of a new type, L. An L is 
> like an F, but represents a field on the "local" or "left" side of the 
> relation, where the Q is filtering against the remote "to" side of the 
> relation. For example, in a materialised path tree, this is how you might 
> express descendants:
>
> class Node(models.Model):
> path = models.TextField()
> descendants = Relationship(
> "self",
> Q(path__startswith=L('path'), pk__ne=L('pk')),
> multiple=True,
> reverse_multiple=True,
> related_name='ascendants',
> )
>
> Now you can use the descendants field like any other many-to-many field in 
> all the places I mentioned above, but the relationship is based purely on 
> prefix-matching on the path field. You also get an ascendants field on 
> Node, which represents the path back to the root and can be used in the 
> same way.
>
> I think this could make a nice new feature for Django. It would give a 
> usability boost to anyone using MPTT or treebeard, for example. It works OK 
> as a third-party library, but the current implementation relies heavily on 
> undocumented ORM internals, and there are a few features I'd like to 
> implement that are impractical without making some ORM changes.
>
> Thoughts/feedback/questions welcome!
>
> Thanks,
> Alex
>
> [0] https://github.com/alexhill/django-relativity
>
>

-- 
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/c87af098-baa4-4d45-9a4b-757166b41734%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Proposal: relationships based on arbitrary predicates

2018-08-30 Thread Alexander Hill
Hi all,

I've run into many situations during my time using Django where I've wanted
to be able to express relations based on some other criteria than foreign
key equality. A few examples:
- descendants or children of a node in a tree structure
- saved search terms to search results
- a model containing a date range to timestamped items falling within that
date range

Currently to do this kind of thing, you might write a getter which returns
a queryset - think for example mptt's get_descendants(). But you don't get
any of the nice things a real relation field gives you - you can't use that
relationship in filters, you can't select/prefetch_related() or values(),
there's no reverse relationship, etc.

I've written a Relationship field[0] that lets you define relations in
terms of arbitrary Q filters containing objects of a new type, L. An L is
like an F, but represents a field on the "local" or "left" side of the
relation, where the Q is filtering against the remote "to" side of the
relation. For example, in a materialised path tree, this is how you might
express descendants:

class Node(models.Model):
path = models.TextField()
descendants = Relationship(
"self",
Q(path__startswith=L('path'), pk__ne=L('pk')),
multiple=True,
reverse_multiple=True,
related_name='ascendants',
)

Now you can use the descendants field like any other many-to-many field in
all the places I mentioned above, but the relationship is based purely on
prefix-matching on the path field. You also get an ascendants field on
Node, which represents the path back to the root and can be used in the
same way.

I think this could make a nice new feature for Django. It would give a
usability boost to anyone using MPTT or treebeard, for example. It works OK
as a third-party library, but the current implementation relies heavily on
undocumented ORM internals, and there are a few features I'd like to
implement that are impractical without making some ORM changes.

Thoughts/feedback/questions welcome!

Thanks,
Alex

[0] https://github.com/alexhill/django-relativity

-- 
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/CA%2BKBOKzXDHJnO09Trkws2MJDu4DnOUsgxgA634xpCAKk1O5xJw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.