Problem with CircularDependency Model

220 views
Skip to first unread message

Tobias Dacoir

unread,
May 5, 2017, 8:41:52 AM5/5/17
to Django users
I am trying to build a model (in two separate apps) which has the following constraints:

- There can be many institutes
- there can many users
- each user is a member of exactly one institute
- each institute has exactly one master user

However I am running into CircularDependency errors when trying to create this model and database. Of course this model is a bit problematic because you can't have an institute without master user, but also you can't create a user without some default institute. How do I fix this? Should I be more lean on the model side and allow for a lot of blanks and null values and make all the exception handling somewhere else?

Please tell me how to improve my model.
 

class User(AbstractBaseUser, PermissionsMixin):

   
# account-related information
    institute
= models.ForeignKey(Institute, blank=True, null=True, on_delete=models.SET(get_default_institute))
    username
= ...
   
....

This is the model in another app for the institutes

class Institute(models.Model):
    master_user
= models.ForeignKey(settings.AUTH_USER_MODEL, related_name='master_user')
    name
= models.CharField(max_length=255)

   
def members(self):
       
UserModel = apps.get_model('play', 'User')  # need to avoid circular import
       
return UserModel.objects.filter(institute=self)

Dartos

unread,
May 5, 2017, 11:00:46 AM5/5/17
to Django users
Hi,

Have you tried breaking up the migrations into multiple steps?

First create User and Institute without reference to each other. Then try additional migrations.

Also, you can simplify Institute.members using reverse relations as follows:
def members(self):
   
return Institute.user_set.all()

Dartos

Dartos

unread,
May 5, 2017, 12:07:18 PM5/5/17
to Django users
Sorry, my earlier suggestion for Institute.members is incorrect. Here's the correct code:
def members(self):
   
return self.user_set.all()

For example, if foo is an institute, then foo.user_set.all() would give all users related to that institution.

Dartos

Tobias Dacoir

unread,
May 8, 2017, 1:26:06 PM5/8/17
to Django users
Thanks for the suggestion, but I the migration by itself is not the only problem. I still have the feeling I should fine-tune the model somehow.

Melvyn Sopacua

unread,
May 9, 2017, 4:51:36 AM5/9/17
to django...@googlegroups.com

On Friday 05 May 2017 05:41:52 Tobias Dacoir wrote:

> I am trying to build a model (in two separate apps) which has the

> following constraints:

>

> - There can be many institutes

> - there can many users

> - each user is a member of exactly one institute

> - each institute has exactly one master user

>

> However I am running into CircularDependency errors when trying to

> create this model and database. Of course this model is a bit

> problematic because you can't have an institute without master user,

> but also you can't create a user without some default institute. How

> do I fix this? Should I be more lean on the model side and allow for

> a lot of blanks and null values and make all the exception handling

> somewhere else?

>

> Please tell me how to improve my model.

>

> >> class User(AbstractBaseUser, PermissionsMixin):

> > # account-related information

> > institute = models.ForeignKey(Institute, blank=True, null=True,

 

institute = models.ForeignKey('mycoolapp.Institute', ....)

 

This uses a promise and resolves the model at init stage (rather then class declaration). Make sure you remove the import of Institute or it won't help.

 

If you're interested in the topic of resolving models at runtime, take a loot at django.apps.Apps, specifically get_model. But all the work for foreign keys is already done for you.

 

--

Melvyn Sopacua

Tobias Dacoir

unread,
May 17, 2017, 8:57:58 AM5/17/17
to Django users
Thanks Melvyn,

I used promises before, when I use a foreign key for a class that is not yet defined, however it didn't change anything for my problem. I can make migrations, but not migrate. I am starting with a new database from scratch.
   self.ensure_not_cyclic(target, lambda x: (parent.key for parent in self.node_map[x].parents))
 
File "/Users/no68tuh2/.virtualenvs/ihearu/lib/python2.7/site-packages/django/db/migrations/graph.py", line 370, in ensure_not_cyclic
   
raise CircularDependencyError(", ".join("%s.%s" % n for n in cycle))
django
.db.migrations.exceptions.CircularDependencyError: play.0001_initial, portal.0001_initial

This CircularDependencyError is driving me crazy.

Tobias Dacoir

unread,
May 17, 2017, 9:25:42 AM5/17/17
to Django users

I am not sure if this is solution but instead of running makemigrations first, I ran migrate --run-syncdb. That got me past this error.

Melvyn Sopacua

unread,
May 17, 2017, 11:41:41 AM5/17/17
to django...@googlegroups.com

Ah, now I see!

This has nothing to do with *model* dependencies. Two *migrations* depend on each other: play and port, both 0001_initial.

 

Do you have any idea how you got into that jam? Did you fiddle with django_migrations table? Maybe run --fake?

--

Melvyn Sopacua

Tobias Dacoir

unread,
May 19, 2017, 3:19:04 AM5/19/17
to Django users
No, I am trying to deploy it locally. I have no sqlite DB and no migrations. For some strange reason I used to just run manage.py makemigrations and would create the initial migrations for all Apps. This doesn't work for some reason anymore. So I have to to makemigrations APPname for each app and when I run to migrate to create the intial database schema, it doesn't work. So I figured my Data Model is crap.

Melvyn Sopacua

unread,
May 19, 2017, 11:50:42 AM5/19/17
to django...@googlegroups.com

On Friday 19 May 2017 00:19:04 Tobias Dacoir wrote:

> No, I am trying to deploy it locally. I have no sqlite DB and no

> migrations. For some strange reason I used to just run manage.py

> makemigrations and would create the initial migrations for all Apps.

> This doesn't work for some reason anymore. So I have to to

> makemigrations APPname for each app and when I run to migrate to

> create the intial database schema, it doesn't work. So I figured my

> Data Model is crap.

 

Yep, let me see if I got it right:

- The User model is defined as AUTH_USER_MODEL in settings.py

- User points to institute

- Institute points to User

 

So it is in fact a circular dependency on the model level as well. And one you don't need.

 

From the way you defined the related_name it is also clear you don't get the RelatedObject principle of Django.

 

A ForeignKey creates a bridge between two models: you can travel in both directions. Using your models:

 

class User(AbstractBaseUser, PermissionsMixin):

username = ...

 

class Institute(models.Model):

master_user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='institute')

