diff --git a/django/db/models/query.py b/django/db/models/query.py
index 71ebf660c7..2db6690c44 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -7,6 +7,7 @@ import operator
 import warnings
 from collections import OrderedDict, namedtuple
 from functools import lru_cache
+from itertools import chain
 
 from django.conf import settings
 from django.core import exceptions
@@ -981,7 +982,10 @@ class QuerySet:
         clone = self._chain()
         names = self._fields
         if names is None:
-            names = {f.name for f in self.model._meta.get_fields()}
+            names = set(chain.from_iterable(
+                (field.name, field.attname) if hasattr(field, 'attname') else (field.name,)
+                for field in self.model._meta.get_fields()
+            ))
 
         for alias, annotation in annotations.items():
             if alias in names:
diff --git a/tests/aggregation_regress/tests.py b/tests/aggregation_regress/tests.py
index 9c8bac0d21..7f03d66bab 100644
--- a/tests/aggregation_regress/tests.py
+++ b/tests/aggregation_regress/tests.py
@@ -769,6 +769,11 @@ class AggregationTests(TestCase):
         with self.assertRaisesMessage(ValueError, msg):
             Author.objects.annotate(friends=Count('friends'))
 
+    def test_fk_attname_conflict(self):
+        msg = "The annotation 'contact_id' conflicts with a field on the model."
+        with self.assertRaisesMessage(ValueError, msg):
+            Book.objects.annotate(contact_id=F('publisher_id'))
+
     def test_values_queryset_non_conflict(self):
         # Regression for #14707 -- If you're using a values query set, some potential conflicts are avoided.