1

I'm having a seemingly simple definition error whereby the class Automation is not defined. I've created a very simple app called automations which seems to have caused a problem despite barely changing it. Note that there's also another app called messages, with a many:many relationship with the new automations.

Automations is just an app with this simple models.py file:

from django.db import models
from accounts.models import Account
from messages.models import Message

class Automation(models.Model):
    name = models.CharField(max_length=100)
    description = models.CharField(max_length=200)
    account = models.ForeignKey(Account, on_delete=models.CASCADE)
    date_created = models.DateTimeField(auto_now_add=True, null=True)
    messages = models.ManyToManyField(Message)

    def __str__(self):
        return self.name

And messages is a separate app with this models.py file:

from django.db import models
from accounts.models import Account
# from automations.models import Automation  # < FAILS
from automations.models import *  # < WORKS
# from apps.get_model(email_messages, Message)   # < FAILS
# from django.apps get_model(email_messages, Message)  # < FAILS
# from django.apps import apps  # < FAILS
# Automations = apps.get_model('email_messages', 'Messages')  # < FAILS


class Message(models.Model):
    name = models.CharField(max_length=100)
    subject = models.CharField(max_length=128)
    text = models.TextField()
    account = models.ForeignKey(Account, on_delete=models.CASCADE)
    date_created = models.DateTimeField(auto_now_add=True, null=True)
    automations = models.ManyToManyField(Automation) # < PROBLEMATIC LINE

    def __str__(self):
        return self.name

I initially had this import from automations.models import Automation which caused this error:

ImportError: cannot import name 'Automation' from partially initialized module 'automations.models' (most likely due to a circular import)

But I no longer received that error after adding from automations.models import *. However, now I'm getting a definition error NameError: name 'Automation' is not defined when adding automations = models.ManyToManyField(Automation) to the Message class.

Does anyone know why is this happening? I just want to add a many:many relationship between classes Message & Automation (presumably a ManytoManyField(Class) field is needed to be added to both classes?).

I've read other posts with similar issues but could find no solution. Thanks

user8758206
  • 2,106
  • 4
  • 22
  • 45
  • Well, do you understand the error message? Did you try to figure out what gets imported from where? When you tried this, did you detect a circular import? "I've read other posts with similar issues but could find no solution." Which ones? Why was the advice there not sufficient to solve the problem? – Karl Knechtel Aug 06 '21 at 22:00
  • 1
    Does this help? https://stackoverflow.com/a/47761188/75103 – thebjorn Aug 06 '21 at 22:08

1 Answers1

2

Warning!

I am not a professional and I am speaking just from minor experience. There might be a normal solution, but I have not yet found it!

I have stumbled upon this type of error and the reasoning behind it is very straight forward. The reason why you are unable to do this is that you need to be specific in these types of stuff. For example, to give a better perspective, imagine how would classes look like, when you want to model a family.

You might think of something like:

Mother, Father -> Child
Child -> Mother, Father

The problem is that this is not possible, since one of them HAS TO BE CREATED BEFORE THE OTHER ONE. This is the reason why you are getting a circular error. Normally you would be able to create this, but from computer perspective, it needs to know how much memory it should allocate for the object, but because of the objects wants to be in each other, you will get something like this (in theory): Allocate -> mother(childe(mother(child(....)), father(child(....)))) which I hope you can see would be an infinite cycle of allocating infinity.

To be even more specefic, what is python doing here is that it is trying to "copy-paste" everything you need right there where you have declared it. So what is happening here is that you said to it in file B -> import A and in A you have said import B which means to make it work, at the end, it needs to "copy-paste" itself on the place of import (so B -> import A -> import B => B -> imports B => impossible)

The only solution that at least I was able to find was something like this: https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_many/ (Pay closer look on how its modeled)

The only way you can access the other object (lets say you have a publication and you want to know which articles have it in their M2M field) you have to search through the all articles when you want to know which articles have the publication.

Now to the second part of a question. Why it doesnt smash the circular error when you use import *. I personally think that the reason why is that python is interpreted language and it is doing alot of lazy loading in behind. What I mean by that is that it know when it seens import A -> import B -> import A, but when you introduce * there, the syntax check does not notice it ??? until it comes to the specific line. Similar types of errors can be seen when you use a wrong method name on a class and python notices it after it tries to call the mentioned method.

I am not that much sure with the second answer, yet I see this very often happen when I misstype the name of a fuction

So in other words, what you need to do: Remove one of the imports, so you can clear the circular import problem (choose one, that fits more, like in the family example I have mentioned (it should be more clear which one should search for the other one))

Access the other part of M2M (in PSEUDOCODE !!!!):

Article.objects.filter(publication in publications)

Unfortunatelly I do not know the syntax by heart, but you can see where I am going with this (hopefully).

I hope it helps, if you have any questions, please go ahead and ask :)

StyleZ
  • 1,276
  • 3
  • 11
  • 27