Книга
неплохая но есть и некоторые нестыковки. Необычно выглядит использование __str__ в моделях вместо настоятельно рекомендуемого __unicode__.(см.
Choosing between __str__() and __unicode__()) Книга показалась интересной поэтому продолжил чтение сверяя код из книги с документацией. Вскоре наткнулся на следущую незавершенность.
Вот модель которую предлагает автор(после замены __str__ на __unicode__):
class Link(models.Model):
url = models.URLField(unique=True)
def __unicode__(self):
return self.url
class Bookmark(models.Model):
title = models.CharField(max_length=200)
user = models.ForeignKey(User)
link = models.ForeignKey(Link)
def __unicode__(self):
return '%s, %s' % (self.user.username, self.link.url)
class Tag(models.Model):
name = models.CharField(max_length=64, unique=True)
bookmarks = models.ManyToManyField(Bookmark)
def __unicode__(self):
return self.name
Неопытный читатель вроде меня ничего бы не заметил если бы не одна особенность Джанго которая показалась не совсем удобной и из-за которой захотелось копнуть глубже. Вот она:
>>> dir(Bookmark)
['DoesNotExist', 'MultipleObjectsReturned', '__class__', '__delattr__', '__dict_
_', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__
', '__metaclass__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_e
x__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_
_unicode__', '__weakref__', '_collect_sub_objects', '_default_manager', '_get_FI
ELD_display', '_get_next_or_previous_by_FIELD', '_get_next_or_previous_in_order'
, '_get_pk_val', '_meta', '_set_pk_val', 'delete', 'link', 'objects', 'pk', 'sav
e', 'save_base', 'tag_set', 'user']
>>> dir(Tag)
['DoesNotExist', 'MultipleObjectsReturned', '__class__', '__delattr__', '__dict_
_', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__
', '__metaclass__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_e
x__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_
_unicode__', '__weakref__', '_collect_sub_objects', '_default_manager', '_get_FI
ELD_display', '_get_next_or_previous_by_FIELD', '_get_next_or_previous_in_order'
, '_get_pk_val', '_meta', '_set_pk_val', 'bookmarks', 'delete', 'objects', 'pk',
'save', 'save_base']
>>>
Есть что-то нелогичное в разнице между Tag.bookmarks и Bookmark.tag_set. Почему бы не реализовать повсюду одинаково - Tag.bookmark_set ? (Смутно припоминаю что в RoR тоже не очень приятная история со множественным числoм существительных. )
У нас есть Bookmark и есть Tag. Согласно
докам : "It doesn't matter which model gets the ManyToManyField, but you only need it in one of the models -- not in both."
Однако затем читаем:
"Generally, ManyToManyField instances should go in the object that's going to be edited in the admin interface, if you're using Django's admin. In the above example, toppings is in Pizza (rather than Topping having a pizzas ManyToManyField ) because it's more natural to think about a pizza having toppings than a topping being on multiple pizzas."
У нас хоть и ManyToMany, но таки натуральней думать что Букмарк имеет Тэги а не наоборот.