1

I’m working through a book that introduces the Django and Django Rest frameworks. The tutorial creates a rest API with games, categories, players and scores, with a suitable relational model between them. I’ve stumbled upon an issue when inserting a game category I get the following error:

AttributeError at /game-categories/
'GameCategory' object has no attribute 'games'
Request Method: POST
Request URL:    http://127.0.0.1:8000/game-categories/
Django Version: 1.10.5
Exception Type: AttributeError
Exception Value:    
'GameCategory' object has no attribute 'games'
Exception Location: C:\Users\user\AppData\Local\Programs\Python\Python36-32\lib\site-packages\rest_framework\fields.py in get_attribute, line 103
Python Executable:  C:\Users\user\AppData\Local\Programs\Python\Python36-32\python.exe
Python Version: 3.6.0
Python Path:    
['C:\\Python\\Django01\\gamesapi',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36-32\\python36.zip',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36-32\\DLLs',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36-32\\lib',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36-32',
 'C:\\Users\\user\\AppData\\Local\\Programs\\Python\\Python36-32\\lib\\site-packages']
Server time:    Wed, 8 Mar 2017 09:40:42 +0000

My Game and GameCategory models are as follows:

class GameCategory(models.Model):
    name = models.CharField(max_length=200)

    class Meta:
        ordering = ('name',)

    def __str__(self):
        return self.name

class Game(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    name = models.CharField(max_length=200, blank=True, default='')
    game_category = models.ForeignKey(GameCategory, related_name='games', on_delete=models.CASCADE)
    release_date = models.DateTimeField()
    game_category = models.CharField(max_length=200, blank=True, default='')
    played = models.BooleanField(default=False)

    class Meta:
        ordering = ('name',)
    def __str__(self):
        return self.name

and the snippet of my urls.py:

urlpatterns = [
    url(r'^game-categories/$', views.GameCategoryList.as_view(),name=views.GameCategoryList.name),
    url(r'^game-categories/(?P<pk>[0-9]+)$', views.GameCategoryDetail.as_view(), name=views.GameCategoryDetail.name),

views.py:

from games.models import GameCategory
from games.models import Game
from games.models import Player
from games.models import PlayerScore    
from games.serializers import GameCategorySerializer
from games.serializers import GameSerializer
from games.serializers import PlayerSerializer
from games.serializers import PlayerScoreSerializer
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.reverse import reverse

class GameCategoryList(generics.ListCreateAPIView):
    queryset=GameCategory.objects.all()
    serializer_class=GameCategorySerializer
    name='gamecategory-list'

class GameCategoryDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset=GameCategory.objects.all()
    serializer_class = GameCategorySerializer
    name = 'gamecategory-detail'

class GameList(generics.ListCreateAPIView):
    queryset=Game.objects.all()
    serializer_class = GameSerializer
    name = 'game-detail'

class GameDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset=Game.objects.all()
    serializer_class=GameSerializer
    name='game-detail'

serializers.py

from rest_framework import serializers
from games.models import GameCategory
from games.models import Game
from games.models import Player
from games.models import PlayerScore
import games.views

class GameCategorySerializer(serializers.HyperlinkedModelSerializer):
    games = serializers.HyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name='game-detail'
    )

    class Meta:
        model = GameCategory
        fields = ('url','pk','name','games')

class GameSerializer(serializers.HyperlinkedModelSerializer):
    #We want to display the game categorys name instead of the id
    game_category = serializers.SlugRelatedField(queryset=GameCategory.objects.all(), slug_field='name')

    class Meta:
        model = Game
        fields = (
            'url',
            'game_category',
            'name',
            'release_date',
            'played'
        )

I’m still very new to Python, let alone Django, so I am struggling to source the error here from the debug information given.

Can anyone help me understand where the problem is and why?

1 Answer 1

1

How about removing HyperlinkedModelSerializer from GameCategorySerializer?

class GameCategorySerializer(serializers.ModelSerializer):
    # rest here unchanged

Check here for more.

[UPDATE]: You have a duplication inside the Game model!

game_category = models.ForeignKey(GameCategory, related_name='games', on_delete=models.CASCADE)

and

game_category = models.CharField(max_length=200, blank=True, default='')

You should rename the second game_category field to something else. Thus, the error you'are getting. GameCategorySerializer tries to handle this field (which does not have a related_name attribute) instead of the first (ForeignKey) one.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks, but unfortunately it made no difference. Exact same error.
I think I found it... Updated my answer!
You're right. It was indeed a duplicate. How did you deduce that from the error though? I have another error now (no such column: games_game.game_category_id) but would prefer to know how to debug these myself. Any ideas? Thanks
I didn't resolve it from the error. I just copy pasted your code into my IDE and instantly highlighted it! You should use an IDE too. It makes dev, so much easier! Concering the other error, it seems like the database does not have the column game_category_id under the table games_game (classic DB error). So, what is the solution? Poke me, if you need it!
Awesome. Thanks. Deleted my migrations folder (and DB), reran the migrations and its working now. Any recommendations on a (free) IDE for Python? Lightweight if possible. Cheers
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.