views:

36

answers:

1

I am working on an app which would enable a preview function for a model. Models marked as preview-able would allow for changes to be made in the Django admin interface and previewed on site using the same view as would an object of that type normally use to render itself, but rendered instead with the new (unsaved) object in it's place.

This is a pretty easy task to do in a bespoke fashion when you know the views or templates ahead of time. But I want this to be reusable and simple.

What I Was Thinking

My idea would be to apply the resolve() urls function to the existing (saved) object's get_absolute_url() value to discover the view used dynamically. Then call that view, get the returned HTTPResponse and alter it in some fashion before returning it myself.

The Problem

It seems that by the time the HTTPResponse is returned by the object's natural view the HTML has already been rendered by the template engine.

So I guess the question is: Is there a way to get at a HTTPResponse before the template is rendered and alter the context variables before that happens.

If not then could the same idea be implemented in another fashion. Would using middleware change anything (your still working with a HTTPResponse object there as well).

Thanks,

Marcus

P.S. If you come up with a radically different methodology to solve this problem, I will be sure to attribute that concept to you in the app documentation (despite it being a small app right now).

+1  A: 

It is not trivially possible no, the easiest way would actually be to write your own template context processor that checks for example if something like GET['preview'] is set, then sets dictionary values based on some other GET or POST data. Furthermore when other variables are added it should make sure these don't overwrite the existing values set by this method (otherwise the view would override it anyway with some other data).

One remark however: completely unintrusive behaviour will often lead to erroneous behaviour. If the view does not know of this preview functionality and e.g. it expects a valid id or redirects to an error page, your preview won't work (as you don't really have a valid id). Choosing for views that know of this preview functionality is indeed some more work but will certainly be correct. You could try to make it more generic by using decorators or callable view classes (which can be derivable from some generic base) instead of view methods.

A completely different approach that is only slightly 'view' intrusive, I assume you do not want to save the model so it doesn't show up in public listings. An approach could be to add a 'preview' field and use managers to restrict lookups, so something like this:

class NoPreviewManager(models.Manager): def get_query_set(self): return super(MyModelManager, self).get_query_set().filter(preview=False)

class MyModel(models.Model): ... #other fields preview = models.BooleanField()

objects = NoPreviewManager()
allobjects = models.Manager()

In all normal views you can just use MyModel.objects so previews won't be listed. In the object-specific view you use MyModel.allobjects to also enable defailed views of previews. This way you don't have to do weird view hijacking things, but you should take care preview objects get cleaned up if they aren't promoted to real objects. Note that you can also combine many of this logic into a base class.

KillianDS
I think the template context is probably the only way I could use an existing view and get my variable in there. I will probably as you say need to be a bit more intrusive than I had originally intended. For example it may be a good idea to let the developer set up the URL scheme by which their models will be preview-able, with just a little bit of manual work, resulting it much more flexibility.
Marcus Whybrow