Django and Services

603 views
Skip to first unread message

Michael Burton

unread,
May 1, 2008, 6:07:59 PM5/1/08
to Django users
I'm coming to Django from the Java Spring MVC world, and over in those
remote lands we have something called a "Service" layer.

The service layer is responsible for doing all the business logic that
your views might otherwise do. For example, you might have something
like:

class UserService:
def create_user(self,...)
def create_friendship(self,user_a, user_b)

or maybe:

class InvitationService:
def send_email_invitation(self,...)
def redeem_invitation(self,invitation)

These services would be instantiated as singletons which would then be
used by higher-up layers like the views. They would typically be re-
used between different views (eg. iPhone, desktop, and REST views
might all use the same service).

Looking at Django, I don't see an obvious place to put these sorts of
common business-logic methods. They don't really belong in the views
since you don't want to have to reimplement them every time you add a
new platform. But they also don't belong in the models, since you
might not want to tie your models to presentation-specifics such as
which email template to use.[1]

I'd like to do things the "Django way", so I thought I'd put the
question out to the community. Where would be the best places to put
business logic like this?

Cheers,
Mike


[1] In addition, there might not be an obvious model for a particular
kind of service. For example, what model would you use for a
FacebookService, if you need to integrate with the facebook platform?

alex....@gmail.com

unread,
May 1, 2008, 6:16:09 PM5/1/08
to Django users
You actually would place the on the model, for example you might have
a send_email method on an invitation model, to abstract the template,
simply make it a parameter to the function.

Michael Burton

unread,
May 1, 2008, 7:43:50 PM5/1/08
to Django users
Thanks for the response, Alex. That's certainly possible in some
cases.

But it seems like there are at least a few situations where you
wouldn't want to or are unable to put these methods into the model:

For example, maybe this model is not under your control. Perhaps it's
in a different app, or perhaps it's part of the base Django
distribution (as is the case with the User class, for example). If
you can't add methods to classes such as User, where do you stick
them?

Or another case is business logic that doesn't map directly to an
existing model in your app. Methods related to interfacing with
external services such as Facebook or Pownce might fall in this
domain. Your app might not have a Facebook model, so where do you put
business logic related to Facebook?

Even if you can find a place in one of your models to put logic like
this, I'd argue that there are times when it doesn't truly belong in
the model. The example I brought up before was putting a "send_email"
function into your Invitation model. Sure, you can do this, but it
introduces a lot of dependencies into your model that don't really
belong there. Should your model know how to send emails? How to
render email views? Which email template to use? Sure, as you
suggest, you can pass the template to use as a parameter, but what
about if you want to send invites as IM or SMS, or just display the
url for the user to copy-paste to their friends? You end up with a
proliferation of send_XXX methods that I'd argue are probably best to
keep out of the model.


So I guess I'm just curious to hear how other members of the community
have solved this. Are people slipping business logic into the model
classes for the most part?

Cheers,
Mike




On May 1, 3:16 pm, "alex.gay...@gmail.com" <alex.gay...@gmail.com>
wrote:

Michael Elsdörfer

unread,
May 1, 2008, 10:48:14 PM5/1/08
to Django users
> So I guess I'm just curious to hear how other members of the
> community have solved this. Are people slipping business logic into
> the model classes for the most part?

If what I need to write doesn't really fit in any of the common
"places" (models, managers, views, middleware etc.), then I would say
you are free to put it wherever you want to. I don't think there would
be anything wrong with using, say, ./project/app/services/facebook.py.
Or just ./project/services/facebook.py if it's not specific to an
application.

I know some people are creating separate apps (within their project)
for that sort of thing. I guess that makes most sense if you can map
sub-parts of what you implement to Django pieces. Say your Facebook
functionality wants provide it's own set of template tags. Or a
callback view for the API. Or if there is a chance it *might* need
it's own model at some point.

For a Facebook client that possibly doesn't even depend on your
project, you might even want to consider a separate reusable
package.

Michael

Russell Keith-Magee

