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 <[email protected]> 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 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 [email protected]. > To post to this group, send email to [email protected]. > 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 > <https://groups.google.com/d/msgid/django-developers/c87af098-baa4-4d45-9a4b-757166b41734%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 [email protected]. To post to this group, send email to [email protected]. 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%2BKBOKyd3T3MiRHZT7%2BivC669X32FWaNPA0wGGe05ptmzRyjdA%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.