members = models.ManyToManyField(settings.AUTH_USER_MODEL)

 

Because of the ForeignKey on Institute, Django creates an attribute on User, called 'institute'. It uses institute, because I specified that as related_name. You could use a different name there, but it has to be a name that can be used on the model you point to.

 

However, this isn't a good representation of realitity, given your own description.

So this is a better way to do it:

 

class User(AbstractBaseUser, PermissionsMixin):

institute = models.ForeignKey('port.Institute', related_name='members')

is_master_user = models.BooleanField(default=False)

 

class Institute(models.Model)

name = models.CharField(max_length=255)

# members is now created by the foreign key

@property

def master_user(self):

return self.members.get(is_master_user=True)

 

This model is much simpler. All you need to do is to verify that when someone is made master user, that all other members of the institute are *not* master user.

 

You can do this in a post_save signal or in the clean method of User , both with its own caveats.

 

Another way would be to define a MasterUser model, which is easier to maintain in the admin:

 

class MasterUser(models.Model):

# Assume one person can be master user of many institutes

# if not, this also has to be a OneToOneFIeld

user = models.ForeignKey(settings.AUTH_USER_MODEL)

institute = models.OneToOneField(Institute, related_name='master_user')

 

 

 

 

> On Wednesday, May 17, 2017 at 5:41:41 PM UTC+2, Melvyn Sopacua wrote:

> > On Wednesday 17 May 2017 05:57:57 Tobias Dacoir wrote:

> > > Thanks Melvyn,

> > >

> > >

> > >

> > > I used promises before, when I use a foreign key for a class that

> > > is

> > >

> > > not yet defined, however it didn't change anything for my problem.

> > > I

> > >

> > > can make migrations, but not migrate. I am starting with a new

> > >

> > > database from scratch.

> > >

> > > self.ensure_not_cyclic(target, lambda x: (parent.key for parent in

> > >

> > > self. node_map[x].parents))

> > >

> > > File

> > >

> > > "/Users/no68tuh2/.virtualenvs/ihearu/lib/python2.7/site-packages/d

> > > jang

> > >

> > > o/db/migrations/graph.py" , line 370, in ensure_not_cyclic

> > >

> > > raise CircularDependencyError(", ".join("%s.%s" % n for n in

> > >

> > > cycle)) django.db.migrations.exceptions.CircularDependencyError:

> > >

> > > play.0001_initial, portal.0001_initial

> > >

> > >

> > >

> > > This CircularDependencyError is driving me crazy.

> >

> > Ah, now I see!

> >

> > This has nothing to do with *model* dependencies. Two *migrations*

> > depend on each other: play and port, both 0001_initial.

> >

> >

> >

> > Do you have any idea how you got into that jam? Did you fiddle with

> > django_migrations table? Maybe run --fake?

> >

> >

> > Melvyn Sopacua

 

--

Melvyn Sopacua

Tobias Dacoir

unread,
May 22, 2017, 9:11:57 AM5/22/17
to Django users
Thank you very much for the great answer!
You are correct, I didn't had a clear understanding of the RelatedObject Principle. I think I added this when I was fiddling with the rest framework.
Of course, I also had thought about making MasterUser a boolean, but I need to ensure that there is only one MasterUser. I thought about doing it in the Code, but I thought it might be better to enforce it at Database Level. However, I think your solution is much better.
I also thought having a separate Table for the MasterUser Relationship, like you suggested. I tried to some find some suggestions on how to model a relationship like that. I think, I will go for the boolean field but my intuitive assumption would be to overwrite the pre_save method. post_save kinda sounds like it is too late at that point.

Anyway, thanks a lot for you recommendations!

--

Melvyn Sopacua

Reply all
Reply to author
Forward
0 new messages