Можете ли вы объяснить странное поведение Django ManyToManyField?

#django #sqlite #django-models #transactions #many-to-many

#django #sqlite #django-модели #транзакции #многие ко многим

Вопрос:

У меня есть пара связанных моделей, которые выглядят примерно так:

 class Book(models.Model):
    title = models.TextField()

class Author(models.Model):
    """
    >>> b = Book(title='some title')
    >>> b.save()
    >>> a = Author(name='some name')
    >>> a.save()
    >>> a.books.add(b)
    >>> b in a.books.all()
    True
    """
    name = models.TextField()
    books = models.ManyToManyField(Book)
  

Эта версия является упрощением моего производственного приложения, но тот же тест в рабочей среде завершается неудачей — a.books.all () возвращает пустой список, даже после того, как я выполняю a.books.add (b).

Я заглянул в базу данных (sqlite), и в таблице объединения определенно была создана новая запись, book_author. Я также пытался вызвать transaction.commit() и connection.close(), чтобы попытаться обновить представление базы данных. Никакой радости. Любые указания на то, какие вещи могут вызывать странное поведение в процессе производства, будут с благодарностью приняты.

Одна из моих мыслей заключалась в том, что это как-то связано с третьей связанной моделью, для которой я вручную указал сквозную таблицу, что-то вроде этого:

 class Genre(models.Model): 
    desc = models.TextField() 
    books = models.ManyToManyField(Book, through='BookGenres') 

class BookGenres(models.Model): 
    book = models.ForeignKey(Book) 
    genre = models.ForeignKey(Genre)
  

Однако добавление этого в тестовое приложение ничего не нарушает… На что еще мне следует обратить внимание?

[правка 11/5] еще более странное поведение, следуя совету Дэниела в комментариях (спасибо за попытку! 🙂

Еще более странное поведение:

 >>>a.books.all()
[]
>>>a.books.filter(pk=b.id)
[]
>>>a.books.filter(pk=b.id).count()
1
>>>len(a.books.filter(pk=b.id))
0
  

Как я уже сказал, мои «реальные» модели более сложные, и я не смог воспроизвести это поведение в упрощенных тестах, но любые идеи о том, на что обратить внимание, были бы с благодарностью оценены.

Ответ №1:

Я не уверен, что in оператор обязательно работает в наборах запросов. Не забывайте, что экземпляры модели Django не имеют идентификаторов, поэтому два объекта, загруженные из базы данных в ходе двух отдельных операций, могут иметь разные внутренние идентификаторы, даже если у них одинаковый pk.

Я бы явно запросил элемент, который вы ожидаете :

 >>> a.books.add(b)
>>> a.books.filter(pk=b.pk).count()
1
  

Я бы также добавил, что я не вижу смысла в этом тестировании. Операции с моделью Django очень хорошо охвачены его собственным набором тестов — вам следует зарезервировать свои модульные тесты для вашей собственной бизнес-логики.

Комментарии:

1. согласовано — объекты, представляющие один и тот же элемент БД, могут иметь разные внутренние идентификаторы python, но сравниваются одинаково, потому что эквалайзер работает при сравнении pk

2. и я бы не писал тесты для низкоуровневых операций модели django, если бы они вели себя так, как должны были!