95

Directions from my supervisor: "I want to avoid putting any logic in the models.py. From here on out, let's use that as only classes for accessing the database, and keep all logic in external classes that use the models classes, or wrap them."

I feel like this is the wrong way to go. I feel that keeping logic out of the models just to keep the file small is a bad idea. If the logic is best in the model, that's where it really should go regardless of file size.

So is there a simple way to just use includes? In PHP-speak, I'd like to propose to the supervisor that we just have models.py include() the model classes from other places. Conceptually, this would allow the models to have all the logic we want, yet keep file size down via increasing the number of files (which leads to less revision control problems like conflicts, etc.).

So, is there a simple way to remove model classes from the models.py file, but still have the models work with all of the Django tools? Or, is there a completely different yet elegant solution to the general problem of a "large" models.py file? Any input would be appreciated.

4
  • 7
    You do know the import statement, right?
    – balpha
    Jul 21, 2009 at 17:29
  • 7
    PS. I don't mean that offensively, I just want to know where you're at.
    – balpha
    Jul 21, 2009 at 17:31
  • 1
    Yes, but I didn't know if django's admin tools would work just using import statements to pull in the Models. I would rather ask here than spend lots of time trying out using plain ole imports just to find out django's tools don't play well with them. I admit I'm newer to python and django, so I'm probably only at a simple understanding of the import statement...
    – Eddified
    Jul 21, 2009 at 20:26
  • see stackoverflow.com/questions/6336664/…
    – andorov
    Oct 5, 2015 at 17:31

3 Answers 3

117

It's natural for model classes to contain methods to operate on the model. If I have a Book model, with a method book.get_noun_count(), that's where it belongs--I don't want to have to write "get_noun_count(book)", unless the method actually intrinsically belongs with some other package. (It might--for example, if I have a package for accessing Amazon's API with "get_amazon_product_id(book)".)

I cringed when Django's documentation suggested putting models in a single file, and I took a few minutes from the very beginning to figure out how to split it into a proper subpackage.

site/models/__init__.py
site/models/book.py

__init__.py looks like:

from .book import Book

so I can still write "from site.models import Book".


The following is only required for versions prior to Django 1.7, see https://code.djangoproject.com/ticket/3591

The only trick is that you need to explicitly set each model's application, due to a bug in Django: it assumes that the application name is the third-to-last entry in the model path. "site.models.Book" results in "site", which is correct; "site.models.book.Book" makes it think the application name is "models". This is a pretty nasty hack on Django's part; it should probably search the list of installed applications for a prefix match.

class Book(models.Model):
    class Meta: app_label = "site"

You could probably use a base class or metaclass to generalize this, but I haven't bothered with that yet.

11
  • 2
    +1 I have used this with success. While S. Lott is right in multiple apps being a good idea, this is the here and now solution. Jul 21, 2009 at 18:09
  • 37
    I don't see much benefit of splitting things into a bunch of apps, when your models are closely and intrinsically related. Jul 21, 2009 at 20:15
  • 2
    This interests me. I read the django wiki link scompt posted and found this: "This has been verified to work without the Meta class app_labels, in the current main branch." So does that mean if you are working with the main branch we can discard the Meta: app_label stuff? It's confusing as it's after the comment about the ticket to solve this problem. Jul 22, 2009 at 1:41
  • 2
    I just tested with trunk (as of earlier today, r11286); if the app_name isn't set, the model simply doesn't show up in "sqlall appname" and probably won't be created by syncdb (but I don't use that so I can't test it). It's quite a confusing error case, because it doesn't trigger any errors; it just silently doesn't show up. Jul 22, 2009 at 4:08
  • 3
    Wow, nearly 10 years later and I still love this solution. Agreed that it is a much better approach than splitting your code into smaller apps, which in my opinion can lead to a codebase that is difficult to reason about. Aug 22, 2018 at 22:52
64

Django is designed to let you build many small applications instead of one big application.

Inside every large application are many small applications struggling to be free.

If your models.py feels big, you're doing too much. Stop. Relax. Decompose.

Find smaller, potentially reusable small application components, or pieces. You don't have to actually reuse them. Just think about them as potentially reusable.

Consider your upgrade paths and decompose applications that you might want to replace some day. You don't have to actually replace them, but you can consider them as a stand-alone "module" of programming that might get replaced with something cooler in the future.

We have about a dozen applications, each model.py is no more than about 400 lines of code. They're all pretty focused on less than about half-dozen discrete class definitions. (These aren't hard limits, they're observations about our code.)

We decompose early and often.

8
  • 1
    right on point. any non-trivial webapp would be several small 'apps'. take a hint of the contrib and other popular apps, user authentication is one app, tagging is another one, user profiles one more, etc.
    – Javier
    Jul 21, 2009 at 17:44
  • 5
    While this is the "right" way, and helpful to know, it isn't quite what I was looking for. I apologize if there was no way to know what kind of answer I was looking for. :)
    – Eddified
    Jul 21, 2009 at 20:22
  • @Eddified: if you don't do this, it's only going to get worse. Start splitting now.
    – S.Lott
    Jul 21, 2009 at 20:25
  • Funny enough, at this very moment I'm listening to Jacob Kaplan Moss (at OSCON) explaining exactly this in great and strongly justified detail;-). Jul 21, 2009 at 21:11
  • 14
    Glenn Maynard answer is much better on this one. Dividing up a complex webapp into many apps is certainly a good practice, but so is refactoring a model.py file up WITHIN an app. The two actions can be orthogonal.
    – Erik
    Jul 29, 2012 at 0:25
6

I can't quite get which of many possible problems you might have. Here are some possibilities with answers:

  • multiple models in the same file

    Put them into separate files. If there are dependencies, use import to pull in the additional models.

  • extraneous logic / utility functions in models.py

    Put the extra logic into separate files.

  • static methods for selecting some model instances from database

    Create a new Manager in a separate file.

  • methods obviously related to the model

    save, __unicode__ and get_absolute_url are examples.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.