| 714 | is_aliased_class: bool |
| 715 | |
| 716 | def __init__( |
| 717 | self, |
| 718 | parent: Union[RootRegistry, PropRegistry], |
| 719 | entity: _InternalEntityType[Any], |
| 720 | ): |
| 721 | self.key = entity |
| 722 | self.parent = parent |
| 723 | self.is_aliased_class = entity.is_aliased_class |
| 724 | self.entity = entity |
| 725 | self.path = parent.path + (entity,) |
| 726 | |
| 727 | # the "natural path" is the path that we get when Query is traversing |
| 728 | # from the lead entities into the various relationships; it corresponds |
| 729 | # to the structure of mappers and relationships. when we are given a |
| 730 | # path that comes from loader options, as of 1.3 it can have ac-hoc |
| 731 | # with_polymorphic() and other AliasedInsp objects inside of it, which |
| 732 | # are usually not present in mappings. So here we track both the |
| 733 | # "enhanced" path in self.path and the "natural" path that doesn't |
| 734 | # include those objects so these two traversals can be matched up. |
| 735 | |
| 736 | # the test here for "(self.is_aliased_class or parent.is_unnatural)" |
| 737 | # are to avoid the more expensive conditional logic that follows if we |
| 738 | # know we don't have to do it. This conditional can just as well be |
| 739 | # "if parent.path:", it just is more function calls. |
| 740 | # |
| 741 | # This is basically the only place that the "is_unnatural" flag |
| 742 | # actually changes behavior. |
| 743 | if parent.path and (self.is_aliased_class or parent.is_unnatural): |
| 744 | # this is an infrequent code path used for loader strategies that |
| 745 | # also make use of of_type() or other intricate polymorphic |
| 746 | # base/subclass combinations |
| 747 | parent_natural_entity = parent.natural_path[-1] |
| 748 | |
| 749 | if entity.mapper.isa( |
| 750 | parent_natural_entity.mapper # type: ignore |
| 751 | ) or parent_natural_entity.mapper.isa( # type: ignore |
| 752 | entity.mapper |
| 753 | ): |
| 754 | # when the entity mapper and parent mapper are in an |
| 755 | # inheritance relationship, use entity.mapper in natural_path. |
| 756 | # First case: entity.mapper inherits from parent mapper (e.g., |
| 757 | # accessing a subclass mapper through parent path). Second case |
| 758 | # (issue #13193): parent mapper inherits from entity.mapper |
| 759 | # (e.g., parent path has Sub(Base) but we're accessing with |
| 760 | # Base where Base.related is declared, so use Base in |
| 761 | # natural_path). |
| 762 | self.natural_path = parent.natural_path + (entity.mapper,) |
| 763 | else: |
| 764 | self.natural_path = parent.natural_path + ( |
| 765 | parent_natural_entity.entity, # type: ignore |
| 766 | ) |
| 767 | # it seems to make sense that since these paths get mixed up |
| 768 | # with statements that are cached or not, we should make |
| 769 | # sure the natural path is cacheable across different occurrences |
| 770 | # of equivalent AliasedClass objects. however, so far this |
| 771 | # does not seem to be needed for whatever reason. |
| 772 | # elif not parent.path and self.is_aliased_class: |
| 773 | # self.natural_path = (self.entity._generate_cache_key()[0], ) |