[Django]-How to avoid circular references and write DRY code in django

5👍

The error occurs because when you import get_top_viewed_posts at the top of models.py the Post model is not declared yet.

You have a few alternatives.

Move the import from the top of models.py to inside the method

def send(self):        
    from social.services import get_top_viewed_posts
    posts = get_top_viewed_posts()

Don’t worry about performance, imports are cached – but if you use it in other methods it may be tedious to repeat the same import over and over.

Abstract the class

Make the function more generic passing the model as an argument, this way you don’t need to import the model in the top of the services.py file:

def get_top_viewed_model(model, popular_ids, order_by='-created_at'):
    return model.objects..filter(
        pk__in=popular_ids,
    ).order_by(
        order
    )

Then:

def send(self):        
    posts = get_top_viewed_model(type(self), popular_posts_ids)

# at other places
get_top_viewed_model(Posts, popular_posts_ids)

Use a custom manager

Create a custom manager with a top_viewed method:

class TopViewedManager(models.Manager):
    def __init__(self, order='-created_at', **kwargs):
        self._order = order
        self._filter = kwargs

    def top_viewed(self):
        return self.get_queryset().filter(**self._filter).order_by(self._order)

class Post(models.Model):
    ...
    objects = TopViewedManager(pk__in=popular_posts_ids)

Then just use this where you would use get_top_viewed_model:

Post.objects.top_viewed()

This manager is quite generic so you can use it with any model, filter and order you want.

Probably there are other alternatives and it is a matter of personal taste.

Leave a comment