unread,
May 1, 2008, 11:03:42 PM5/1/08
to django...@googlegroups.com
On Fri, May 2, 2008 at 6:07 AM, Michael Burton <mbu...@gmail.com> wrote:
>
> I'm coming to Django from the Java Spring MVC world, and over in those
> remote lands we have something called a "Service" layer.
>
> The service layer is responsible for doing all the business logic that
> your views might otherwise do. For example, you might have something
> like:
>
> class UserService:
> def create_user(self,...)
> def create_friendship(self,user_a, user_b)

The difficulty you're having here is that you're trying to write Java
code in Python. :-)

Unless there's something stateful about the UserService, there's no
reason for it to be in a class at all - create_user, create_friendship
etc can just be methods sitting in a module. If you have a whole lot
of services that are logically similar, you might want to split up
your tree thusly:

/myapp
models.py
views.py
/services
user.py
invitation.py

and then put your method definitions into user.py and invitation.py as
appropriate, import the 'service modules' as required, and invoke the
methods:

from myapp.services.user import create_user
...
def myview(request):
...
create_user(some, arguments, here)

In short - not everything is an object, if a method doesn't have state
there is no reason for it to be bound to an object, so don't try to.
Inhale deeply the Pythonic goodness, and the world will be a much
groovier place :-)

> These services would be instantiated as singletons which would then be

Ah.. the singleton... Java's answer to the fact that not everything is
an object. :-)

If you _really_ need your OO fix, consider this - in Python, modules
are objects anyway, so putting the create_user method in the User.py
module is kinda like creating a singleton User object with a
create_user method.

Yours,
Russ Magee %-)

Michael Burton

unread,
May 2, 2008, 2:11:59 AM5/2/08
to Django users
> Ah.. the singleton... Java's answer to the fact that not everything is
> an object. :-)

Ha ha, fair enough Russ :) And I don't dispute a certain java-
oriented predisposition to the way i think about these problems. But
let me bring a point in favor of the Singleton, and why I think it
might actually remain relevant even in the world of python.

[ First though, we're digressing a little off topic, so I wanted to
thank Michael Elsdörfer for his point about a services directory.
That's probably what I'll end up doing here once my services.py module
grows too large. Cheers]

Static methods are generally speaking not a good replacement for
singletons, in as much as they force you to mix your configuration/
setup with your execution. There's no good way to pre-configure a
static method (without resorting to globals), and then use that
configured method later. As a case in point, I was recently reading
James' very good article about using context processors (http://www.b-
list.org/weblog/2006/jun/14/django-tips-template-context-processors/),
where he suggests using RequestContext instead of Context to render
your views.

It's a very good idea, but it suffers from a DRY usability concern
related to the pervasive use of the render_to_response() shortcut
commonly employed by Django apps. Because render_to_response() is a
static method, there's no good way to pre-configure it to use
RequestContext instead of Context, which means that you must ensure
that you must set context_instance in EVERY use of render_to_response
in your view. eg. render_to_response(...,
initial_context=RequestContext(request)). If you ever forget this one
detail, you might end up with a funny looking page.

If render_to_response() belonged to a singleton, though, then we could
configure that singleton up front to use RequestContext as its
context_instance, and none of our views would ever need to worry about
that particular detail. You might imagine something like the
following in django's core library:

class RenderService:
context_instance = Context()
def render_to_response(*args, **kwargs):
...

render_instance = RenderService()

In my views.py I might want to use the default config and do something
like the following:

from django.shortcuts import render_instance
def my_view():
return render_instance.render_to_response(...)

But in my complicated_views.py, I might want to do something a little
fancier using the RequestContext instead:

from django.shorcuts import RenderService
render_instance =
RenderService(initial_context=RequestContext())
def my_complicated_view():
return render_instance.render_to_response(...)

My views methods never need to know the details about which context is
being used. They just set up their contexts and render their views as
normal.

(I do recognize that there are a couple of gaps in this example, in
particular how to get the request object to the RequestContext, but
please don't hold that against my defense of the poor concept of the
Singleton. Hopefully the benefits of the singleton still come across.)


