Reputation: 4131
I have something like this in models.py
class ZipCode(models.Model):
zip = models.CharField(max_length=20)
cities = City.objects.filter(zip=self).distinct()
class City(models.Model):
name = models.CharField(max_length=50)
slug = models.CharField(max_length=50)
state = models.ForeignKey(State)
zip = models.ManyToManyField(ZipCode)
When I do this I get:
NameError: name 'City' is not defined
Is this because the order of declaration matters? And if so, how can I do this, because either way I arrange this, it looks like I'm going to get a NameError.
Thanks.
Upvotes: 6
Views: 5405
Reputation: 126731
Apart from order issues, this is wrong:
cities = City.objects.filter(zip=self).distinct()
It is not inside a method, so "self" will also be undefined. It is executed only once, at class-creation time (i.e. when the module is first imported), so the attribute created would be a class attribute and have the same value for all instances. What you might be looking for is this:
@property
def cities(self):
return City.objects.filter(zip=self).distinct()
Because this is inside a method, which is not executed until it's accessed, ordering issues will no longer be a problem. As ozan points out, this is a duplication of what Django reverse relations already give you for free:
a_zip_code.city_set.all()
And you can use related_name to call it what you like:
zip = models.ManyToManyField(ZipCode, related_name='cities')
...
a_zip_code.cities.all()
So I don't think the ordering issue you originally asked about is even relevant to your situation. When it is, others have already pointed out using quoted strings in ForeignKey and ManyToManyField declarations to get around it.
Upvotes: 5
Reputation: 7615
Yes, order does matter, but your example does not look right to me. I think you should just be using a foreign key for your many-to-one relationship:
cities = models.ForeignKey(City)
This has the details on many-to-one relationships with django models.
Edit:
It was pointed out to me in the comments that cities in Europe might have several cities in the same zip code. If you are looking for a many-to-many relationship here, you should use:
cities = models.ManyToManyField(City)
This is described in Django's documentation. The point is, this is either of these examples are much more clear than what is used in the example.
Upvotes: 2
Reputation: 9331
Yes order does matter as others have noted.
Though, encountering this issue is almost always going to be an indication that you're doing something wrong.
In this case your declaration:
cities = City.objects.filter(zip=self).distinct()
... is both redundant and bad practice. You can find the cities related to a zip code by referring to that zip code's city_set in your views (ie not in your model!). So if zip is an instance of ZipCode, you would do:
cities = zip.city_set.all()
If you really want to call it 'cities' rather than 'city_set' you can use the related_name parameter in your m2m declaration.
Upvotes: 4
Reputation: 7471
I was once worried about order... because I thought my models below could only reference models above. But then realized that you can just do a
models.ForeignKey('appName.modelName')
and all was fine.
Upvotes: 4
Reputation: 147366
When you have references to classes defined after, you can use this trick:
attribute = models.ForeignKey('ClassDefinedAfterThis')
Upvotes: 5
Reputation: 7604
Order matters in Python. This thread may be relevant to your question. Also for your uses, you may want to use a unique foreign key in the ZIP code class.
Upvotes: 0