Reputation:
Hello Awesome People!
A simple question about a thing that I came across and understand the issue but can't figure out why?
Not only I do not know for example whether model1
must have the ForeignKey
to model2
, or model2
with a ManyToManyField
to model1
(Confused).
Let's suppose we have two(2) models files and three(3) class Model, app1.models.py
with [Model1
, ModelA
] and app2.models.py
with [Model2
].
app1.models.py
from app2.models import Model2
class Model1(models.Model):
field1 = models.ForeignKey(Model2)
app2.models.py
from app1.models import ModelA
class Model2(models.Model):
field2 = models.ForeignKey(ModelA)
This will definitely raise an error ImportError
,
But if I do:
class Model1(models.Model):
field1 = models.ForeignKey("app2.Model2")
class Model2(models.Model):
field2 = models.ForeignKey("app1.ModelA")
this will work well
Why exactly?
I Know that I can create the model in the file needed, but what I want is to understand why one works, and not the other.
Thank you!
Upvotes: 1
Views: 96
Reputation: 962
The notation
field1 = models.ForeignKey(Model2)
implies that "Model2" has already been created. But python create the two objects in a sequence. You get this error when python creates ModelA, which try to point on ModelB, which is not existing yet. You can solve this issue by using
field1 = models.ForeignKey("app2.Model2")
You can also see this in the Django documentation
If you need to create a relationship on a model that has not yet been defined, you can use the name of the model, rather than the model object itself.
Upvotes: 1
Reputation: 477598
import
statements are in fact executable statements. If you ask to import foo.bar.qux
, then Python will first check if it has imported the file already (in sys.modules
).
In case it has not yet imported that module, it will first perform a search what file corresponds to the module. In case the file can not be found, we get an ImportError
, in the other case, Python will open the corresponding file, and "import it".
Importing means it will execute all the statements in the file (top to bottom). But now imagine that you have to files that require each other. In that case file A
requires file B
to be imported (completely). But B
requires file A
to be (completely) imported. There is no way to solve this: since there can be statements at the end of the file, that change the class
definition, etc. But we have reached an import
statement, which is a "contract" that the module is entirely loaded. So this is basically the "chicken-and-the-egg"-problem.
We can however solve such cyclic imports by postponing the linking. Instead of passing a reference to the class. We can for example pass some sort of token (Django uses strings, but there could have been different ways). Now the string does not introduces a problem, since before we start interpreting, the "builtin library" of Python is already loaded into memory.
After all the model classes are loaded into memory, Django will perform a "linking" phase. After the loading phase, all the models.py
files are loaded into memory, so now we can replace the strings with references to the classes. So Django basically solves the problem by postponing linking the ForeignKey
s to the real class object.
Upvotes: 1