Chapter 6: I don't get an AttributeError: 'List' object has no attribute 'save'

2,994 views
Skip to first unread message

Paul Spiteri

unread,
Oct 11, 2014, 3:13:28 PM10/11/14
to obey-the-test...@googlegroups.com
Hi

Sorry for another question (hope you received my thanks for the previous one), but I had a couple of cracks at this - using git I wound back to a version that worked in line with (your great) book and started from there twice and I got the same difference twice, which eventually led me to a hole I couldn't get out of, so I thought I'd pipe up.

Anyhoo, just after the Adjusting Our Models section of Chapter 6 you leave use (i.e. your readers) to our own devices to add some code to correct the unit test errors that present themselves.

The first error is ...

ImportError: cannot import name 'List'

... to which I added some code - 

# models.py

class List(models.Model):
    pass

I then got another error not listed in the book - 

django.db.utils.OperationalError: no such table: lists_list

So I figure I need to make a migration with - 

python manage.py makemigrations

Oh, python above is 3.4.1 - trust me, I just checked. Then I get the following error - 

AttributeError: 'Item' object has no attribute 'list'

See what happened here? I didn't get the "I don't get an AttributeError: 'List' object has no attribute 'save'" error. Now the first time I wen't through the exercise I just ignored this - I mean one less error ... that's great. But then I get further into the exercise and it all goes to pot. For example, in the 'XYZ' section of this chapter I get an error message ...

sqlite3.IntegrityError: NOT NULL constraint failed: lists_item.list_id

... rather than the expected ...

File "/workspace/superlists/lists/views.py", line 14, in new_list
Item.objects.create(text=request.POST['item_text'])
... and if I ignore the difference in that error message I end up with an error ...

lists.models.DoesNotExist: Item matching query does not exist.

And by this stage the whole thing has gone belly up. I think it has something to do with the fact that I don't receive the "'List' object has no attribute 'save'" error message.

I'll past my test in below, just in case it is useful - please bear in mind that the test below only goes up to the point where I get the "AttributeError: 'Item' object has no attribute 'list'" error .. because after that time I am pretty sure I have compromised it beyond all recognition.

Thanks again and hope you're having a great day (or had a great weekend, assuming you won't get to this till Monday, in whatever timezone your in) - Paul

from django.test import TestCase
from django.core.urlresolvers import resolve
from django.http import HttpRequest
from lists.views import home_page
from django.template.loader import render_to_string
from lists.models import Item, List


class HomePageTest(TestCase):
    def test_root_url_resolves_to_home_page_view(self):
        found = resolve('/')
        self.assertEqual(found.func, home_page)

    def test_home_page_returns_correct_html(self):
        request = HttpRequest()
        response = home_page(request)
        expected_html = render_to_string('home.html')
        self.assertEqual(response.content.decode(), expected_html)


class ListAndItemModelTest(TestCase):
    def test_saving_and_retrieving_items(self):
        list_ = List()
        list_.save()

        first_item = Item()
        first_item.text = 'The first (ever) list item'
        first_item.list = list_
        first_item.save()

        second_item = Item()
        second_item.text = 'Item the second'
        second_item.list = list_
        second_item.save()

        saved_list = List.objects.first()
        self.assertEqual(saved_list, list_)

        saved_items = Item.objects.all()
        self.assertEqual(saved_items.count(), 2)

        first_saved_item = saved_items[0]
        second_saved_item = saved_items[1]
        self.assertEqual(first_saved_item.text, 'The first (ever) list item')
        self.assertEqual(first_saved_item.list, list_)
        self.assertEqual(second_saved_item.text, 'Item the second')
        self.assertEqual(second_saved_item.list, list_)


class ListViewTest(TestCase):
    def test_uses_list_template(self):
        response = self.client.get('/lists/the-only-list-in-the-world')
        self.assertTemplateUsed(response, 'list.html')

    def test_displays_all_items(self):
        Item.objects.create(text='itemey 1')
        Item.objects.create(text='itemey 2')

        # request = HttpRequest()
        # response = home_page(request)
        response = self.client.get('/lists/the-only-list-in-the-world')

        self.assertContains(response, 'itemey 1')
        self.assertContains(response, 'itemey 2')

