0

Before I get the obvious response, about checking the database, itself, I'll start by saying that I've already checked out this post, which has a nearly identical setup to mine, and the solutions of deleting the database and migrations, and adding a default value in the table did not work, as expected. I do, however, expect the solution to be quite simple.

So, that said, I'm doing the tutorial for django-rest-framework and my problem started on part 4. The tutorial says as follows:

Now if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again.

Once you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet pks that are associated with each user, in each user's 'snippets' field.

So, I tried to create the "snippets" objects with the manage.py shell, as in the first part of the tutorial, with the following code:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer

snippet = Snippet(code='foo = "bar"\n')
snippet.save()

And that's where it ends. .save() triggers the error, which I've printed the traceback for, below.

Using the advice from the previously answered question, I've very slightly changed my setup, but I'm still getting the error. Here's the setup:

models.py:

from django.db import models

from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight


LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())


class Snippet(models.Model):
    owner = models.ForeignKey('auth.User', related_name='snippets')
    highlighted = models.TextField(default='')
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly',max_length=100 )


    class Meta:
        ordering = ('created',)

    def save(self, *args, **kwargs):
        lexer = get_lexer_by_name(self.language)
        linenos = self.linenos and 'table' or False
        options = self.title and {'title': self.title} or {}
        formatter = HtmlFormatter(style=self.style, linenos=linenos,
                                  full=True, **options)
        self.highlighted = highlight(self.code, lexer, formatter)
        super(Snippet, self).save(*args, **kwargs)

serializers.py:

from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES,STYLE_CHOICES
from django.contrib.auth.models import User


class SnippetSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')

    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style', 'owner')


class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())

    class Meta:
        model = User
        fields = ('id', 'username', 'snippets')

views.py:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer, UserSerializer
from rest_framework import generics
from django.contrib.auth.models import User
from rest_framework import permissions


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)


class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

/snippets/urls.py

from django.conf.urls import url, include
from snippets.views import SnippetList, SnippetDetail, UserList, UserDetail
from rest_framework.urlpatterns import format_suffix_patterns


urlpatterns = [

    url(r'^snippets/$', SnippetList.as_view()),
    url(r'^snippets/(?P<pk>[0-9]+)/$', SnippetDetail.as_view()),
    url(r'^users/$', UserList.as_view()),
    url(r'^users/(?P<pk>[0-9]+)/$', UserDetail.as_view()),

]

urlpatterns = format_suffix_patterns(urlpatterns)
urlpatterns += [
    url(r'^api-auth/', include('rest_framework.urls',
                               namespace='rest_framework')),
]

urls.py:

from django.conf.urls import url, include

urlpatterns = [
    url(r'^', include('snippets.urls')),
]

and, lastly, the ugly traceback:

