def _relation(self, id, join_on, join_to, level=None, featuretype=None,
                  order_by=None, reverse=False, completely_within=False,
                  limit=None):

        # The following docstring will be included in the parents() and
        # children() docstrings to maintain consistency, since they both
        # delegate to this method.
        """
        Parameters
        ----------

        id : string or a Feature object

        level : None or int

            If `level=None` (default), then return all children regardless
            of level.  If `level` is an integer, then constrain to just that
            level.
        {_method_doc}

        Returns
        -------
        A generator object that yields :class:`Feature` objects.
        """

        if isinstance(id, Feature):
            id = id.id

        other = '''
        JOIN relations
        ON relations.{join_on} = features.id
        WHERE relations.{join_to} = ?
        '''.format(**locals())
        args = [id]

        level_clause = ''
        if level is not None:
            level_clause = 'relations.level = ?'
            args.append(level)

        query, args = helpers.make_query(
            args=args,
            other=other,
            extra=level_clause,
            featuretype=featuretype,
            order_by=order_by,
            reverse=reverse,
            limit=limit,
            completely_within=completely_within,
        )

        # modify _SELECT so that only unique results are returned
        query = query.replace("SELECT", "SELECT DISTINCT")
        for i in self._execute(query, args):
            yield self._feature_returner(**i)