class NewListTest(TestCase):
    def test_saving_a_POST_request(self):
        self.client.post(
            '/lists/new',
            data={'item_text': 'A new list item'}
        )
        self.assertEqual(Item.objects.count(), 1)
        new_item = Item.objects.first()
        self.assertEqual(new_item.text, 'A new list item')


    def test_redirects_after_POST(self):
        response = self.client.post(
            '/lists/new',
            data={'item_text': 'A new list item'}
        )
        self.assertRedirects(response, '/lists/the-only-list-in-the-world/')





Agustín Scaramuzza

unread,
Oct 12, 2014, 12:17:32 PM10/12/14
to obey-the-test...@googlegroups.com
Can we see your whole models.py code?

Paul Spiteri

unread,
Oct 12, 2014, 10:14:33 PM10/12/14
to obey-the-test...@googlegroups.com
Sure - but it doesn't look much different from the example in the book because I am asking why I am not getting the "'List' object has no attribute 'save'" error message because I think it is that which is causing the problems I am getting later on in the exercise.

Thanks for taking the time to read my question - Paul 

from django.db import models

class List(models.Model):
    pass

class Item(models.Model):
    text = models.TextField(default='')
    list = models.ForeignKey(List, default=None)

Agustín Scaramuzza

unread,
Oct 13, 2014, 3:17:17 PM10/13/14
to obey-the-test...@googlegroups.com
Have you tried deleting all the migration files but the first one and running makemigrations again?

Paul Spiteri

unread,
Oct 18, 2014, 10:00:21 PM10/18/14
to obey-the-test...@googlegroups.com
Well what do you know - it worked. I don't understand why, but I am on my way now so no complaints from me.

Thanks heaps, AS, for helping out.

Harry Percival

unread,
Nov 3, 2014, 1:14:33 PM11/3/14
to Paul Spiteri, obey-the-test...@googlegroups.com
Hi Paul,

Just picking up on this, a couple of decades after you posted it I know, but still...

Here's what you said:

The first error is ...

ImportError: cannot import name 'List'

... to which I added some code - 

# models.py

class List(models.Model):
    pass

I then got another error not listed in the book - 

django.db.utils.OperationalError: no such table: lists_list

In fact the "no such table" error is absolutely listed in the book, but it's meant to be the next-but-one expected failure.  The reason you're not seeing "List has not attribute save" is because you've gone too fast. Your first step should have been

    class List(object):
        pass

But you inherited from models.Model straight away.  Not wrong necessarily, but smaller steps are the name of the game in this exercise...


--
You received this message because you are subscribed to the Google Groups "Obey the testing goat! Test-Driven Web Development with Python book" group.
To unsubscribe from this group and stop receiving emails from it, send an email to obey-the-testing-go...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
------------------------------
Harry J.W. Percival
------------------------------
Twitter: @hjwp
Mobile:  +44 (0) 78877 02511
Skype:         harry.percival

Paul Spiteri

unread,
Nov 5, 2014, 2:40:40 AM11/5/14
to obey-the-test...@googlegroups.com, pspi...@gmail.com, hj...@cantab.net
Hi Harry (the Australian in me wants to abbreviate that to H, but I appreciate this may not be everyone's cup of tea. Pun not intended.)

Thanks for picking this up. The only reason I flagged this in the first instance was that later ... a fair way ... down the track I got an error that I assumed was because of this one. In hindsight, this was probably hindsight.

My progress through the exercises have lagged a bit. I think my absorption of some testing principles and algorithms was getting lost on having to pick up some Django basics, which I think I've gone a long way to addressing now having whipped up a reasonably complex app over the weekend. I'll rebuild it TDD style, promise! One more week and I'll be back onto the exercises. 

More then.
To unsubscribe from this group and stop receiving emails from it, send an email to obey-the-testing-goat-book

For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages