Should any semi-complicated logic go in a model and/or modelmanager?

I’m a Django beginner. Most tutorials I’ve read so far haven’t included logic that’s much more complicated than basic database queries. I’m working on a website and the first feature I’m trying to code is a function that accepts a list of strings, and searches virtually every field of a model for that string, and returns a queryset for each search term.

I was going to make a services.py file to put the function in, then I read a blog sternly warning against it, though I’m not sure I fully understood it. Am I supposed to put basically any function that’s semi-complicated in a Model or ModelManager?

Here’s what I have, which works, but I just want to make sure I’m starting off with good code practices (the last search_field is to a model with a foreign key to self.model):

class MoleculeManager(models.Manager):
    def _build_search_query(self, search_string, molecule_types):
        search_fields = [
            'hgnc_id',
            'hgnc_symbol',
            'hgnc_name',
            'hmdb_accession',
            'hmdb_name',
            'moleculealias__alias',
        ]

        query = Q()
        for search_field in search_fields:
            query |= Q(**{f"{search_field}__iexact": search_string})

        return query & Q(type__in=molecule_types)
    
    def find_matching_molecules(self, search_strings, molecule_types):
        """Takes list of strings to search on and list of molecule types (e.g. Molecule.MoleculeType.GENE) to search"""
        search_results = []

        for search_string in search_strings:
            query = self._build_search_query(search_string, molecule_types)

            molecules = self.filter(query).distinct()
            search_results.append({'search_string': search_string, 'molecules': molecules})

        return search_results

First, what you are showing here looks good to me.

There’s no pat answer to this. This is a question that should be considered on a case-by-case basis. You’re generally best served by putting the code in the most appropriate location for that code.

(My personal take on that referenced blog post is that what you should not do is simply create a structure to house this code - an opinion with which I’m in complete agreement.)

For example, if you need to create a complex query - or if you’re looking to perform calculations based upon data retrieved by a query, then the manager may be appropriate place for it.

On the other hand, if these calculations need to be performed on an individual instance of a model, then you might be best served by making it a model method.

If the code is independent of any models, then I would find it appropriate to include it in the view. (Most such code tends to only need to be used in one place in the system - no value in moving it to a custom location if it’s not being used in multiple different locations.)

I work under the principle that Python is not Java, PHP, C/C++, etc. You should adopt the coding practices of Python and not try to adopt patterns beneficial or required by those other languages.

1 Like

Thank you! That’s reassuring.