[Another in a series of posts about moving from Django to Plone. I’m a Plone/Zope newbie writing about my bafflements and enlightenments as they happen.
Some of my opinions are certainly wrong. I’m writing this in the expectation that the history of my meandering learning path may be useful, or at least entertaining, to future Plone newbies. If I sound wordy today, it may be because I just watched Good Night, and Good Luck. on my Blu-ray player. (The difference between Edward R. Murrow, and the regurgitating talking heads of today’s television, is a sad thing to contemplate.)]
I spent only half of this week on Plone/Zope. Here’s some of what I bumped into.
Portlets, Viewlets, and Content Managers
I’ve been puzzled about the big conceptual difference between portlets and viewlets. Not until I read page 234 of Professional Plone Development did I understand that it was in their invocation model! A viewlet will generate page content anywhere in the page when a viewlet manager deliberately invokes it. A portlet may generate content in the left or right column, automatically.
Although an interesting distinction, I still don’t see a good reason for portlets’ existence. Instead of a “portlets” entity, why not instead give viewlets a hook to indicate they’re an automatically-invoked-left-or-right-column object? It could be something as simple as the existence of an automatic_render()
method in a viewlet object. If that method is defined, it signifies the object as a candidate for automatic rendering in the left or right column.
Because this is an obvious point, and the Plone/Zope community contains so many smart developers, I’ve got to be missing some other difference. And so I am very likely an ignoramus. These aren’t the droids you’re looking for, move along…
Portlets don’t exist in Django, but they’re easier in Django
I guess you’d make Django “portlets” by putting a loop in a left or right column’s <div>
. The loop would call all of the portlets’ functions or methods. Each one would decide whether to return HTML, based on business logic. You’d iterate over a set or list of portlets, with each function checking its display criteria. So, the View (template) would call the Controller (“portlet”) code, which decides whether to display HTML. And you’d have to code this up manually.
In Plone, the relationship (unless I’m wrong about this…) is flipped. The Controller (portlet object) decides (apparently…UIWAT) whether to be displayed within the View (page template’s columns) without being deliberately called.
Although Django doesn’t have this functionality built-in, any halfway-decent Python developer could create a Django portlet system in no time.
Zcatalog, Holy Crap
ZCatalog is powerful, but I’m missing how it’s hooked up to search forms.
It seems like a ZCatalog is used if an <input>
tag’s name=xxxx
attribute matches an index; and a report form is used if it references a ZCatalog by name. But that’s too fragile a relationship definition to be viable in a large application. The linkage must be defined in some XML or interface file, but I’m too dense to see it right now.
Zope 3 interfaces: Gaaaa!
Zope 3 Interfaces leave me cold. They smell like a traditional language’s way of protecting the programmer from herself. It’s a layer of Java-esque crap static typing layered over Python code.
When I consider using a preexisting code package, I don’t just close my eyes and copy it over blind. First, I’ll read the documentation, and search for published reviews. I’ll then evaluate its support and license. Then, I’ll look for example or demo code. Only after all that will I decide whether to use it, and if so, do the necessary coding. Since I’m doing all that anyway, of what value is an interface definition?
Maybe I’ll rue these words in two months, but I think separate interface definitions are syntactic sludge. I’m hoping for an experienced Zope developer will show me that I’m dead wrong.
Aspect-Oriented Programming: Gaaaaaaaaaaaaaaaaaaa!
Either Aspect-oriented programming (AOP) is grotesque, or I’m a software Neanderthal.
I appreciate the problem definition. Briefly: Some code (like logging code, or user authentication) can be scattered throughout an application. This makes it hard to share OOP source code, and increases maintenance costs. AOP tries to mitigate this problem by defining cross-cutting code as aspects, and providing ways to declare when such aspects should be invoked.
I have two complaints with this.
First, the problems aren’t as painful as they’re made out to be, in my experience. I’ve never seen a project get hung up by changes to its logging code; in fact, logging calls are the most painless code in a project. Authentication code can be problematic, but hassles with it are usually due to poor design decisions within the authentication package, and not in having to add authentication calls around other code.
But let’s forget all that. And, let’s pretend like the problem, as described, is a big deal. Now I get to my second problem, which is this: When I read through what AOP makes you do to solve this problem, I want to gouge my eyes out with a yogurt spoon. Join points, pointcuts, advice, adapters, interfaces, adapter factories…man, you’ve got to be kidding me! All of that is better than giving code you’re thinking of using a once-over?!? I don’t think so.
I don’t think you understand the difference between viewlets and portlets. 🙂
A viewlet is something a developer writes. We have a pluggable UI where people can define “holes” (viewlet managers) where a developer can “plug in” a viewlet. It’s a configuration thing that requires a Zope restart to change (though you can hide and re-order things at runtime).
A portlet is something an administrator uses. You have a GUI to add/remove and configure a portlet, and rules about how they’re blocked and how they interact. Crucially, there is a per-portlet persistent configuration, and you can have as many instances of a given portlet as you want.
… and on the catalog: You search the catalog however you want (see chapter 9 of my book). The default search form has a way to pass request parameters directly to the catalog, but that’s just a feature of the default search page, nothing more.
… and you’re dead wrong about interfaces, but we’ll leave that for another day. Suffice it to say that for the component architecture to do its magic, you need to be able to describe a component separately from its implementation. You don’t *need* to use interfaces unless you’re trying to make things that are extensible or overridable or you’re trying to use the CA as a registry for something. Having a backlash against them just because Java has them is not very sensible, though.
… and similarly, you don’t need to use the CA (what you call APO) in your own code. But when you decide that you want to customise the way the navtree renders itself for a particular type of folder only, and you don’t want to copy and paste the five or six bits of code that constitute the navtree, I bet you that you’ll be glad we made it possible for you to register an adapter for your interface that provides a new navtree strategy.
I appreciate that it’s all a bit much to take in at once. Plone and Zope are very different in philosophy than Django, and solve different types of problems. However, the things that are there are often (but not always) there for a good reason. 🙂
Martin
Let me see if I can shed some light for you:
Portlets are an artifact since the beginning of Plone. Typically, they’re the boxes on the left and right side of the Plone site. The classic portlets were implemented as a macro (a bit of PT code) in a template, somewhere, and you pointed to those macros in a “magic property” called left_slots and right_slots. This property you could set on every object and through the acquisition and the skin machinery, the portlets appeared on the site, but you could override them and even define new ones, per location. Modern Plone portlets (and here I might be wrong – I haven’t been heavily involved with Plone recently) have a complex implementation, partially based on the concept viewlets.
I think you got it wrong when you say you understood the difference between portlets and viewlets. Both will be inserted automatically, in the sense that a Plone portlet manager is a content provider, which means you need to insert it in the main template if you want its associated portlets to appear. Viewlet managers are content providers as well. The main difference, I would say, is that Plone portlets managers are “plonified viewlet managers”: they are persistent and you can define their behaviour through the web.
Now, viewlets are a completely different thing. A viewlet doesn’t need to be a box on the left side or right side. A viewlet is a “small view”. A view, in z3 terminology, is something that you use to render a certain detail of an object. A view can be a page or just a small html fragment that you can insert into other views. Viewlets are rendered inside viewlet managers. A viewlet manager is a piece of code that “gathers” all the viewlets that can be rendered for a particular context (let’s say object), sorts them and renders them in a particular way. In practice, viewlets can be anything: menus, boxes of content, a list of actions that you can do, etc. One of the advantages of using viewlets (and here is an application of the interfaces), is that you can render conditionally. When you register a viewlet you need to specify the type of the context (is it a Folder? An image? A homefolder? etc), the request (which means you can have different implementations of the same viewlet per skin), the View (typically, the page that is rendered into) and the manager in which will be rendered. All these types are typically specified by interfaces, not by classes. You could specify by classes, too.
Now, you say that you can implement this with Django as well. In your example, you say that the code that renders that viewlet, or HTML bit, needs to know about the context where it is inserted. Indeed, but then why should you add more code in the viewlet that decides if the viewlet should render or not? Using the viewlet/viewlet manager/content providers mechanism you can have the viewlet manager decide this for you, based on the registration of the viewlet. The thing is, it’s hard to compare code from Django with code from Plone. I have almost 0 experience with Django, but my understanding is that the applications that you do with it are focused. You could probably insert the mechanism that would discriminate agains the context in the viewlet code and be done and happy with it. Plone wants to minimize the code needed and provide standardized ways of doing this very typical problem.
Zope 3 Interfaces have multiple purposes: API documentation, model definition (using zope.schema), can be used in form generation and validation and, using zope.component, they can be used to create a plugable infrastructure. A zope interface can’t force you anything. Really. It’s not static typying! As it’s typical in Python, it’s a gentlemen’s agreement. If you say that your class implements an interface, it’s up to you to actually implement that interface. Even more (and I think Django models are more restrictive in this aspect), you can define “invariants” or constraints for the attributes of the objects derived from that interface, but you need to call this validation functionality by hand. In general, you could say that you can define behaviour with them. Oh, one more thing, this might help you in understanding the lingo from the docs: classes are declared to implement interfaces, objects constructed from those classes are said to “provide” those interfaces.
ZCA (Zope Component Architecture) has several major types of components: adapters, utilities, subscribers and sites. A component is an object that provides an interface. Adapters have certain AOP connections to them: they allow you to see an aspect of an object. The typical basic example of an adapter is the ISize interface. When you have a folder listing with a mixed variety of objects inside, you want to have a standardized way of displaying the size of those objects, even if your framework doesn’t know anything about the objects. For example, an image could list its pixel size, a file its byte size and an mp3 its duration. By using a standardized interface, ISize, you can have different implementations: for each object type you provide an adapter that implements the ISize interface for that object type. Zope 3 views (or pages) are named multiadapters: they adapt the request and the context to the IView interface (or IBrowserView, if you’re dealing with an http application), to produce a view. This multiadapter is registered with a name, the name of the page.
Utilities are objects that are registered as “singletons” for a particular interface. They can also be registered with a particular name. This makes it possible to define pluggable and overridable implementations of site behaviour, for example, the language negociation of the site. Sites are objects that have a site manager associated to them (you would implement a portal or a website in its own site) and this site manager has a component registry associated to it, which means that you can create persistent utilities and register them for certain interfaces, at site level, overriding the utilities that are defined in sites above the hierarchy or utilities that are registered in code. Subscribers are a way to associate callables to interfaces. This makes it possible to implement, for example, events.
I can understand your frustration in learning Plone. It is unfortunate that, right now, learning Plone means having to learn multiple ways of doing the same thing, of which one is deprecated but you still need to know about it. Transitions are never easy. Hopefully in a couple of years this situation will be solved and the Plone learning curve can be significantly flattened.
Now, the zope catalog: it indexes objects attributes and allow you to search through them. By doing a search, you specify what index is used, the value you want to be searched in that index. The search will return you with a list of “brains”, which are small objects that contain information about the object that was found as the result of the search. This is an optimization. It does not return directly the object and instead returns the brain because this brain will contain the metadata that was extracted from the object, and “waking up” objects is an expensive operation in ZODB, while the catalog index will typically be cached by zope. Of course, you can get the real object by calling one method on the brain. In a Plone catalog you define indexes and metadata. The indexes are used for searching, while the metadata “indexes” are used to define the information that will be stored in the brain. When you define an index you specify the attribute (or method) from all content objects that you want to be indexed. When you save an object, the catalog will use all the indexes defined to extract information from that object, and all the metadata will be stored associated with that object.
Hope this is helpful! 🙂 One more piece of advice: the Plone community is usually a friendly one, and you can usually get imediate help for any question throught he #plone @ irc.freenode.net IRC channel. Just ask the question, and you’ll probably get an answer.
Interfaces rock! 🙂
At the point when you are using a package and “want to do the necessary coding” is one of the sweet spots of interfaces. An Interface is simply formalized documentation. Think of an interfaces.py file in a package as an “API Reference” for that package. The README.txt for a package will give you an overview of the purpose of the package, and some examples of how to get started, but when you need to look-up specific details such as method names,
signatures and a documentation on that method, then interfaces.py is the place to look. Putting important interfaces in interfaces.py is nice because
you can look at the description of the API of an object you are looking at
without being distracted by the implementation details … I know I tend to
sometimes wander beyond just looking up the details of a method call when
presented with a big juicy file full of code n’ documentation.
The interfaces.py file is the second place I look when exploring a Python
package new to me. First is README.txt to see what the package is about,
then a glance at interfaces.py to suss out the size and scope of the published
APIs of the package.
Interfaces are also now in the Python standard library as of the 2.6 release
(under the guise of Abstract Base Classes) – and no, Python is not becoming
statically typed!
Read Jeff Shell’s article on the subject of Interfaces and ABCs, it’s quite
informative:
http://griddlenoise.blogspot.com/2007/05/abc-may-be-easy-as-123-but-it-cant-beat.html
“””Zcatalog, Holy Crap”””
Your observed behavior is intentional and documented (see The Zope Book 2.7 edition). In addition: you can define custom attribute and method names to be in indexed. So the name of the index is definetly only lously coupled with the data to be indexed. This also implies that the search is not tied to the names of the indexes. In reality you usually have a layer sitting between the form and the ZCatalog performing modifications to the search request data.
-aj
first of all thanks for your posting: those reports help us to figure out where we need to do a better job of explaining our approach.
further to what Martin responded already:
regarding the CA: the top-level answer is: divide and conquer.
If you are interested, here is a bit of background/history: one of the mistakes Zope (2) did (in retrospect) has been to overuse multiple inheritance to make objects that could be used in a variety of ways. It turned out that this approach found its limits when it came to extensibility and reuse by other applications (and more so frameworks of course) and their extensions. So the next generation of Zope (3) adopted a completely different approach: the CA. It’s all about pluggability, extensibility and customizability. But also about making the code base more managable by breaking it down into smaller, more managable pieces (even if it doesn’t always look like that).
From a more down-to-earth perspective: how do you want to deal with situations where add-ons (in Plone’s parlance ‘Products’) want to change or expand the stuff that’s there by default (e.g., adding fields to the schema of (a subset of) the default content types or adding further views – in the sense of further output formats – to objects defined in yet another add-on)?
Sure enough: as long as you are in control of the entire code base of your application all those problems seem to be non-existent. But as soon as you start considering integrating third-party code (or share your own) with people that don’t program your perspective will change completetly.
Last but not least: with Plone we are trying to reach beyond the developer. This is why (web-based) configurabilty is so important to us. Again, if your use cases don’t include situations where sites are managed by non-developers (aka people who neitehr know Python nor have file system access at the server) you might consider this complete overkill but we do believe it makes sense and it addresses real-world situations.
And finally regarding viewlets versus portlets: in addition to what Martin mentioned already: there is also a bit of history or more precisely learning experinence involved. First we had portlets (which were quite simply managed prior to Plone 3 but that has proven to be too naive in the long run) and later viewlets were introduced based on the experiences made from the portlets overhaul. That said, some people are thinking about how to integrate this in the long run.
To your catalog remark:
http://plope.com/Books/2_7Edition/SearchingZCatalog.stx
Cheers,
Raphael
Thank you to all of you who commented so far! I’m in awe of your willingness to take the time to correct and help me on this stuff. I hope someday I can do as well with helping someone else learn Plone and Zope.
I can see some of your points clearly, while others are still murky. Yes, I concede that I jumped the gun at assuming
Zope Interfaces == Java Interfaces
. 🙂 I haven’t been thinking about developers vs. administrators. Is setting the bar that administrators should know Python so bad? It’s easy to learn, although, sure, XML is easier…I’m going back to my books to continue learning!
I’ve been through Plone for twice now, and I agree with your view of the Plone/Zope world. Too much complication for too little gain. You end up never needing the full power the CA intends to achieve, and when you need it for use cases that were not foreseen by the Plone/Zope developers you end up in a dead-end.
Other Zope3 technologies were supposed to be improvements over the earlier ones, but I for one think Zope is plainly replacing one mistake (the inheritance issue that was cited above) with a bigger one – trying to view the whole world through interfaces/adapters.