21

I have a django app which basically is just a photo album. Right now I have two models: Image and Album. Among other things, each Album has a foreign key to an Image to be its thumbnail and each Image has a foreign key to the Album it belongs in. However, when I try to use manage.py syncdb or manage.py sqlall I get errors saying the class not defined first in models.py isn't defined when it is used in the first class defined.

models.py (abridged):

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)

Error I get when I do 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

I get the same error when I switch the order of the classes in models.py except it says 'Album' undefined instead of 'Image' undefined I also tried commenting the dependancy in the first class then uncommenting after everything else was successfully imported but that didn't help. How should I go about making this work? I'm reluctant to make an entire third class Thumb because it will have a lot of the same code as Image I'm also pretty sure I could manually add the foreign key to the database but I want this to be clean and not hackish.

jamesbtate
  • 1,359
  • 3
  • 19
  • 25

3 Answers3

45

You don't actually have a circular reference; the issue is that, at the time you define Album, you haven't defined Image yet. You can fix that by using a string instead:

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

However, in this case, you might want to use a OneToOneField instead of a foreign key. (Note that you'll still have to use the trick with the string, though).

mipadi
  • 398,885
  • 90
  • 523
  • 479
  • 2
    Your solution worked but I also had to add `related_name='Image'` to the parameters for the `Album.thumb` definition. What would the advantage of a OneToOneField be over a ForeignKey? – jamesbtate Sep 10 '10 at 06:05
  • 1
    OneToOneField enforces a unique restriction on the foreign key. Also, you only have to set it on one of the classes, and the other gets the back-link "for free". – mipadi Sep 10 '10 at 12:50
  • btw, the backlink is for free with the ForeignKey also, but it returns a queryset and not a single item – Ilja Feb 07 '17 at 14:54
13

Use quotes to force a lazy reference:

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

Also, ForeignKey.related_name is your friend (avoids back-reference name clashes).

Paulo Scardine
  • 73,447
  • 11
  • 124
  • 153
  • 2
    @puddingfox you realize you can change the selected correct answer? – Chris Jan 02 '13 at 19:59
  • @Kevin: I guess you are familiarized with a currying language like JavaScript, but in Python you have to declare a variable before making a reference (this 'make-it-a-string' trick is a Django convention to handle this fact). Variable currying can be confusing as well - if you have time dive into Python namespaces, it is one of my favorite language traits in Python. – Paulo Scardine Dec 30 '13 at 14:08
0

This is old but anyway, i'd like to say that I don't see a reason for attribute album in model Image. In my opinion, it is not really needed.

clime
  • 8,695
  • 10
  • 61
  • 82
  • I would disagree with you on that - even though it's now quite a while later. He want's his database to allow for there to be only one possible thumbnail for this album. The most natural way to express this is to let images to refer with an FK to an Album and for an album to refer to a single image with an FK. By having an FK on Album you automatically enforce there can only be one. The only alternative ways I can think of to model that would require some comparatively more complex constraints or additional logic in the application code itself to guarantee this. – Philipp Doerner Sep 09 '21 at 17:45