Can you explain strange behaviour with Django ManyToManyField?
I have a couple of related models, that look a bit like this:
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)
This v开发者_如何转开发ersion is a simplification of my production app, but the same test in production fails - a.books.all() returns the empty list, even after I do an a.books.add(b).
I've looked in the database (sqlite), and a new entry has definitely been created in the joining table, book_author. I've also tried calling a transaction.commit(), and a connection.close() to try and refresh the view of the DB. No joy. Any pointers towards what sorts of things might be causing the strange behaviour in production would be gratefully received.
One thought I had was that it had something to do with a third related model, for which I've manually specified a through table, something like this:
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)
However, adding this to the test app doesn't break things... What else should I look for?
[edit 11/5] more weird behaviour, following on from advice from Daniel the comments (thanks for trying! :-)
More strange behaviour:
>>>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
As I say, my "real" models are more complex, and I haven't been able to replicate this behaviour in simplified tests, but any ideas for what to look at would be gratefully appreciated.
I'm not sure that the in
operator necessarily works in querysets. Don't forget that Django model instances don't have identity, so two objects loaded from the db in two separate operations can have different internal IDs even if they have the same pk.
I would explicitly query the item you're expecting :
>>> a.books.add(b)
>>> a.books.filter(pk=b.pk).count()
1
I'd also add that I don't see the point of this test. Django's model operations are very well covered by its own test suite - you should reserve your unit/doctests for your own business logic.
精彩评论