| 677 | is_aliased_class: bool |
| 678 | |
| 679 | def __init__( |
| 680 | self, |
| 681 | parent: Union[RootRegistry, _PropRegistry], |
| 682 | entity: _InternalEntityType[Any], |
| 683 | ): |
| 684 | self.key = entity |
| 685 | self.parent = parent |
| 686 | self.is_aliased_class = entity.is_aliased_class |
| 687 | self.entity = entity |
| 688 | self.path = parent.path + (entity,) |
| 689 | |
| 690 | # the "natural path" is the path that we get when Query is traversing |
| 691 | # from the lead entities into the various relationships; it corresponds |
| 692 | # to the structure of mappers and relationships. when we are given a |
| 693 | # path that comes from loader options, as of 1.3 it can have ac-hoc |
| 694 | # with_polymorphic() and other AliasedInsp objects inside of it, which |
| 695 | # are usually not present in mappings. So here we track both the |
| 696 | # "enhanced" path in self.path and the "natural" path that doesn't |
| 697 | # include those objects so these two traversals can be matched up. |
| 698 | |
| 699 | # the test here for "(self.is_aliased_class or parent.is_unnatural)" |
| 700 | # are to avoid the more expensive conditional logic that follows if we |
| 701 | # know we don't have to do it. This conditional can just as well be |
| 702 | # "if parent.path:", it just is more function calls. |
| 703 | # |
| 704 | # This is basically the only place that the "is_unnatural" flag |
| 705 | # actually changes behavior. |
| 706 | if parent.path and (self.is_aliased_class or parent.is_unnatural): |
| 707 | # this is an infrequent code path used for loader strategies that |
| 708 | # also make use of of_type() or other intricate polymorphic |
| 709 | # base/subclass combinations |
| 710 | parent_natural_entity = parent.natural_path[-1] |
| 711 | |
| 712 | if entity.mapper.isa( |
| 713 | parent_natural_entity.mapper # type: ignore |
| 714 | ) or parent_natural_entity.mapper.isa( # type: ignore |
| 715 | entity.mapper |
| 716 | ): |
| 717 | # when the entity mapper and parent mapper are in an |
| 718 | # inheritance relationship, use entity.mapper in natural_path. |
| 719 | # First case: entity.mapper inherits from parent mapper (e.g., |
| 720 | # accessing a subclass mapper through parent path). Second case |
| 721 | # (issue #13193): parent mapper inherits from entity.mapper |
| 722 | # (e.g., parent path has Sub(Base) but we're accessing with |
| 723 | # Base where Base.related is declared, so use Base in |
| 724 | # natural_path). |
| 725 | self.natural_path = parent.natural_path + (entity.mapper,) |
| 726 | else: |
| 727 | self.natural_path = parent.natural_path + ( |
| 728 | parent_natural_entity.entity, # type: ignore |
| 729 | ) |
| 730 | # it seems to make sense that since these paths get mixed up |
| 731 | # with statements that are cached or not, we should make |
| 732 | # sure the natural path is cacheable across different occurrences |
| 733 | # of equivalent AliasedClass objects. however, so far this |
| 734 | # does not seem to be needed for whatever reason. |
| 735 | # elif not parent.path and self.is_aliased_class: |
| 736 | # self.natural_path = (self.entity._generate_cache_key()[0], ) |