Unable to use @property method

I have a model named Invitation, where I have created a property method which returns the value returned by __str__() method, like below:

class Invitation(models.Model):
    def __str__(self):
        name = "Sent To: " + self.sent_to + " - Invited To: " + self.invited_to

        if self.accepted_at is not None:
            name = name + " - Accepted On: " + \
                str(self.accepted_at.date()) + " (yy-mm-dd) - At: " + str(self.accepted_at.hour) + \
                ":" + str(self.accepted_at.minute) + ":" + str(self.accepted_at.second) + \
                " (hh:mm:ss)"
        
        if self.is_canceled:
            name = name + " - CANCELED"
        elif timezone.now() > self.expiration_date:
            name = name + " - EXPIRED AT: " + str(self.expiration_date)

        return(name)

    @property
    def get_str(self):
        return self.__str__()

Now, I am trying to use this property method in QuerySet’s values_list() method as below:

Invitation.objects.values_list("pk", "get_str")

But I am getting following error:

Cannot resolve keyword 'get_str' into field. Choices are: accepted_at, created_at, created_by, created_by_id, expiration_date, id, invited_to, invitee_first_name, invitee_last_name, is_canceled, secret, sent_to

Full Traceback of error is:

Traceback Switch to copy-and-paste view
/app/.heroku/python/lib/python3.10/site-packages/django/db/models/sql/query.py, line 2107, in add_fields
                join_info = self.setup_joins( …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/db/models/sql/query.py, line 1773, in setup_joins
                path, final_field, targets, rest = self.names_to_path( …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/db/models/sql/query.py, line 1677, in names_to_path
                    raise FieldError( …
Local vars
During handling of the above exception (Cannot resolve keyword 'get_str' into field. Choices are: accepted_at, created_at, created_by, created_by_id, expiration_date, id, invited_to, invitee_first_name, invitee_last_name, is_canceled, secret, sent_to), another exception occurred:
/app/.heroku/python/lib/python3.10/site-packages/django/core/handlers/exception.py, line 55, in inner
                response = get_response(request) …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/core/handlers/base.py, line 197, in _get_response
                response = wrapped_callback(request, *callback_args, **callback_kwargs) …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/views/generic/base.py, line 84, in view
            return self.dispatch(request, *args, **kwargs) …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/contrib/auth/mixins.py, line 73, in dispatch
        return super().dispatch(request, *args, **kwargs) …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/views/generic/base.py, line 119, in dispatch
        return handler(request, *args, **kwargs) …
Local vars
/app/apps/accounts/views.py, line 295, in get
        invites = Invitation.objects.get_invites_to_cancel(user) …
Local vars
/app/apps/accounts/models.py, line 551, in get_invites_to_cancel
                values_list("pk", "get_str") …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/db/models/query.py, line 980, in values_list
        clone = self._values(*_fields, **expressions) …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/db/models/query.py, line 943, in _values
        clone.query.set_values(fields) …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/db/models/sql/query.py, line 2425, in set_values
        self.add_fields(field_names, True) …
Local vars
/app/.heroku/python/lib/python3.10/site-packages/django/db/models/sql/query.py, line 2140, in add_fields
                raise FieldError( …
Local vars

What wrong am I doing? What is the proper way to use property method? Any help would be appreciated.

Hey there!
The get_str property is not available at the database level. It’s a property of each object of yours Invitation class. You won’t be able to that in a queryset. The other way around it is:

for invitation in Invitation.objects.all():
    invite = str(invitation)
    # The equivalent of
    # invite = invitation.get_str

[opinion]
Notice that i called the str method, not the __str__ method. So the get_str property may not be necessary
[/opinion]

@leandrodesouzadev I know this way, but I was trying to skip the use of for-loop for better coding structure. I have searched on stackoverflow and other pages, I found some related things like @cached_property, annotation(), F() etc, can you explain if I can achieve my required functionality using any of the things I mentioned instead of using for loop? Please refer to this stackoverflow article

From my point of view, the result of the str method changes by a lot of constraints, so you may be able to move this to the database, but it will require a lot of work, and all this code will be on the database, on a function or something. So i don’t think all of the work required to move this out of your python code, are worth skipping a line of a builtin python feature.
for loops are not a bad pratice or a code smell, they are a feature of the language.

@leandrodesouzadev yeah I know they are not bad practice. But I try to avoid them as much as possible if the functionality can be achieved in other simpler way. But, OK, I will use for-loop for this functionality. Thanks

Yeah, that’s the correct way of thinking, if we can simplify things by getting them from the database, will lead to less complexity, that may be the case of nested for loops. But sometimes it’s just not worth it!

Maybe a other way around for your solution is to save the invitation message into the database, and only display the message with: “EXPIRED” directly on the clients: js, admin or apis.

Actually I was coding the interface for system admin to cancel the invitations (by changing the value of BooleanField is_canceled to False). So, I was trying to get only those Invitation instances, which were not canceled (already), expired and accepted. So, system admin would be able to only select the correct invitation and not the incorrect one (which might have been accepted, already canceled, or expired). So, I think for-loop approach is better and I will go for it.