Avoid Circular Model Imports in Django Apps

jkeesh picture jkeesh · Aug 3, 2011 · Viewed 9k times · Source

I have a django project with 2 apps like this:

## tags app, models.py
class Tag(models.Model):
    title = models.CharField(max_length=50)


## items app, models.py
from application.tags.models import Tag

class Item(models.Model):
    title = models.CharField(max_length=300)
    tags = models.ManyToManyField(Tag, related_name="items")

UPDATE TO CLARIFY FUNCTION LOCATION

I have a method on another model in items.models which gets all of the items that have a set of tags.

The resulting query is like:

## Gets all of the items that have tags t1 and t2
Item.objects.filter(tags=t1).filter(tags=t2)

This method uses the Item model as well as the Tag model, which is ok, since Tag is imported into the items app.

However, I want to have access to this method in the tags app, but doing so would result in a circular import.

Right now my workaround to get all of the items with a set of tags in the tags app is to do a set intersection on the reverse relation in the many to many field.

## Get all items that have the tags with ids tag_ids
item_set = set(Tag.objects.get(pk=tag_ids[0]).items.all())
for cur_tag_id in tag_ids[1:]: ## for all of the rest of the tags
     item_set = item_set & set(Tag.objects.get(pk=cur_tag_id).items.all())

This results in several more queries and a set intersection. Is there a way I can do something like Item.objects.filter(tags=t1).filter(tags=t2)... from the Tag model in the tags app?

I was able to get around this using contenttypes to get the Item model to make the same query. Is this acceptable, or is there a better way to organize this code?

Answer

Matthew Schinckel picture Matthew Schinckel · Aug 3, 2011

When you define models with foreign keys, you can use the format:

tags = models.ManyToManyField('tags.Tag', ...)

This means you don't need to have imported the Tag class, just installed the tags app.

Then you can store a function in a different location, that might import both Tag and Item, and not have to worry about circular imports.