I changed a OnetoOnefield in a PostImage model to a ForeignKey and getting the said error.
Here is what is changed :
class PostImage(models.Model):
# post = models.OneToOneField(Post, on_delete=models.CASCADE, related_name='image', null=True)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='image', null=True)
image = ProcessedImageField(verbose_name=_('image'), storage=post_image_storage,
upload_to=upload_to_post_image_directory,
width_field='width',
height_field='height',
blank=False, null=True, format='JPEG', options={'quality': 80},
The logs of docker-compose :
my-api | Traceback (most recent call last):
my-api | File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
my-api | response = get_response(request)
my-api | File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
my-api | response = wrapped_callback(request, *callback_args, **callback_kwargs)
my-api | File "/usr/local/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
my-api | return view_func(*args, **kwargs)
my-api | File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
my-api | return self.dispatch(request, *args, **kwargs)
my-api | File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
my-api | response = self.handle_exception(exc)
my-api | File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
my-api | self.raise_uncaught_exception(exc)
my-api | File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
my-api | raise exc
my-api | File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
my-api | response = handler(request, *args, **kwargs)
my-api | File "/opt/my-api-core/my_posts/views/posts/views.py", line 55, in get
my-api | return self.get_posts_for_authenticated_user(request)
my-api | File "/opt/my-api-core/my_posts/views/posts/views.py", line 92, in get_posts_for_authenticated_user
my-api | post_serializer_data = AuthenticatedUserPostSerializer(posts, many=True, context={"request": request}).data
my-api | File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 768, in data
my-api | ret = super().data
my-api | File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 253, in data
my-api | self._data = self.to_representation(self.instance)
my-api | File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 686, in to_representation
my-api | return [
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 280, in __iter__
my-api | self._fetch_all()
my-api | File "/usr/local/lib/python3.8/site-packages/cacheops/query.py", line 273, in _fetch_all
my-api | return self._no_monkey._fetch_all(self)
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 1324, in _fetch_all
my-api | self._result_cache = list(self._iterable_class(self))
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 51, in __iter__
my-api | results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1156, in execute_sql
my-api | sql, params = self.as_sql()
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 507, in as_sql
my-api | extra_select, order_by, group_by = self.pre_sql_setup()
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 55, in pre_sql_setup
my-api | self.setup_query()
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 46, in setup_query
my-api | self.select, self.klass_info, self.annotation_col_map = self.get_select()
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 227, in get_select
my-api | cols = self.get_default_columns()
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 671, in get_default_columns
my-api | only_load = self.deferred_to_columns()
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1096, in deferred_to_columns
my-api | self.query.deferred_to_data(columns, self.query.get_loaded_field_names_cb)
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py", line 754, in deferred_to_data
my-api | callback(target, model, values)
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py", line 2179, in get_loaded_field_names_cb
my-api | target[model] = {f.attname for f in fields}
my-api | File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py", line 2179, in <setcomp>
my-api | target[model] = {f.attname for f in fields}
my-api | AttributeError: 'ManyToOneRel' object has no attribute 'attname'
django.server ERROR "GET /api/posts/?count=10& HTTP/1.1" 500 195670
Based on the above logs, the function causing the error :
def get_posts_for_authenticated_user(self, request):
query_params = request.query_params.dict()
normalize_list_value_in_request_data('circle_id', query_params)
normalize_list_value_in_request_data('list_id', query_params)
serializer = GetPostsSerializer(data=query_params)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
circles_ids = data.get('circle_id')
lists_ids = data.get('list_id')
max_id = data.get('max_id')
min_id = data.get('min_id')
count = data.get('count', 10)
username = data.get('username')
user = request.user
if username:
if username == user.username:
posts = user.get_posts(max_id=max_id)
else:
posts = user.get_posts_for_user_with_username(username, max_id=max_id, min_id=min_id)
else:
posts = user.get_timeline_posts(
circles_ids=circles_ids,
lists_ids=lists_ids,
max_id=max_id,
min_id=min_id,
count=count
)
posts = posts.order_by('-id')[:count]
post_serializer_data = AuthenticatedUserPostSerializer(posts, many=True, context={"request": request}).data
return Response(post_serializer_data, status=status.HTTP_200_OK)
What I have tried :
1.Changed the order_by() from
posts = posts.order_by('-id')[:count]
to
posts = posts.order_by('-created')[:count]
since count was mentioned in the docker logs. But still getting the said error.
2.I even tried clearing the database using the flush command. Recreated the docker containers. Error persists.
AttributeError: 'ManyToOneRel' object has no attribute 'attname': that should mean that Django tries to access a field attribute that does not exist on a related model.That error often occurs after changing relationships in models, as you did by changing a
OneToOneFieldto aForeignKey. (a bit as in "How to Update Django Model Relationship only with Foreign Key Value", even though the focus is on how to update a foreign key relationship in a Django model by directly using the primary key of the related model, rather than fetching the entire related model instance).When you switch from a
OneToOneFieldto aForeignKey, Django treats this relationship differently. TheManyToOneRelobject is an internal Django construct representing the reverse relation fromPosttoPostImage.Since the error is occurring during serialization, make sure
AuthenticatedUserPostSerializercorrectly handles thepostfield. If the serializer attempts to access a non-existent attribute on thepostfield, it could cause this error. Look for any part of the serializer that might be treatingpost.imageas a single instance rather than a set of instances.You have
related_name='image'in yourForeignKey. That means that, on thePostmodel, Django expectspost.imageto return multiplePostImageinstances. If anywhere in your code (or serializers) you are treatingpost.imageas a single instance (as it was with aOneToOneField), it would cause an error. You might need to adjust this logic or change therelated_nameto something like'images'to reflect the multiple instances.The
related_name='image'in yourForeignKeyshould now reflect thatpost.imagewill return a queryset ofPostImageinstances, not a single instance. Consider changing it to'images'to better represent the relationship.After changing field types, make sure your database schema is updated correctly with Django migrations. Sometimes, inconsistencies between the model and the database can lead to unexpected errors.
Finally, add debugging information in the
get_posts_for_authenticated_usermethod to inspect thepostsquery.These debugging statements will help you understand what is happening at the database query level. Keep an eye on your console or logs to see the output.
The
related_nameattribute in aForeignKeyrelationship specifies the name to use for the reverse relation from the related model back to your model. In your case, by settingrelated_name='images'in thePostImagemodel, you are telling Django to useimagesto access relatedPostImageinstances from aPostinstance.So, after this change, on any
Postobject, you can access its relatedPostImageobjects usingpost.images.all().Yes, changing to a ForeignKey and using
related_name='images'will indeed allow multiple instances ofPostImageto be associated with a singlePost. That is the correct approach for your use case where a blog post may have multiple images.The error
FieldDoesNotExist: Post has no field named 'image'occurs because, in your views or serializers, you are likely still accessingimagedirectly on aPostobject. After the change in your model, you should update your code to useimages.Wherever you were accessing
post.image, you should now usepost.images.all()to get a queryset of all relatedPostImageobjects.Update any views, serializers, or other parts of your code that previously relied on
post.image. For serialization, if you need to include images in yourPostserializer, you might use a nested serializer forPostImage.Your serializer could be:
Run migrations again after any model change: