I've seen two patterns in use, and neither is completely satisfactory.
I'm wondering if there are possibilities I've overlooked.
The first technique is to put all of the logic in the model class. This
leads to large model classes, and combines all of the field lists with
the logic to manipulate them.
The second technique is to leave the model classes as bare as possible
(basically just Field declarations), and create a separate module full
of helper methods that has all the logic. This means the objects don't
manipulate themselves, and you have to go back to non-OO techniques of
passing passive data structures into un-classes functions.
Is there some way to put the model and the logic in two separate places
but somehow end up with an object class with real methods? Am I making
any sense?
--
Ned Batchelder, http://nedbatchelder.com
If I'm understanding you correctly, I would argue the logic goes in
the model class. Models are intended to contain all data and logic
about that type of object; this way, everything model-related lives in
one place. As you point out, this means sometimes models have a lot of
methods, but I don't see how that's a problem in practice. Does this
answer your question?
Adrian
--
Adrian Holovaty
holovaty.com | djangoproject.com
I think what you may want to do there (and this may be a shot in the
dark) is to use a class for your view instead of a function. There's
nothing that says a view *needs* to be a function, it's just the most
common way to do things. Something like this might work for you. In
views.py:
class MyController:
def __call__(request, param1 param2):
self.someLogic()
return render_to_response() # <-missing args of course
def someLogic(self):
# do some business logicy stuff here
myview = MyController()
Then you can put the path to myview in your urls.py
HTH
Joseph
Good question, Ned!
I usually end up with what's usually thought of as "business logic"
in my view code -- but that's a byproduct of the fact that I'm
working 99% of the time on web views, so most logic I need to write
is concerned with aggregating/filtering data for display.
The times when it's not, though, I do struggle with the two different
patterns you talk about, and like you I'm not totally happy with
either of them. I'd say I more often do the "helper module" thing,
but I often later forget about that module and am surprised not to
find it somewhere more obvious.
Any ideas?
Jacob
> Is there some way to put the model and the logic in two separate
> places but somehow end up with an object class with real methods? Am
> I making any sense?
That sounds pretty much like a mixin, and these should be possible in
the magic-removed model classes, though I haven't tried. There might
be some issues getting mixins to work with new-style classes and the
model metaclass.
Luke
--
"Making it up? Why should I want to make anything up? Life's bad enough
as it is without wanting to invent any more of it." (Marvin the
paranoid android)
Luke Plant || L.Plant.98 (at) cantab.net || http://lukeplant.me.uk/
As a practical matter I find that I need to have the model at hand to
write helper functions, so for that reason alone I've come to
appreciate having them all together, ActiveRecord style. I may feel
differently after the models themselves have settled down (i.e. when
I'm no longer adding/changing fields weekly).
pb
We use two modules per table, one has the "manager class" and the other
modules has the bare model class and the "managed class." For example,
if the name of the table is "servers", we then have serversmgr.py and
servers.py.
In servers.py you'd have two classes ServersData (the bare model) and
the Servers class, which has all the logic related to servers and
methods that operate on themselves, e.g., Servers.getIPaddrs().
Business rules' validators go in here too like say a "powerOn" method
that ensures it can only be powered on if it has an IP address. The
Servers class has a class attribute called 'data', which is a reference
to an object of the ServersData class establishing a "uses"
relationship with the model. We use duck typing here so we can replace
the data object with a test model that "fakes" the database stuff and
to use the same "logic" class with different models "adapted" to our
interface, e.g., SQLObject, Django Models, or our own ORM. For
example, the Django version of ServersData would import the django
model from django/models/servers.py or django/models.py or wherever it
is and then wraps it with our own interface methods, e.g.,
ServersData.store() would call Django's save() method.
In serversmgr.py we'd have the ServersMgr class, which has all the CRUD
methods, e.g., ServersMgr.new(), ServersMgr.delete(),
ServerMgrs.update() and other stuff like ServersMgr.list() and other
general methods like instantiators, e.g.,
ServersMgr.Object(name='fooserver'). In general, all the methods that
operate on or produce the corresponding managed objects. (I guess
these are what you called unclassed functions.)
We also have a set of abstract base classes, MgrBase and ObjBase, where
we put generic manager methods that operate on generic managed ObjBase
objects. As you can guess I bet, ServerMgr is a subclass of MgrBase
and Server is a subclass of ObjBase. The MgrBase class has a class
attribute called moc (Managed Object Class), which in this ServerMgr
class example will have it set to Servers, e.g., moc = Servers. This
allows us to write generic methods in MgrBase that do generic stuff
like self.moc.__name__ or instantiate an object by calling moc(*args,
**kwds). The moc attribute is what links the manager module with the
managed module.
Similarly, ObjBase subclasses implement concrete versions of abstract
methods like getMyManager() so that you write generic methods in
ObjBase or MgrBase that operate on an object without caring what kind
it is, e.g., mylist = object.getMyManager().list(**kwds).
As you can see, this framework keeps the model completely bare and
separates logic between the manager (list) and managed (validator)
classes allowing the view to use the managers as the first point of
contact.
I'm sincerely curious to know what you guys think about this framework.
--
Luis P Caamano
Atlanta, GA USA
Manager pattens remind me of Java. Here's the problem I've seen there.
Manager classes become huge ("Hey, I'll just add getByFoo and we'll be
sweet") and they *still* get in the way when it comes to either schema
evolution, or doing something unexpected but useful real quick.
Which is to say in Java land we used to start with manager classes and
wish for generic DB APIs (like, we did that for *years*, until
Hibernate/Ibatis came along). I've never seen any interesting db-backed
app not need to go round Managers eventually to get something done, ergo
DB APIs rock - and if things crystalise you can refactor the call into
the Manager. But given the quality of DB apis these days, I wouldn't
start with Managers.
So, imo, do what Adrian said and if you can't see the model anymore,
create a Manager class to organize the code (that's what OO is for
ultimately). At that point I think to be idiomatic and consistent across
your domains, put the Manager inside the Model:
class DomainModel(models.Model):
# fields
class Admin:
pass
class Meta:
pass
class Manager: # 'logic' funkiness here
pass
my 2c
cheers
Bill
I'll just point out here that Django's ORM is in some sense
*deliberately* underpowered in recognition of this fact. At times
it's easier to Just Write SQL™ instead of trying to kludge an ORM
into doing the right thing.
In other words:
DB-API: too cold
Hibernate: too hot
Django: *just* right
(Which I suppose makes me Goldilocks, and I'm not sure how I feel
about that.)
Jacob