2010-09-10 1 views
17

У меня есть приложение django, которое в основном представляет собой просто фотоальбом. Сейчас у меня две модели: Image и Album. Среди прочего, каждый Album имеет внешний ключ для Image, чтобы быть его миниатюром, и каждый Image имеет внешний ключ к Album, к которому он принадлежит. Однако, когда я пытаюсь использовать manage.py syncdb или manage.py sqlall, я получаю ошибки, говорящие, что класс, не определенный первым в models.py не определяется, когда он используется в первом классе.Django models.py Circular Foreign Key

models.py (в сокращенном виде):

from django.db import models 
import os 

class Album(models.Model): 
    thumb = models.ForeignKey(Image, null=True, blank=True) 

class Image(models.Model): 
    image = models.ImageField(upload_to='t_pics/images') 
    thumb = models.ImageField(upload_to='t_pics/images/thumbs') 
    album = models.ForeignKey(Album) 

Ошибка я получаю, когда я manage.py sqlall appname:

[...] 
File "/path/to/file/appname/models.py", line 4, in ? 
    class Album(models.Model): 
    File "/path/to/file/appname/models.py", line 5, in Album 
    thumb = models.ForeignKey(Image, null=True, blank=True) 
NameError: name 'Image' is not defined 

я получаю ту же ошибку, когда я изменить порядок классов в models.py кроме этого говорит 'Album' undefined вместо 'Image' undefined Я также пробовал комментировать зависимость в первом классе, а затем раскомментировать после того, как все остальное было успешно импортировано, но это не помогло. Как я должен заниматься этой работой? Я не хочу делать весь третий класс Thumb, потому что у него будет много того же кода, что и Image. Я также уверен, что могу вручную добавить внешний ключ в базу данных, но я хочу, чтобы это было чистым, а не хакерским.

ответ

38

У вас фактически нет циркулярной ссылки; проблема в том, что в то время, когда вы определяете альбом, вы еще не определили изображение. Вы можете исправить это с помощью строки вместо:

class Album(models.model): 
    thumb = models.ForeignKey('Image', null=True, blank=True) 

Однако, в этом случае, вы можете использовать OneToOneField вместо внешнего ключа. (Обратите внимание, что вам все равно придется использовать трюк со строкой).

+1

Ваше решение сработало, но мне также пришлось добавить 'related_name = 'Image'' в параметры для определения' Album.thumb'. Каким будет преимущество OneToOneField над ForeignKey? – Puddingfox

+1

OneToOneField обеспечивает уникальное ограничение для внешнего ключа. Кроме того, вам нужно только установить его на одном из классов, а другой получить ссылку «бесплатно». – mipadi

+0

Кстати, обратная связь предоставляется бесплатно с ForeignKey, но она возвращает запрос, а не один элемент. – Ilja

11

Используйте кавычки, чтобы заставить ленивую ссылку:

models.ForeignKey('Image', null=True, blank=True) 

Кроме того, ForeignKey.related_name является вашим другом (избегает обратных ссылок столкновений имен).

+0

Правильнее, но я сначала ответил mipadi :( – Puddingfox

+2

@puddingfox вы понимаете, что можете изменить выбранный правильный ответ? – Chris

+0

@Kevin : Я предполагаю, что вы знакомы с языком currying, например JavaScript, но в Python вам нужно объявить переменную перед созданием ссылки (этот трюк «make-it-a-string» - это соглашение Django для обработки этого факта). Переменная currying может быть запутанным - если у вас есть время погружения в пространства имен Python, это одна из моих любимых особенностей языка в Python. –

0

Это старый, но в любом случае я хотел бы сказать, что я не вижу причины для альбома атрибутов в модели Image. На мой взгляд, это действительно не нужно.