18đ
Introduction talk in the bottom of the answer (more straight to the answer). I will assume that you have one app for text handling called Text and one app for picture handling called Pictures and a third app for blogging called Blog.
Big picture
You will need to study the manual about the template language for python programmers. The idea is that each thing is in its own app and that you have a third app that connects everything. The apps should then supply its models and views as you like them (just remember to keep you focus on what the app should do) and also supply a set of templatetags.
How to make inclusion tags
Make inclusion tags and it is really easy! It will remind you of writing normal views.
Create a directory templatetags in you app folder. Also create a __init__.py
file in this templatetags (so the directory becomes a python package).
Then create a python file. The name is important, you will use this in {% load xyz %}
in the templates that will use your app. For instance if call the file picturestags.py
, you will call
{% load picturestags %}
in all templates that will use it.
First in the file add some politics you need not to think much about, just include this before anything else:
from django.template import Library
register = Library()
Then add the tags by defining functions with the same name as your tag. I will call it display_picture in the example and it will take one argument url. The function should create a dictionary that you will use in a template. My example will just display the picture the url is pointing to.
@register.inclusion_tag('pictures/display_picture.html')
def display_picture(url):
return {'picture': url}
Create the path templates/pictures in your app and create the file display_picture.html inside containing:
<img src="{{ picture }}" />
As you probably understand, the @register makes this a tag, what is inside the dictionary display_picture returns are what you can use in display_picture.html. Very much like your normal view functions.
In the end you will end up with these files:
pictures/
__init__.py
models.py
views.py
tests.py
templates/
pictures/
display_picture.html
templatetags/
picturetags.py
That is all you need to add to your Picture app. To use this in your Blog app, you need to add Pictures to your INSTALLED_APPS. Then in the templates, where you need to use your own newly home baked tag first load it: {% load picturestags %}
then just add the tag {% display_picture https://www.google.com/intl/sv_ALL/images/logos/images_logo_lg.gif %}
like this:
{% load picturestags %}
<html>
<body>
{% display_picture https://www.google.com/intl/sv_ALL/images/logos/images_logo_lg.gif %}
</body>
</html>
Results
This is just a small example but you can see that it is very easy to expand this. Your blog could connect the Text and Pictures app by importing their models and foreign key them. There is you connection Text and Pictures for a certain blog post. Your blog_post.html-template could look like (simplified):
{% load picturestags %}
{% load texttags %}
<html>
<body>
<h1>{{ subject }}</h1>
<div class="article">{% article_to_html articleid %}</div>
<div class="article_picture">{% display_picture %}</div>
</body>
</html>
Notice that only the Blog has dependencies and it is dependencies it should have (no blog without text and picturesâŠbut pictures can live without text). The look and placement should be and can be controlled by CSS and DIV/SPAN-tags. In this way you can take your Picture app and give it to someone who has no idea about Text app and use it, displaying pictures in different ways probably without ever touching your code!
Inclusion tags is the only thing I know of since I just learned this yesterday. I think it is a convenience provided by Django to make life simple. On the documentation page there are a whole lot more (including how to make ârealâ tags the hard way without âshortcutsâ). So if you find this method to limited, read the documentationâŠit has lots of examples. It also discusses how to make filters, simple_tags, thread considerations and other advanced stuff.
Introduction
I had this exactly this problem as you and I also wanted something different than the answers I read (I donât say the answers was bad, they helped me learn a lot and gave me insights, but I wanted this I am writing now). I managed to figure something out that is not very obvious, thanks to your question and definitely thanks to Stack Overflow so this is my contribution back even to a half year old question that is probably abandoned (might help a googler or two)!
I also got a lot of inspiration from Google Tech Talk Reusable Apps. In the end (43 minutes) he mentions some good examples like django-tagging which is what he says a model for how to write reusable apps. That gave me the idea for all this, because that is the way django-tagging solves this very problem we had/have.
Now after I written all this (took an hour), I feel for the first time that I might contribute instead of just google and follow what others are doing or complaining that others are not writing how I need to do things. For the first time I am taking my responsibility of writing my view so others can google this (just had to write this paragraph đ because it feels really great, even if it might be shredded to pieces or ignored and forgotten).
6đ
Think of it in the same way that you would use any 3rd-party app in your project. âRe-usableâ doesnât mean âwithout dependenciesâ. On the contrary, youâd be hard-pressed to find an app that doesnât have at least one dependency, even if itâs just dependent on Django or core Python libraries. (While core Python libraries are usually thought of as âsafeâ dependencies, i.e. everyone will have it, things do sometimes change between versions of Python, so youâre still locking your app into a specific point in time).
The goal of re-usuable is the same as that of DRY: you donât want to write the same code over and over again. As a result, it makes sense to break out functionality like a picture app, because you can then use it over and over again in other apps and projects, but your picture app will have dependencies and other packages will depend on it, as long as there are no circular dependencies, youâre good (a circular dependency would mean that you havenât actually separated the functionality).
- [Django]-CommandError: You appear not to have the 'sqlite3' program installed or on your path
- [Django]-How to serve django media files via nginx ?
- [Django]-How to spread django unit tests over multiple files?
5đ
This is a good question, and something I find quite difficult to manage also. But â do you imagine these applications being released publicly, or are you only using them yourself? If youâre not releasing, I wouldnât worry too much about it.
The other thing is, dependencies are fine to have. The pictures app in your example sounds like a good candidate to be a âreusableâ app. Itâs simple, does one thing, and can be used by other apps.
A blog app on the other hand usually needs to consume other apps like a picture app or a tagging app. I find this dependency fine to have. You could try to abstract it a little, by simply linking to a media resource that was put there by your picture app.
Itâs all just a little bit of common sense. Can you make your apps slim? If yes, then try to create them so they can be reused. But donât be afraid to take dependencies when they make sense. Also, try to allow extension points so you can potentially swap out dependencies for other ones. A direct foreign key isnât going to help here, but perhaps something like signals or Restful APIs can.
- [Django]-Django Admin: How to access the request object in admin.py, for list_display methods?
- [Django]-AttributeError: 'ManyRelatedManager' object has no attribute 'add'? I do like in django website but got this error
- [Django]-Add Text on Image using PIL
4đ
I am a Django newbie and I faced the same problem. It is shameful how thereâs so much buzz about reusable apps in the Django community, but not a single authoritative reference on how to connect them
in a project without hard coded dependencies.
I have been working on a new Django project and I wanted to set up my models to avoid hard coding as much as possible. This is the pattern that I used.
Layout
project_root/
core/
models/
mixinmodels1.py
mixinmodels2.py
...
utils.py
...
app1/
models/
__init__.py
base.py
basemixins.py
mixins.py
concrete.py
/signals
__init__.py
handlers.py
utils.py
...
app2/
...
...
App models
base.py
: This module implements only the âreason for existenceâ of that app in abstract classes.
Rules are:
-
This module usually only imports from the
core
app. It usually does not import anything from other apps in the same project. -
An exception to the above rule is when the âreason for existenceâ assumes the existence of another app. For example, a
group
app assumes that there is anuser
app somewhere. In this case, the way to link them is:# project_root/settings.py AUTH_USER_MODEL = 'app_label.UserModel' # project_root/groups/models/base.py from django.conf import settings
and then using settings.AUTH_USER_MODEL to refer to the
user
model -
Use this pattern for all apps, not just the
user
app. For example, you should also do# project_root/settings.py GROUP_MODEL = 'app_label.GroupModel'
-
If using the above pattern, only assume the functionality provided by
base.py
of the other app
to which you are linking. Do not assume the functionality of elaborate concrete classes (I will discuss where to put concrete classes shortly) -
Of course, importing from django, third party apps and python packages is allowed.
-
Make sure that the assumptions that you make in
base.py
of any app is rock solid and would not
change much in the future. A good example isdjango-registration
by James Bennett. Its an old app, but its appeal did not wane because it made rock solid assumptions. Since good reusable apps do one thing well, it is not difficult to find that set of assumptions.
basemixins.py
: This module should implements plugs to the concrete models of that app. A âplugâ to a model M is any model that contains a foreign key to the model M. For example:
# project_root/groups/models/basemixins.py
from django.conf import settings
from django.db import models
class BaseOwnedByGroup(models.Model):
"""
This is a plug to the group model. Use this
to implement ownership like relations with
the group model
"""
owner = models.ForeignKey(settings.GROUP_MODEL,
related_name = '%(app_label)s_%(class)s_owner',
verbose_name = 'owner')
# functionality and manager definitions go here.
class Meta:
abstract = True
app_label = 'groups'
BaseOwnedByGroup
is a âplugâ to the group
model. The rules here are the same as âbase.py`
- While defining âplugsâ in
basemixins.py
, only assume functionality provided bybase.py
. - Import only from
core
, django, third party apps and python packages.
mixins.py
: This module should be used for two purposes
-
To define elaborate âplugsâ, which assumes the functionality of the elaborate concrete classes but not the relationships with other apps. The elaborate plugs should ideally inherit one of the âbase plugsâ defined in
basemixins.py
. -
To define mixin models (that are not âplugsâ) which can be used by the concrete classes of that app.
concrete.py
: This module should be used to define (you guessed it) concrete classes of that apps and to set up relationships with other apps. In short, this module assumes your project and all the
functionality you want to provide in it.
Relationships to other apps should be set up as follows:
-
To establish a
one to one
ormany to one
relationship with model M of appanother_app
, do the following:# project_root/another_app/utils.py def plug_to_M_factory(version_label): """ This is a factory method which returns the plug to model M specified by version_label """ if version_label == 'first_version': from another_app.models.basemixins import BasePlugToM return BasePlugToM if version_label == 'second_version': from another_app.models.mixins import PlugToM return PlugToM ... # project_root/groups/models/concrete.py from groups.models.base import BaseGroup from another_app.utils import plug_to_M_factory PlugToMClass = plug_to_M_factory(version_label = 'second_version') class ConcreteGroup(BaseGroup, PlugToMClass): # define your concrete model class Meta: app_label = 'groups'
-
To establish a
many to many
relationship, the recommended way is to use athrough
model. Inherit the correct plug in thethrough
model in the exact same way (as we did in theConcreteGroup
model)
signals
: While setting up relationships, often we have to perform operations on a model M of app app1
, when model N of app app2
changes. You can use signals to handle that. Your handlers can assume the functionality of the concrete sender, but often they donât need to. Assumption of the base version of the sender in base.py
is enough. This is why it is a good idea to always use settings.ModelName
to refer to a concrete model. You can extract out the model class from the settings.ModelName
string by either using ContentType
or using a project wide get_model_for_settings
function as follows:
# project_root/project/utils.py
from django.db.models import get_model
from django.core.exceptions import ImproperlyConfigured
def get_model_from_settings(model_string):
"""
Takes a string of the form 'app_label.model' as input, returns the
appropriate model class if it can find it.
"""
try:
app_label, model_name = model_string.split('.')
except ValueError:
raise ImproperlyConfigured("function argument must be of the "
"form 'app_label.model_name', got '%s'" % model_string)
model = get_model(app_label, model_name)
if model is None:
raise ImproperlyConfigured("function argument refers to model "
"'%s' that has not been installed" % model_string)
return model
core
: The core app is a special app which stores project wide mixin functions.
-
These mixins should not assume anything about any other app. Only exception to this rule is mixins that rely on base functionality of settings.AUTH_USER_MODEL. This is because you can safely assume that most projects will have an
user
model. -
Of course imports from django, third party and python packages are still allowed
-
Remember that all
base.py
andbasemixins.py
modules are allowed to import fromcore
.
Finally for everything to work as intended, import your models in models/__init__.py
of every app.
The advantages that I find from following this scheme is:
-
The models are reusable. Anyone can use the abstract base models and mixins to design their own concrete model.
base.py
,basemixins.py
and related factory methods can be bundled together with a bare bones concrete model and shipped in a reusable app. -
The apps are extendable. All mixins are versioned and there is a clear inheritance scheme.
-
The apps are loosely coupled. External mixins are accessed via factory methods and external models are referred to using django.conf.settings.
-
The apps are self contained. Any changes in an app will most likely break that app and that app only. Other apps will most likely remain unscathed. Even if external apps break, the place where this
could happen is clearly marked.
I have been using this scheme to reduce coupling between my apps. I am not an experienced Django programmer, and I have a lot to learn, so any feedback is appreciated.
- [Django]-Django-rest-framework 3.0 create or update in nested serializer
- [Django]-Exclude a field from django rest framework serializer
- [Django]-Passing variable urlname to url tag in django template