In [6]: snippet.save()
    ---------------------------------------------------------------------------
    OperationalError                          Traceback (most recent call last)
    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\backends\utils.py in execute(self, sql, params)
         63             else:
    ---> 64                 return self.cursor.execute(sql, params)
         65

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\backends\sqlite3\base.py in execute(self, query, params)
        322         query = self.convert_query(query)
    --> 323         return Database.Cursor.execute(self, query, params)
        324

    OperationalError: table snippets_snippet has no column named owner_id

    The above exception was the direct cause of the following exception:

    OperationalError                          Traceback (most recent call last)
    <ipython-input-6-fe28bd3dc796> in <module>()
    ----> 1 snippet.save()

    D:\GitHub Repositories\Django\tutorial\snippets\models.py in save(self, *args, **kwargs)
         34                                   full=True, **options)
         35         self.highlighted = highlight(self.code, lexer, formatter)
    ---> 36         super(Snippet, self).save(*args, **kwargs)

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\models\base.py in save(self, force_insert, force_update, using, update_fields)
        698
        699         self.save_base(using=using, force_insert=force_insert,
    --> 700                        force_update=force_update, update_fields=update_fields)
        701     save.alters_data = True
        702

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\models\base.py in save_base(self, raw, force_insert, force_update, using, update_fields)
        726             if not raw:
        727                 self._save_parents(cls, using, update_fields)
    --> 728             updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
        729         # Store the database on which the object was saved
        730         self._state.db = using

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\models\base.py in _save_table(self, raw, cls, force_insert, force_update, using, update_fields)
        810
        811             update_pk = bool(meta.has_auto_field and not pk_set)
    --> 812             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
        813             if update_pk:
        814                 setattr(self, meta.pk.attname, result)

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\models\base.py in _do_insert(self, manager, using, fields, update_pk, raw)
        849         """
        850         return manager._insert([self], fields=fields, return_id=update_pk,
    --> 851                                using=using, raw=raw)
        852
        853     def delete(self, using=None, keep_parents=False):

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\models\manager.py in manager_method(self, *args, **kwargs)
        120         def create_method(name, method):
        121             def manager_method(self, *args, **kwargs):
    --> 122                 return getattr(self.get_queryset(), name)(*args, **kwargs)
        123             manager_method.__name__ = method.__name__
        124             manager_method.__doc__ = method.__doc__

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\models\query.py in _insert(self, objs, fields, return_id, raw, using)
       1037         query = sql.InsertQuery(self.model)
       1038         query.insert_values(fields, objs, raw=raw)
    -> 1039         return query.get_compiler(using=using).execute_sql(return_id)
       1040     _insert.alters_data = True
       1041     _insert.queryset_only = False

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\models\sql\compiler.py in execute_sql(self, return_id)
       1058         with self.connection.cursor() as cursor:
       1059             for sql, params in self.as_sql():
    -> 1060                 cursor.execute(sql, params)
       1061             if not (return_id and cursor):
       1062                 return

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\backends\utils.py in execute(self, sql, params)
         77         start = time()
         78         try:
    ---> 79             return super(CursorDebugWrapper, self).execute(sql, params)
         80         finally:
         81             stop = time()

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\backends\utils.py in execute(self, sql, params)
         62                 return self.cursor.execute(sql)
         63             else:
    ---> 64                 return self.cursor.execute(sql, params)
         65
         66     def executemany(self, sql, param_list):

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\utils.py in __exit__(self, exc_type, exc_value, traceback)
         93                 if dj_exc_type not in (DataError, IntegrityError):
         94                     self.wrapper.errors_occurred = True
    ---> 95                 six.reraise(dj_exc_type, dj_exc_value, traceback)
         96
         97     def __call__(self, func):

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\utils\six.py in reraise(tp, value, tb)
        683             value = tp()
        684         if value.__traceback__ is not tb:
    --> 685             raise value.with_traceback(tb)
        686         raise value
        687

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\backends\utils.py in execute(self, sql, params)
         62                 return self.cursor.execute(sql)
         63             else:
    ---> 64                 return self.cursor.execute(sql, params)
         65
         66     def executemany(self, sql, param_list):

    C:\Users\Jordon\AppData\Local\Programs\Python\Python35\lib\site-packages\django\db\backends\sqlite3\base.py in execute(self, query, params)
        321             return Database.Cursor.execute(self, query)
        322         query = self.convert_query(query)
    --> 323         return Database.Cursor.execute(self, query, params)
        324
        325     def executemany(self, query, param_list):

    OperationalError: table snippets_snippet has no column named owner_id
Community
  • 1
  • 1
Jordon Birk
  • 480
  • 1
  • 9
  • 28
  • Have you tried running `makemigrations` and `migrate`? – vmonteco Apr 09 '16 at 13:21
  • @vmonteco - Yes. As previously stated in my question, I ran the migrations, initially, with no problem until I hit the .save() command in the shell. Then I deleted the migrations and the database, and I ran them again, only to have the same problem happen again. – Jordon Birk Apr 09 '16 at 13:26
  • Could you try `./manage.py makemigrations ` and then migrate? – vmonteco Apr 09 '16 at 14:03
  • I've already done that. There are no migrations to apply. – Jordon Birk Apr 09 '16 at 14:15
  • Even if you pass the name of your app as an argument to `./manage.py makemigrations`? (`./manage.py makemigrations main` for instance.) – vmonteco Apr 09 '16 at 14:17
  • Yes. Even if I pass the app name. – Jordon Birk Apr 09 '16 at 14:21
  • You added `"id"` to fields in **SnippetSerializer**. But there is no `id` field in your `Snippet` class. Is that normal? – vmonteco Apr 09 '16 at 14:25
  • 1
    Yes @vmonteco. id is automatically added unless you explicitly specify a primary key. – Abhinav Apr 09 '16 at 19:22
  • @AbhinavI You're right, my bad. I never used id and thought there only was pk. I rode some articles to have further informations. – vmonteco Apr 09 '16 at 19:33

2 Answers2

1

First of all, you have not provided a value for 'owner' while saving a Snippet object. You need to do something like this:

from django.contrib.auth.models import User
new_user = User.objects.create(...)
snippet = Snippet(owner=new_user, code='foo = "bar"\n')
snippet.save()

Nevertheless, it doesn't explain why the owner_id column was not created. Can you change the model to something like this and see if it detects the owner column?

from django.contrib.auth.models import User

class Snippet(models.Model):
    owner = models.ForeignKey(User, related_name='snippets')
    ...

Then run these steps to attempt to create the column.

python manage.py makemigrations snippets
python manage.py migrate
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Abhinav
  • 622
  • 6
  • 15
  • Thanks. This solved the problem of adding an owner_id to the snippets table, but, it may have created another problem, which is that the endpoint defined in `/snippets/urls.py` seems to not be working, which might be a consequence of the change in the model: The url: `url(r'^users/$', UserList.as_view())`, as well as the UserDetail url, are returning `AttributeError at /users/1/ 'User' object has no attribute 'snippets'`, which indicates that the serializers aren't doing their job. Maybe `OnetoOneField(User)` is the wrong choice. – Jordon Birk Apr 09 '16 at 20:03
  • 1
    Oh, my bad, I removed the related_name='snippets' in the model change. By default, if you don't give a related_name, django uses 'somefieldname_set' for reverse linking of 'somefieldname'. So in UserSerializer, change 'snippets' to 'snippets_set' and it should work fine. – Abhinav Apr 09 '16 at 20:14
  • Wait, just a second - if I put `related_name='snippets'` back in, should that work, or does changing the model field prevent that from working? And, just to be sure, could you update your response? – Jordon Birk Apr 09 '16 at 20:19
  • Ah yes. That should work. It does not require a migration as well. – Abhinav Apr 09 '16 at 20:22
  • 1
    Appears now I'm getting `TypeError at /users/ 'Snippet' object is not iterable`. To the UserSerializer I go. – Jordon Birk Apr 09 '16 at 20:29
  • 1
    Oh no. It appears that I have made a mistake again. In this model, we have defined a onetoonefield - saying that a user can have only one snippet and vice versa. But that's not the case. A user can have many snippets(many=True). Please change the owner field to be a ForeignKey and run makemigration/migrate. I apologize for misleading. – Abhinav Apr 09 '16 at 20:41
  • 1
    Got it. Thanks again, Abhi. I feel like I have much better understanding of what's going on in the inside, now. – Jordon Birk Apr 09 '16 at 20:46
  • Does anyone know why `owner = models.ForeignKey('auth.User', related_name='snippets')` doesn't work? I need to specifically use `django.contrib.auth.models.User` to get this working. Django 2.2 (not by choice, blame the company) – stonecharioteer Oct 13 '21 at 14:41
0

It looks like you didn't delete the original database.

The tutorial removes it after the model is changed:

rm -f tmp.db db.sqlite3
rm -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate

If you did remove none of the tmp.db or db.sqlite3 then Django might think it has already done the migration and will not redo it.

Make sure you find one of the two aforementioned files and remove them and then run the above script (plus the createsuperuser for you to login in).

Linovia
  • 19,812
  • 4
  • 47
  • 48
  • The initial way I deleted was dragging to the trash, not in `cmd`, but, I tried your method (without the `-f` flag, because I'm in windows) and it still didn't work. The same error at the `snippet.save()` in the manage.py shell. – Jordon Birk Apr 09 '16 at 15:04
  • and you did remove the content of the snippets/migrations directory too ? – Linovia Apr 09 '16 at 15:47
  • Yes. I've done this several times and continue to get the same error. Either something is fishy with the model, itself, or with the save() method. I'm running python 3.5 and django 1.9. – Jordon Birk Apr 09 '16 at 15:55