Saiful Azam
Saiful Azam

Reputation: 107

'tuple' object has no attribute 'user'

Thank you so much for your help, i really appreciate it.

I get this error "'tuple' object has no attribute 'user'" Anybody can tell me if my codes is right. im trying to make artist followers system.

View.py

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def artist_follow(request, pk):
    artist = Artist.objects.get(pk=pk)
    current_user = User.objects.get(pk=request.user.id)

    # check the current user already followed the artsit, if exist remove it else add
    if artist.followers().user.filter(user=current_user).exists(): # This line throw error
        artist.followers().user.remove(current_user)
    else:
        artist.followers().user.add(current_user)

    return Response(status=status.HTTP_200_OK)

Model.py

class Artist(models.Model):
    name = models.CharField(unique=True, max_length=100)
    .....

    def __str__(self):
        return self.name

    def followers(self):
        return ArtistFollower.objects.get_or_create(artist=self)

class ArtistFollower(models.Model):
    artist = models.OneToOneField(Artist, on_delete=models.CASCADE)
    user = models.ManyToManyField(User, related_name='user_artist_followed')

    def __str__(self):
        return self.artist.name

Traceback

Traceback (most recent call last):
  File "/opt/anaconda3/envs/app-ENV/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/opt/anaconda3/envs/app-ENV/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/opt/anaconda3/envs/app-ENV/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/opt/anaconda3/envs/app-ENV/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/opt/anaconda3/envs/app-ENV/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/opt/anaconda3/envs/app-ENV/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/opt/anaconda3/envs/app-ENV/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/opt/anaconda3/envs/app-ENV/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/opt/anaconda3/envs/app-ENV/lib/python3.8/site-packages/rest_framework/decorators.py", line 50, in handler
    return func(*args, **kwargs)
  File "/Users/username/Desktop/Clients/app/source/app/artists/web/views.py", line 44, in artist_follow
    if artist.followers().user.filter(user=current_user).exists():

Exception Type: AttributeError at /api/v1/web/artists/1/follow
Exception Value: 'tuple' object has no attribute 'user'

Upvotes: 1

Views: 685

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476557

Your .followers() method uses .get_or_create(…) [Django-doc], and thus:

Returns a tuple of (object, created), where object is the retrieved or created object and created is a boolean specifying whether a new object was created.

You can return the item that is obtained or constructed by subscripting:

class Artist(models.Model):
    # …
    
    def followers(self):
        return ArtistFollower.objects.get_or_create(artist=self)[0]

or work with a throw away variable:

class Artist(models.Model):
    # …
    
    def followers(self):
        obj, __ = ArtistFollower.objects.get_or_create(artist=self)
        return obj

It however does not seem to make much sense to create a ArtistFollower model. Why not create a ManyToManyField to the User class in Artist model. This will simplify the modeling, and reduce the size of the database.

Such model thus looks like:

from django.conf import settings

class Artist(models.Model):
    name = models.CharField(unique=True, max_length=100)
    followers = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='following_artists'
    )

You thus can then access the followers of an artist with:

my_artist.followers.all()  # queryset of User objects

or for a user the artists they are following with:

my_user.following_artists.all()

You can also edit the ManyToManyField at both directions. For more information, see te section on many-to-many relations of the documentation.


Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Upvotes: 1

Related Questions