  class YearExact(YearLookup, Exact):
      lookup_name = 'exact'
  
      def as_sql(self, compiler, connection):
          # We will need to skip the extract part and instead go
          # directly with the originating field, that is self.lhs.lhs.
          lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
          rhs_sql, rhs_params = self.process_rhs(compiler, connection)
          try:
              # Check that rhs_params[0] exists (IndexError),
              # it isn't None (TypeError), and is a number (ValueError)
              int(rhs_params[0])
          except (IndexError, TypeError, ValueError):
              # Can't determine the bounds before executing the query, so skip
              # optimizations by falling back to a standard exact comparison.
              return super().as_sql(compiler, connection)
          bounds = self.year_lookup_bounds(connection, rhs_params[0])
          params.extend(bounds)
          return '%s BETWEEN %%s AND %%s' % lhs_sql, params
  
  
  class YearGt(YearComparisonLookup):
      lookup_name = 'gt'
  
 
</s> remove     DateField, DateTimeField, IntegerField, Max, OuterRef, Subquery, TimeField,
 </s> add     DateField, DateTimeField, F, IntegerField, Max, OuterRef, Subquery,
    TimeField,
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
  class YearExact(YearLookup, Exact):
      lookup_name = 'exact'
  
      def as_sql(self, compiler, connection):
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
        # Avoid the extract operation if the rhs is a direct value to allow
        # indexes to be used.
        if self.rhs_is_direct_value():
            # Skip the extract part by directly using the originating field,
            # that is self.lhs.lhs.
            lhs_sql, params = self.process_lhs(compiler, connection, self.lhs.lhs)
            bounds = self.year_lookup_bounds(connection, self.rhs)
            params.extend(bounds)
            return '%s BETWEEN %%s AND %%s' % lhs_sql, params
        return super().as_sql(compiler, connection)
  
  
  class YearGt(YearComparisonLookup):
      lookup_name = 'gt'
  
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 Fixed #30494 -- Disabled __year lookup optimization for indirect values.

The previous heuristics were naively enabling the BETWEEN optimization on
successful cast of the first rhs SQL params to an integer while it was
not appropriate for a lot of database resolved expressions.

Thanks Alexey Chernov for the report. 
django/db/models/lookups.py