Search code examples
pythondjangomoduleorganizationcode-organization

Correct way to make python module smaller by delegating


I'm trying to reduce clutter in my project's models.py and I decide to move out "utility" methods of some models.

The idea was to create a bunch of utility modules with classes and functions which will be used by model classes, but since there is no significant piece of code that doesn't use other models, it results in a circular dependence. E.g. in models.py:

from myproject import parse_util

class Entry(models.Model):
    text = models.TextField()
    parser = parse_util.Parser(self)

    def get_some_object():
        return parser.parse_text()

class SomeModel(models.Model):
    name = models.CharField(max_length=32, null=False, unique=True)

In parse_util.py:

from myproject.models import SomeModel

class Parser():

    def __init__(self, entry):
        self.entry = entry

    def parse_text(self):
        # parsing self.entry.text
        ...
        some_object = SomeModel.objects.get(name=parsed_value)
        return some_object

I can avoid it by importing SomeModel directly in the function, instead of doing it module-wide, or use get_model('myapp', 'SomeModel'). But both of these don't look good.

After the whole day in thoughts I generalised the problem: what is the correct and pythonic way (if any) to move some code coupled with another code in the module out of that module?

That is if there is a module with classA and classB and there are lengthy methods of classB, which use both classA and classB in some way, is there any good solution to cut down described module by splitting it into several models, but keeping entry points to classA and classB in place.


Solution

  • So you have the following hardcoded dependencies: Entry → Parser → SomeModel.

    The way I would break the dependency is to make Parser generic like this:

    class Parser(object):
    
        def __init__(self, entry, model):
            self.entry = entry
            self.model = model
    
        def parse_text(self):
            # parsing self.entry.text
            ...
            some_object = self.model.objects.get(name=parsed_value)
            return some_object
    

    Now the hardcoded dependencies are just: Entry → Parser