Beginner: Trying to understand how apps interact in Django Beginner: Trying to understand how apps interact in Django django django

Beginner: Trying to understand how apps interact in Django


Take a look at django's built-in contenttypes framework:

django.contrib.contenttypes

It allows you develop your applications as stand-alone units. This is what the django developers used to allow django's built-in comment framework to attach a comment to any model in your project.

For instance, if you have some content object that you want to "attach" to other content objects of different types, like allowing each user to leave a "favorite" star on a blog post, image, or user profile, you can create a Favorite model with a generic relation field like so:

from django.db import modelsfrom django.contrib.auth.models import Userfrom django.contrib.contenttypes.models import ContentTypefrom django.contrib.contenttypes import genericclass Favorite(models.Model):    user = models.ForeignKey(User)    content_type = models.ForeignKey(ContentType)    object_id = models.PositiveIntegerField()    content_object = generic.GenericForeignKey('content_type', 'object_id')

In this way you can add a Favorite star from any user to any model in your project. If you want to add API access via the recipient model class you can either add a reverse generic relation field on the recipient model (although this would be "coupling" the two models, which you said you wanted to avoid), or do the lookup through the Favorite model with the content_type and object_id of the recipient instance, see the official docs for an example.


"Is what I wrote above, importing a model from another app and setting it as a foreign key, how Django apps interact?"

Yep. Works for me.

We have about 10 applications that borrow back and forth among themselves.

This leads to a kind of dependency in our unit test script.

It looks like this.

  • "ownership". We have a simple data ownership application that defines some core ownership concepts that other applications depend on. There are a few simple tables here.

  • "thing". [Not the real name]. Our thing application has data elements owned by different user groups. There are actually several complex tables the model for this app. It depends on "ownership".

  • "tables". [Not the real name]. Some of our users create fairly complex off-line models (probably with spreadsheets) and upload the results of that modeling in "tables". This has a cluster of fairly complex tables. It depends on "ownership".

  • "result". [Not the real name]. Our results are based on things which have owners. The results are based on things and tables, and are responses to customer requests. This isn't too complex, perhaps only two or three core tables. It depends on "things" and "table". No, it doesn't completely stand-alone. However, it is subject to more change than the other things on which it depends. That's why it's separate.

  • "processing". We schedule and monitor big batch jobs. This is in this application. It's really generic, and can be used in a variety of ways. It completely stands alone.

  • "welcome". We have a "welcome" app that presents a bunch of mostly static pages. This doesn't have too many tables. But it's on it's second incarnation because the first was too complex. It completely stands alone.

The only relationship among the dependent apps is some table names. As long as we preserve those tables (and their keys) we can rearrange other apps as we see fit.


There's nothing wrong (imho) with making some app dependent on another. After all, apps are just operations on a set of models. you just have to always be aware of which app depends on which app (I guess you could call that a dependency map).

You can achieve loose coupling with the contenttypes framework. It allows an app to be truely portable/pluggable yet still integrated with other applications.

I wrote a comments app (yea, I re-invented the wheel), that can be integrated into any other application, with a few lines in the template of the page where comments should be posted (using custom tags).

Say you want a model "thread" to be pluggable into any other model. The idea is to create e generic foreign key (see django documentation on that), and write a small function that takes any object and returns a "thread" corresponding to it (or creates one if necessary), and write a custom template tag that uses that functionality, e.g. {% get_thread for arbitrary_object as thread %}. All posts are related to a thread, which is related to the object, which can be of any type.

You can think of the "thread" object as a kind of a proxy, so instead of having a post be related to a certain "article" or a "blog post", it's just related to a thread, which is abstract in a sense, what is is a thread? It's just a collection of posts. The thread then allows itself to be related to any object regardless of its type. (although it does more than that, it could hold extra information such as allowing/disallowing anon. posts, closing/opening comments on the page, etc ..)

EDIT

Here's how you can create a generic foreign key with the content types framework:

from django.contrib.contenttypes import genericfrom django.contrib.contenttypes.models import ContentTypeclass Thread( models.Model ):    object_type = models.ForeignKey(ContentType)    object_id = models.PositiveIntegerField()    object = generic.GenericForeignKey('object_type', 'object_id')

You can make it more "transparent" by exploiting the implicit "common" interface that django assumes all objects implement ..

    #inside the Thread class:    def __unicode__(self):        return unicode(self.object)    def get_absolute_url(self):        return self.object.get_absolute_url()