Basically, using a Singleton[1] allows us to use the default instance
supplied by the module for most cases, or instead use our own user-
supplied instance if we desire specific configuration, all without
requiring us to painstakingly modify every call to
render_to_response() in all of our views. And it also protects us
from one module making a global change to the render_to_response()
configuration in a way that unexpectedly affects other modules.[2]

So I think the Singleton is still relevant. Perhaps it doesn't have
to be a class instance in python, perhaps there is a way to make a
python module be the singleton instead. I suspect not since I don't
se how you could avoid side-effects in that case, but I'm still new to
the language.

Cheers,
Mike


[1] One might claim that our Singleton in this example is really more
of a pseudo-Singleton, since we allow the user to override the default
instance with one of their own, should they so desire.
[2] It appears that Random in python's core library does something
like this, by using a "hidden" Random instance that you can override
with your own instance if you need to.

flo...@gmail.com

unread,
May 2, 2008, 2:29:03 AM5/2/08
to Django users
> It's a very good idea, but it suffers from a DRY usability concern
> related to the pervasive use of the render_to_response() shortcut
> commonly employed by Django apps.  Because render_to_response() is a
> static method, there's no good way to pre-configure it to use
> RequestContext instead of Context, which means that you must ensure
> that you must set context_instance in EVERY use of render_to_response
> in your view.  eg. render_to_response(...,
> initial_context=RequestContext(request)). If you ever forget this one
> detail, you might end up with a funny looking page.

I'll have to disagree with you here. I think this is a perfect
example of Russ's point. Somewhere on your PYTHONPATH just write a
function that looks something like this (warning, untested code):

from django.shortcuts import render_to_response as old_rtr
from django.template import RequestContext
def render_response(request, *args, **kwargs):
kwargs['context_instance'] = RequestContext(request)
return old_rtr(*args, **kwargs)

Now all you have to is import your new render_response method and use
that instead of the default render_to_response.

Michael Burton

unread,
May 2, 2008, 3:03:19 AM5/2/08
to Django users
> I'll have to disagree with you here.  I think this is a perfect
> example of Russ's point.  Somewhere on your PYTHONPATH just write a
> function that looks something like this (warning, untested code):
>
> from django.shortcuts import render_to_response as old_rtr
> from django.template import RequestContext
> def render_response(request, *args, **kwargs):
>     kwargs['context_instance'] = RequestContext(request)
>     return old_rtr(*args, **kwargs)
>
> Now all you have to is import your new render_response method and use
> that instead of the default render_to_response.


Very cool. I must admit I quite like the flexibility of python's
import statements. I'll give this a try tomorrow but at first glance
it looks quite flexible. The one catch that I see is that you still
have to go through all your views and make sure that you're now
passing in a request object, and if you miss any then you won't
necessarily know until you notice that your page is rendered funny.
On the other hand, the proposal I put out there earlier didn't really
solve this problem either.

Thanks for the tip!

Mike

Peter Rowell

unread,
May 3, 2008, 1:51:29 AM5/3/08
to Django users
> The one catch that I see is that you still
> have to go through all your views and make sure that you're now
> passing in a request object, and if you miss any then you won't
> necessarily know until you notice that your page is rendered funny.

No problems, mate. :-) All Django views a) take a request object as
their first arg, and b) return an HttpResponse object.

Kartik Arora

unread,
Jan 3, 2018, 7:03:23 AM1/3/18
to Django users

The difficulty you're having here is that you're trying to write Java
code in Python. :-)

Unless there's something stateful about the UserService, there's no
reason for it to be in a class at all - create_user, create_friendship
etc can just be methods sitting in a module.

A little late to the party I am, about 10 years :P. Hope I get a comment back
So I too have just entered this realm of python. This is exactly how I would describe my current status.
 

If you _really_ need your OO fix, consider this - in Python, modules
are objects anyway, so putting the create_user method in the User.py
module is kinda like creating a singleton User object with a
create_user method.

I didn't really understand this statement (again Java baggage). So we used to have an option there to declare these Singleton-Services in Java as Transaction, this helped us in keeping the logic implementation thread safe.
 
Reply all
Reply to author
Forward
0 new messages