AttributeError: 'ManyToOneRel' object has no attribute 'attname'

Hi I’m building a rest API using django 3.2. I changed a OnetoOnefield in a PostImage model to a ForeignKey and getting the said error.

AttributeError: ‘ManyToOneRel’ object has no attribute ‘attname’

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},

Here is the log from 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 aboe 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.

Can you share the serializers used in the get_posts_for_authenticated_user method.

1 Like

Hi,

class GetPostsSerializer(serializers.Serializer):
    circle_id = serializers.ListField(
        required=False,
        child=serializers.IntegerField(validators=[circle_id_exists]),
    )
    list_id = serializers.ListField(
        required=False,
        child=serializers.IntegerField(validators=[list_id_exists])
    )
    max_id = serializers.IntegerField(
        required=False,
    )
    min_id = serializers.IntegerField(
        required=False,
    )
    count = serializers.IntegerField(
        required=False,
        max_value=20
    )
    username = serializers.CharField(
        max_length=settings.USERNAME_MAX_LENGTH,
        allow_blank=False,
        validators=[
            username_characters_validator,
            user_username_exists
        ],
        required=False
    )
class AuthenticatedUserPostSerializer(serializers.ModelSerializer):
    creator = PostCreatorField(post_creator_serializer=CommonPublicUserSerializer,
                               community_membership_serializer=CommunityMembershipSerializer)
    reactions_emoji_counts = PostReactionsEmojiCountField(emoji_count_serializer=PostEmojiCountSerializer)
    reaction = ReactionField(reaction_serializer=PostReactionSerializer)
    comments_count = CommentsCountField()
    circles = CirclesField(circle_serializer=PostCircleSerializer)
    community = PostCommunitySerializer()
    is_muted = PostIsMutedField()
    language = PostLanguageSerializer()
    is_encircled = IsEncircledField()
    hashtags = CommonHashtagSerializer(many=True)
    links = CommonPostLinkSerializer(many=True)
    #Core ends here-------->
    poll_post = PollPostSerializer(many=False)


    class Meta:
        model = Post
        fields = (
            'id',
            'uuid',
            'comments_count',
            'reactions_emoji_counts',
            'created',
            'text',
            'creator',
            'reaction',
            'comments_enabled',
            'public_reactions',
            'circles',
            'community',
            'language',
            'is_muted',
            'is_encircled',
            'is_edited',
            'is_closed',
            'media_height',
            'media_width',
            'media_thumbnail',
            'hashtags',
            'links',
            #Core ends here-------->
            'poll_post',
        )```

I believe there is a ForeignKey relationship or a OneToOneField relationship between Post and Poll Post. Try putting read_only=True to poll_post i.e

    poll_post = PollPostSerializer(many=False, read_only=True)

and let’s see what you get.

Hi. Yes , Here there is a OneToOneField named parent in

class PollPost(models.Model):
    parent = models.OneToOneField(Post, related_name='poll_post', blank=False, null=True, on_delete=models.SET_NULL)

Any reason why this could be causing a problem ? I’ll post the results in a while.

Hey. I’m awaiting a reply. I did as you asked me to. Error persists.

Yes, after seeing the code you shared it seems most of the code in your Models and Serializers are fine.
There might be something that I’m also missing, anyway I can suggest you to do this

  • Double check all the fields in your Post model. Ensure that each field is defined correctly and there are no typos or mistakes.
  • Ensure that all the fields in your AuthenticatedUserPostSerializer are correctly defined and match the fields in your Post model.
  • If any of the fields in your serializer are related fields (such as ForeignKey or OneToOneField), ensure that they are handled correctly. Check the related model and serializer to make sure everything aligns.
  • Use print statements or use a debugger to inspect the data during the serialization process, even use print in the model methods like this one that you have used in your method user.get_timeline_posts.

I just rechecked. The moment I change the Foreignkey to a OnetoOnefield, the error disappears. Anything you sense additionall about what’s happening ?

When you changed Foreignkey to a OnetoOnefield did you run migrations command ?

Yup. Did that. Run migrations and then migrate.

I’ve been trying to follow this thread, but I’m confused here regarding the models and serializers being used.

What I see are:

  • A model named PostImage
  • References to user, but no definition for it.
  • A serializer named GetPostsSerializer, with nothing referencing it.
  • A serializer name AuthenticatedUserPostSerializer that references PollPostSerializer
  • A reference to a model named Post, but no definition for it
  • No definition for PollPostSerializer.

So I think some clarification may be needed here.

But aside from that for the moment, if poll_post is a reverse-foreign key with Post, then many must be True, because there can be multiple poll_post for each Post.

This situation is fundamentally different than the OneToOneField situation.

(If this is not the case, then we would need to see all the models and serializers involved.)