Walk the list of names and turns them into PathInfo tuples. A single name in 'names' can generate multiple PathInfos (m2m, for example). 'names' is the path of names to travel, 'opts' is the model Options we start the name resolving from, 'allow_many' is as for setu
(self, names, opts, allow_many=True, fail_on_missing=False)
| 1772 | self._filtered_relations[filtered_relation.alias] = filtered_relation |
| 1773 | |
| 1774 | def names_to_path(self, names, opts, allow_many=True, fail_on_missing=False): |
| 1775 | """ |
| 1776 | Walk the list of names and turns them into PathInfo tuples. A single |
| 1777 | name in 'names' can generate multiple PathInfos (m2m, for example). |
| 1778 | |
| 1779 | 'names' is the path of names to travel, 'opts' is the model Options we |
| 1780 | start the name resolving from, 'allow_many' is as for setup_joins(). |
| 1781 | If fail_on_missing is set to True, then a name that can't be resolved |
| 1782 | will generate a FieldError. |
| 1783 | |
| 1784 | Return a list of PathInfo tuples. In addition return the final field |
| 1785 | (the last used join field) and target (which is a field guaranteed to |
| 1786 | contain the same value as the final field). Finally, return those names |
| 1787 | that weren't found (which are likely transforms and the final lookup). |
| 1788 | """ |
| 1789 | path, names_with_path = [], [] |
| 1790 | for pos, name in enumerate(names): |
| 1791 | cur_names_with_path = (name, []) |
| 1792 | if name == "pk" and opts is not None: |
| 1793 | name = opts.pk.name |
| 1794 | |
| 1795 | field = None |
| 1796 | filtered_relation = None |
| 1797 | try: |
| 1798 | if opts is None: |
| 1799 | raise FieldDoesNotExist |
| 1800 | field = opts.get_field(name) |
| 1801 | except FieldDoesNotExist: |
| 1802 | if name in self.annotation_select: |
| 1803 | field = self.annotation_select[name].output_field |
| 1804 | elif name in self._filtered_relations and pos == 0: |
| 1805 | filtered_relation = self._filtered_relations[name] |
| 1806 | if LOOKUP_SEP in filtered_relation.relation_name: |
| 1807 | parts = filtered_relation.relation_name.split(LOOKUP_SEP) |
| 1808 | filtered_relation_path, field, _, _ = self.names_to_path( |
| 1809 | parts, |
| 1810 | opts, |
| 1811 | allow_many, |
| 1812 | fail_on_missing, |
| 1813 | ) |
| 1814 | path.extend(filtered_relation_path[:-1]) |
| 1815 | else: |
| 1816 | field = opts.get_field(filtered_relation.relation_name) |
| 1817 | if field is not None: |
| 1818 | # Fields that contain one-to-many relations with a generic |
| 1819 | # model (like a GenericForeignKey) cannot generate reverse |
| 1820 | # relations and therefore cannot be used for reverse querying. |
| 1821 | if field.is_relation and not field.related_model: |
| 1822 | raise FieldError( |
| 1823 | "Field %r does not generate an automatic reverse " |
| 1824 | "relation and therefore cannot be used for reverse " |
| 1825 | "querying. If it is a GenericForeignKey, consider " |
| 1826 | "adding a GenericRelation." % name |
| 1827 | ) |
| 1828 | try: |
| 1829 | model = field.model._meta.concrete_model |
| 1830 | except AttributeError: |
| 1831 | # QuerySet.annotate() may introduce fields that aren't |