6.6 Alterando Modelos

Vamos caminhar agora para a solução do nosso problema mais complicado que é o de lidar com diferentes URLs para usuários distintos.

Iniciamos alterando nossos testes unitários para lidar com o que precisamos. O novo conjunto de teste unitário ficou conforme abaixo. Incluí um # ao final de cada linha que foi incluída.

from django.urls import resolve
from django.test import TestCase
from lists.views import home_page
from lists.models import Item, List #

class HomePageTest(TestCase):

	def test_root_url_resolves_to_home_page_view(self):
		found = resolve('/')
		self.assertEquals(found.func, home_page)

	def test_home_page_returns_correct_html(self):
		response = self.client.get('/')
		self.assertTemplateUsed(response, 'home.html')

	def test_only_saves_items_when_necessary(self):
		self.client.get('/')
		self.assertEquals(Item.objects.count(), 0)


class NewListTest(TestCase):

	def test_can_save_a_POST_request(self):
		self.client.post('/lists/new', data={'item_text': 'A new list item'})
		self.assertEquals(Item.objects.count(), 1)
		new_item = Item.objects.first()
		self.assertEquals(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/')


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_list_itens(self):
		Item.objects.create(text='itemey 1')
		Item.objects.create(text='itemey 2')

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

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


class ListAndItemModelTest(TestCase): #

	def test_saving_and_retriving_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.assertEquals(saved_list, list_) #

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

		first_saved_item = saved_items[0]
		second_saved_item = saved_items[1]

		self.assertEquals(first_saved_item.text, 'The first (ever) list item')
		self.assertEquals(first_saved_item.list, list_)	#
		self.assertEquals(second_saved_item.text, 'Item the second')
		self.assertEquals(second_saved_item.list, list_) #

Outra forma de ver as alterações que Percival utilizou em seu livro, foi por meio do git diff, conforme ilustrado abaixo. Linhas com + foram adicionadas

Basicamente as alterações incluem a criação de um objeto List; a atribuição de cada item a esse objeto por meio da propriedade .list. Em seguida, fizemos o teste se a lista foi salva e se os dois itens salvaram seu relacionamento com a lista.

Elaborado o teste, podemos executá-lo para iniciar o ciclo teste de unidade/código.

Para corrigir, precisamos implementar a classe List em lists/models.py, conforme abaixo:

Ao reexecutar os testes obtemos o seguinte erro:

Pela mensagem é um erro relacionado com a base de dados. Lembre-se que quando mudamos o modelo é necessário que façamos o migration para que as informações das tabelas do banco de dados sejam atualizadas a contento.

Feito isso o resultados dos testes passam a ser conforme abaixo:

O erro indica que não temos ainda o atributo .list na classe Item. Podemos incluí-lo da mesma forma que fizemos com o atributo .text.

Como alteramos novamente o modelo, nossos testes, se forem executados, iram nos lembrar que precisamos fazer uma migração.

Executada a migração agora nossos testes apresentam o erro abaixo:

Aparentemente, o tipo de dado do atributo .list. Como queremos estabelecer um relacionamento entre duas classes, o Django oferece um tipo de dado denominado ForeignKey e podemo mudar o tipo do atributo .list conforme mostrado abaixo. Mais informações sobre esse tipo de dado pode ser encontrada em https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.ForeignKeyarrow-up-right.

Alterado o modelo devemos fazer a migração novamente. Entretanto, como a anterior não foi bem sucedida podemos removê-la antes. Cuidado, entretanto, ao remover migrações. Só o faça se as mesmas ainda não foram usadas.

Adaptando o Restante do Código ao Novo Modelo

Feita a correção e a migração acima, ao executar os testes novamente temos as seguintes mensagens de erro:

Ao todo, temos três erros de violação de restrição do tipo NOT_NULL. Como incluímos uma dependência entre Items e Lists, agora cada item tem que ter uma lista-pai associada a ele e, por isso, três de nossos testes unitários falharam.

O primeiro passo então é corrigir nossos testes unitários conforme abaixo. Veja as linhas 42 a 44 na qual é feita a associação entre o item e a lista.

Com isso, nossos testes avanças e ficam restando dois com falha, ambos relacionados ao POST para a inclusão de um novo item e nossa função de view que faz o tratamento a requisição também não faz a associação do item com um lista. A correção é dada nas linhas 13 e 14 abaixo:

Com isso, ao executar os testes de unidade obtemos sucesso na execução.

Novamente, mesmo tendo um resultado de sucesso nesse momento, Percival (2017)arrow-up-right nos lembra de que o desenvolvimento, da forma como estamos fazendo, parece ser contraintuitiva. Criar uma lista a cada item sendo incluído parece estranho. Entretanto, ele nos alerta que mesmo dando vontade fazer tudo de uma única vez e já chegar ao código funcional que consideramos correto, é necessário seguir os passos do TDD. Ele nos lembra que devemos seguir o "Testing Goat!" ou nosso Bode dos Testes.

"Quando você estiver no alto de uma montanha, vai querer pensar cuidadosamente no lugar em que colocará cada pé, e dará um passo de cada vez, verificando, em cada etapa, se o local em que pisou não fará você cair de um penhasco." (Pervcival, 2017arrow-up-right)

Desse modo, para nos assegurar que está tudo bem, ao executar nossos testes funcionais podemos observar que chagamos a um estado já conhecido e consistente.

Após as mudanças e a chegada a um ponto consistente com o que já tínhamos é um bom momento para colocarmos as mudanças sob controle de versão.

Ao verificar nossa lista de itens a cumprir, podemos riscar mais um.

Last updated