Общий ответ: +=
пытается вызвать специальный метод __iadd__
, а если он недоступен, он пытается использовать __add__
. Таким образом, проблема заключается в различии между этими специальными методами.
Специальный метод __iadd__
предназначен для дополнения на месте, то есть он мутирует объект, на котором он действует. Специальный метод __add__
возвращает новый объект и также используется для стандартного оператора +
.
Поэтому, когда оператор +=
используется для объекта, у которого __iadd__
определен объект изменен на месте. В противном случае вместо этого попытается использовать plain __add__
и вернуть новый объект.
Вот почему для изменяемых типов, таких как списки +=
, изменяется значение объекта, тогда как для неизменяемых типов, таких как кортежи, строки и целые числа вместо этого возвращаются новый объект (a += b
становится эквивалентным a = a + b
).
Для типов, которые поддерживают как __iadd__
, так и __add__
, вы должны быть осторожны, какой из них вы используете. a += b
вызывается __iadd__
и мутирует a
, тогда как a = a + b
создаст новый объект и назначит его a
. Это не то же самое действие!
>>> a1 = a2 = [1, 2]
>>> b1 = b2 = [1, 2]
>>> a1 += [3] # Uses __iadd__, modifies a1 in-place
>>> b1 = b1 + [3] # Uses __add__, creates new list, assigns it to b1
>>> a2
[1, 2, 3] # a1 and a2 are still the same list
>>> b2
[1, 2] # whereas only b1 was changed
Для неизменяемых типов (где у вас нет __iadd__
) a += b
и a = a + b
эквивалентны. Это позволяет использовать +=
для неизменяемых типов, что может показаться странным конструктивным решением, пока вы не подумаете, что в противном случае вы не могли бы использовать +=
для неизменяемых типов, таких как числа!
Объект в вашем тестовом методе не обновлен. Вы можете использовать метод refresh_from_db
для обновления после изменений:
def test_can_publish_article_from_POST(self):
other_article_two = Article.objects.create(name='Test Name One', author=User.objects.get(email='testuser@gmail.com'))
correct_article_two = Article.objects.create(name='Test Name Two', author=User.objects.get(email='testuser@gmail.com'))
response = self.client.post(reverse('publish_article', kwargs={'pk' : correct_article_two.pk}))
correct_article_two.refresh_from_db() # Add this line
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, f'/articles/{correct_article_two.pk}/')
self.assertEqual(correct_article_two.published, True)