I've been wondering lately — what good having the server-side web application generate URLs when the client side code is just as capable? Indeed, there is no way we'd ever want to hard-code the application URL paths inside the Javascript code. So why even entertain the notion?
Speaking in terms of overhead — with each URL on a given page, some CPU expense is incurred when constructing each URL. I'm making a presumption here in assuming URLs are in fact being constructed and not just hard-coded in templates. In reality, the overhead is inconsequential, but in some generic circumstances, I see absolutely no reason why these URL's can't be assembled locally on the user's browser.
Why URLs Are Constructed
It's for the best that URL's passed to the browser by the web application are constructed rather than stored statically. Ad-hoc construction of URLs doesn't solve any problems either. For example, if on one page you're formatting a string that'll be returned as a URL, and on another page, another set of URL construction functionality, it's difficult to establish patterns of reuse. Sure, we're able to share some of this code among our application's components — but why should we have to define this capability ourselves?
Look no further than Django's URL resolution architecture for an example of how URLs are specified in one place and one place only. These are base URLs — they can take on different forms by inserting values into path segments. For instance, search strings or object identities.
The rest of the Django application can construct — resolve in Django-speak — the URL to use. What I really like about this methodology is that URLs are treated just like classes in object oriented programming languages. The URL blueprint is defined once, individual instances of the URL are constructed as many times necessary. Some URL instances are simple — they take no additional attributes. They have no path segments to fill in or no query strings to append. In the case of more complex URL instances, we have the URL resolution framework at our disposal — a consistent approach to instantiate our URL blueprints, passing in any additional details the URL class requires in order to make them unique and identifiable.
The theme here being that URLs can, and should be, a generalized concept — just like other systems we build. We're able to abstract away much complexity into classes, instantiating objects, letting the class machinery take care of eliminating what would be otherwise redundant repetitions of URL construction code. Sticking with the Django URL resolution architecture as the example, because it illustrates so well how URLs can be constructed, consider user interface code. The templates. The Django template system comes with a URL tag to help instantiate URLs directly in the user interface. Again, when templates are rendered, we're injecting URL instances into the template. It's from here that we can follow the logical extension into generic URLs in Javascript.
Javascript And URLs
Javascript can do a lot with URLs that the web application, running on the web server doesn't necessarily need to deal with. But why would we want to use Javascript? Shouldn't we stick with the URL construction kit offered by the web application framework? Well, for the most part, I'd say yes, it is a good idea to use things like the url tag, if you're building a Django template. Stick with the infrastructure provided for building URLs and you'll never worry about whether the correct URL is passed to the user interface. Just reference the URL blueprint by name and let the URL instance take care of the rest.
User interfaces in web applications follow patterns. That is a given. From one page to the next, controls repeat themselves. Things like pagination come to mind. Any given page with a list of objects on it uses pagination controls as a means to avoid dumping every object on a single page — making it impossible for the user to navigate in a cogent manor. The common items, or, generic items rather, are the list and the paginator. The list of objects and how they're rendered can be generalized inside the Django template. As can the pagination controls. These controls move back and forth through a set of objects — usually by appending constraints on the URL.
So how does this paginator change from one set of objects to the next? How does it distinguish between one type of object list and the next? Well, the reality is that it doesn't change much for different object types, or, at all. Forward in motion and backward in motion. Generic behavior that doesn't necessarily warrant using the URL resolution system. Any Javascript code that runs on the page already knows about the current URL — just look it up in the window object. We know how to move forward and backward through a list — just update the current URL. For instance, the browser URL is simply updated from /objects/page1 to /objects/page2. There isn't a need to invoke the URL construction code for this — let the client take care of generic things like this where it makes sense while saving server CPU cycles for more important things.
Thursday, November 17, 2011
Thursday, November 10, 2011
How To Gamble With Software
Casinos are a place of pure excitement — each visit an exercise in frenzied fun. The short-lived thrill of a large payout is tough to beat. But then, it's over. Better luck next time. Who could possibly enjoy forfeiting their money like this? Sure, you have great earning potential, but the odds are simply not in your favor.
Gambling, casinos, compulsive habits — software? Yes. And it's easier than you think to make ludicrous decisions during the development of software products. Do they seem out to lunch at the time? Of course not. You're a good developer with common sense. You've toured the neighborhood and have street smarts. And yet, you still make bets that aren't in your favor.
The Big Picture
The largest bet we place on our software projects is whether we do the project at all. What we're setting out to do, to sell, and to solve — this is by far the riskiest job. At inception, there is no product. There's no project either. Because, at this point, everything is still just an idea. At this stage, we're still outside of the casino doors.
Jumping from inception to real projects is the ultimate dice roll. Here, a bold statement is made. We will deliver the best open source project management system. We will turn around this customer portal in six months. We will sell enough of this video editing software to stay alive for the next six months. A gamble is only a gamble because we've set a goal. A tangible to aim for. Without a goal, it cannot be missed — and so you've nothing to lose.
This, in my mind, begs the question — are we better off not setting goals for software projects in the first place? Could we better operate as a software development team if the bar is lowered, if the threat of failure is eliminated? Of course, lowering the pressure — delivering production-ready code fast — endows a degree of competitiveness. Without predefined goals, and conversely, consequences of not meeting those goals, we're more inclined to make the right decisions during the development effort. More focus is targeted at product design. We can evaluate alternatives, something that all too often evaporates in the face of pressure to deliver a sound product or service.
Is there a need to make big-picture gambles? Are there any rewards to setting goals and trying to exceed them? Or should we assume a passive attitude and simply do the right thing in terms of what we, the developers, think is right? The trouble is, not setting a tone — a sense of urgency on the project will ultimately dilute the pace of progress. How can you measure progress if you haven't a target to aim for?
Gambling Versus Guessing
I would argue that the big picture gamble — the decision to build and to solve and to move forward — is inevitable. The truth is, there is no such thing as safe software development with no risk because humans are responsible for building it. Humans are living organisms who only have so much time to devote — there are only 24 hours in a day. We build software because of an insatiable urge to build things. Not random things, but functional tools that serve a purpose and are of appreciable value. Is the software, when finished, going to be of relevance — to yourself or to anyone else? This is a gamble that is unavoidable — it's sown into the fabric of writing code.
The trouble for me, is, how do you know what makes a great software product? What really stands out above and beyond anything else? There is obviously something that makes this a reality for software projects because success stories are plentiful. But they do pale in comparison to failed software development efforts. These failed for there own unique reasons. There wasn't enough consumer adoption. There wasn't enough funding to support the continued development. The list goes on, but I do think there is one dimension shared between those that succeed and those that do not.
Gambling in software development terms isn't exactly the same as gambling in casinos. If you're hitting a slot machine, you've no control over the outcome. This is a pure gamble, win or lose. We're guessing that we'll win. Software development at least gives us some indicators — the kind that can assist with making informed, calculated decisions before we place our bets. This is better than guessing.
So to avoid becoming a statistic in favor of not starting a software project due to likelihood of failure, it's best to limit guessing. Guessing that something will just work. Guessing that your potential customers have a real need for your product and will applaud it. Guessing that using library abc over library xyz will be beneficial. These are all things that we can research and deliberate over. Not doing so means you're guessing, and doing so will lead to diminished control over the direction of your project. Sure, both successful and unsuccessful software projects make big gambles. Some win, and win big, others, not so much. But the ones who win big undoubtedly restrain the amount of guesswork — much more so than their counterparts.
Gambling, casinos, compulsive habits — software? Yes. And it's easier than you think to make ludicrous decisions during the development of software products. Do they seem out to lunch at the time? Of course not. You're a good developer with common sense. You've toured the neighborhood and have street smarts. And yet, you still make bets that aren't in your favor.
The Big Picture
The largest bet we place on our software projects is whether we do the project at all. What we're setting out to do, to sell, and to solve — this is by far the riskiest job. At inception, there is no product. There's no project either. Because, at this point, everything is still just an idea. At this stage, we're still outside of the casino doors.
Jumping from inception to real projects is the ultimate dice roll. Here, a bold statement is made. We will deliver the best open source project management system. We will turn around this customer portal in six months. We will sell enough of this video editing software to stay alive for the next six months. A gamble is only a gamble because we've set a goal. A tangible to aim for. Without a goal, it cannot be missed — and so you've nothing to lose.
This, in my mind, begs the question — are we better off not setting goals for software projects in the first place? Could we better operate as a software development team if the bar is lowered, if the threat of failure is eliminated? Of course, lowering the pressure — delivering production-ready code fast — endows a degree of competitiveness. Without predefined goals, and conversely, consequences of not meeting those goals, we're more inclined to make the right decisions during the development effort. More focus is targeted at product design. We can evaluate alternatives, something that all too often evaporates in the face of pressure to deliver a sound product or service.
Is there a need to make big-picture gambles? Are there any rewards to setting goals and trying to exceed them? Or should we assume a passive attitude and simply do the right thing in terms of what we, the developers, think is right? The trouble is, not setting a tone — a sense of urgency on the project will ultimately dilute the pace of progress. How can you measure progress if you haven't a target to aim for?
Gambling Versus Guessing
I would argue that the big picture gamble — the decision to build and to solve and to move forward — is inevitable. The truth is, there is no such thing as safe software development with no risk because humans are responsible for building it. Humans are living organisms who only have so much time to devote — there are only 24 hours in a day. We build software because of an insatiable urge to build things. Not random things, but functional tools that serve a purpose and are of appreciable value. Is the software, when finished, going to be of relevance — to yourself or to anyone else? This is a gamble that is unavoidable — it's sown into the fabric of writing code.
The trouble for me, is, how do you know what makes a great software product? What really stands out above and beyond anything else? There is obviously something that makes this a reality for software projects because success stories are plentiful. But they do pale in comparison to failed software development efforts. These failed for there own unique reasons. There wasn't enough consumer adoption. There wasn't enough funding to support the continued development. The list goes on, but I do think there is one dimension shared between those that succeed and those that do not.
Gambling in software development terms isn't exactly the same as gambling in casinos. If you're hitting a slot machine, you've no control over the outcome. This is a pure gamble, win or lose. We're guessing that we'll win. Software development at least gives us some indicators — the kind that can assist with making informed, calculated decisions before we place our bets. This is better than guessing.
So to avoid becoming a statistic in favor of not starting a software project due to likelihood of failure, it's best to limit guessing. Guessing that something will just work. Guessing that your potential customers have a real need for your product and will applaud it. Guessing that using library abc over library xyz will be beneficial. These are all things that we can research and deliberate over. Not doing so means you're guessing, and doing so will lead to diminished control over the direction of your project. Sure, both successful and unsuccessful software projects make big gambles. Some win, and win big, others, not so much. But the ones who win big undoubtedly restrain the amount of guesswork — much more so than their counterparts.
Friday, November 4, 2011
Dealing With Permissions In Generic Views
Generic views in Django cover a vast range of usage scenarios. That's why they're called generic views — because they implement the abstract patterns common to most applications that are using views. This a huge savings on code-writing cost because there isn't as much of it to write — or maintain thereafter. So, this all well and good, but what about the pre-generic days of writing Django views? Is there nothing of value there we can carry forward and exploit in our newly-fashioned generic views?
Of course, before we used class based views, there were still some degree of generic capabilities that could be attached to our view behavior. They came in the form of decorators. Decorators in Python are a realization of the decorator pattern — we're attaching additional responsibilities to the function. Or, in this case, our view function. But things aren't so straight forward in the brave new generic world of writing views that use classes. How can we deal with things like permissions in our generic views while still honoring the DRY principle?
The decorated permission approach
The authentication system that ships with Django includes the ability to only expose certain views to specific users. If we're writing views as functions, the authentication framework has decorators we can use to ensure the current user has the appropriate permissions. Like this...
This is an elegant approach to ensuring that only authorized user have access to my_view. Since the permission_required decorator is provided by the authentication framework, it's available for every view in our application. And, we only need to implement one line of code where we need to handle permissions. One per view.
The trouble with permission_required in modern Django applications is that they don't fit in nicely with the newer class-based generic view methodology. So how then, can we exploit the power of the generic views in Django while keeping the simplicity of the decorated permission handling?
The inherited permission approach
One thing that class-based views offer that decorators don't is the ability to define defaults that the rest of the view hierarchy in our application can inherit. These defaults include both data attributes and behavior. This is one approach we can use with our class based views to simplify permission handling...
With this approach, able to control permission-based access based on the HTTP method — all within a dictionary overridden by descendant classes. Here is how it works.
First, we're re-creating the base TemplateView class — called MyAppTemplateView. Any other template views used in my application are now going to inherit from this class instead of the standard TemplateView defined by Django. This is how we decorate each view with the added responsibility of ensuring access control.
The dispatch() method is the first call to action for any generic view. So it is here that we want to approve of any additional execution. What were doing here is really simple. We're checking if the user has the permissions defined by the perms attribute. And this is all we need to override. By default, this attribute is an empty dictionary — so no permissions will be validated.
The MyAppNews view simply overrides the perms attribute to ensure the requesting user has the myapp.can_see_news permission. It's not exactly a decorator, but we're retaining some flexibility while staying true to the DRY principle.
Of course, before we used class based views, there were still some degree of generic capabilities that could be attached to our view behavior. They came in the form of decorators. Decorators in Python are a realization of the decorator pattern — we're attaching additional responsibilities to the function. Or, in this case, our view function. But things aren't so straight forward in the brave new generic world of writing views that use classes. How can we deal with things like permissions in our generic views while still honoring the DRY principle?
The decorated permission approach
The authentication system that ships with Django includes the ability to only expose certain views to specific users. If we're writing views as functions, the authentication framework has decorators we can use to ensure the current user has the appropriate permissions. Like this...
from django.shortcuts import render_to_response
from django.contrib.auth.decorators import permission_required
@permission_required('my_app.can_do_stuff')
def my_view(request):
return render_to_response('my_template.html', dict())
This is an elegant approach to ensuring that only authorized user have access to my_view. Since the permission_required decorator is provided by the authentication framework, it's available for every view in our application. And, we only need to implement one line of code where we need to handle permissions. One per view.
The trouble with permission_required in modern Django applications is that they don't fit in nicely with the newer class-based generic view methodology. So how then, can we exploit the power of the generic views in Django while keeping the simplicity of the decorated permission handling?
The inherited permission approach
One thing that class-based views offer that decorators don't is the ability to define defaults that the rest of the view hierarchy in our application can inherit. These defaults include both data attributes and behavior. This is one approach we can use with our class based views to simplify permission handling...
from django.views.generic import TemplateView
from django.http import HttpResponseForbidden
class MyAppTemplateView(TemplateView):
perms = dict()
def dispatch(self, request, *args, **kwargs):
perms = self.perms.get(request.method.lower(), None)
if perms and not request.user.has_perms(perms):
return HttpResponseForbidden()
parent = super(MyAppTemplateView, self)
return parent.dispatch(request, *args, **kwargs)
class MyAppNews(MyAppTemplateView):
template_name = 'news.html'
perms = dict(
get = ('myapp.can_see_news',)
)
With this approach, able to control permission-based access based on the HTTP method — all within a dictionary overridden by descendant classes. Here is how it works.
First, we're re-creating the base TemplateView class — called MyAppTemplateView. Any other template views used in my application are now going to inherit from this class instead of the standard TemplateView defined by Django. This is how we decorate each view with the added responsibility of ensuring access control.
The dispatch() method is the first call to action for any generic view. So it is here that we want to approve of any additional execution. What were doing here is really simple. We're checking if the user has the permissions defined by the perms attribute. And this is all we need to override. By default, this attribute is an empty dictionary — so no permissions will be validated.
The MyAppNews view simply overrides the perms attribute to ensure the requesting user has the myapp.can_see_news permission. It's not exactly a decorator, but we're retaining some flexibility while staying true to the DRY principle.
Thursday, November 3, 2011
Preferring Filters To Tags
In Django, there are two ways to extend the template language — tags and filters. Tags are named tokens that insert HTML, executing logic behind the scenes. For instance, an if tag and a for tag will conditionally produce content or iterate over a list objects respectfully. The if tag in Django takes an argument — the condition to evaluate.
Filters, on the other hand, are different from tags — they don't spit out HTML markup or alter the overall template logic. Instead, filters modify existing values in the template context for the sake of presentation. For example, Django ships with a fileformat filter that'll display the argument, expressed in bytes, as a more readable representation. This type of functionality is best encapsulated inside a filter because we're simply modifying the presentation of a single value.
I used to find myself grappling over whether something should be implemented as a tag or as a filter. On the one hand, custom tags capacious changes — chunks of HTML or logic for manipulating the template. On the other hand, filters are concise. They take input and modify it. Filters exchange one piece of data for another. Django provides the machinery to do this, to slice up monolithic templates into reusable, independent components. My inclination is that custom template tags can often be avoided in favor of included templates and filters.
Modular templates
If every page in our application were rendered using a single gargantuan template, we wouldn't be burdened with choosing between custom tags and custom filters. We'd have a number of other problems, no doubt, but I digress. Instead, Django treats template files much like Python treats modules. Django templates are modular. Why write one large template, duplicated for individual views? That approach doesn't follow the DRY principle, so we need a mechanism that'll allow us to treat our templates as components.
A component can have sub-components. This abstraction works well in the Django template architecture because components are made up of smaller components. They can be decomposed and reconstructed, using smaller, loosely-coupled components. In Django-speak, this means that we can start with a template that represents the entire page. Logically, based on the application for which this template was designed, we can map out sections of this template that are likely to appear on all pages. Things such as navigation, footer, and so forth.
These sub-components need to fill slots. These slots are called blocks, in Django. Typically, the master template is extended by descendant templates whose job is to define what fills these slots. Now we're starting to move down the composite template structure. Beyond blocks, we need something to fill them with.
Eventually, fine-grained HTML markup is generated. Some of it conditional, some of it iterative, and some using variables from the template context. But even at this level, where we're filling in blocks, there are smaller pieces still. Pieces that don't necessarily belong to a specific block. Down yet another level, we're producing markup that might even appear twice on the same page — in two distinct blocks.
As the user interface of your Django project evolves and forms it's shape, you'll begin to notice these smaller chunks — those that might be of interest to multifarious blocks. The main blocks, the logical regions of the master template are easy to grasp early on in development. It's the smaller template components — the unbound HTML markup — that are more difficult to identify. These low-level template components must take into consideration both custom template tags and custom template filters.
Sharing data
The tools that the Django template system gives developers enables the sharing of data. Sharing between what exactly? You extend the template language because you want to reuse those elements — custom tags and filters — across templates in your application. But these new elements don't generate new data. They're not storing application data. Rather, the data new template elements share is transformed data. They take input and alter what the template ultimately sends to the browser.
Let's say you've got an application that lists events of interest to the user when they first login. These are probably queried from the database and rendered inside a for tag. But maybe the event abstraction isn't limited to a user. Maybe there are different event types that pertain to other abstractions in the application and aren't exclusive to the user. Here, we might want to reuse a much of the same logic that rendered events on the homepage. We're only changing the query.
We could follow the DRY principle here and implement a custom eventlist tag. This tag would accept some type of flag, indicating the query to execute in order to render the appropriate event list. Using this tag in our templates is then quite straightforward — {% eventlist 'home' %} or {% eventlist 'updates' %}. This tag is available in all templates — an easy way to share data across Django templates.
Our tag could even take care of rendering the corresponding HTML markup. We'd simply register these eventlist tag as an inclusion tag. We've now got our own template, isolated from other templates in our user interface dedicated to rendering event lists.
There are, however, a couple problems with this approach. One, the template tag itself is responsible for executing the query. This means that our view context passed to the template during rendering cannot alter the output of the event list. So if we want to use generic views that'll generate a list of events to render, we're stuck. The eventlist tag is expecting an arbitrary flag. We've given the Django template the added responsibility of performing database queries — which isn't a good thing. We could alter our template tag so that it accepts a query set from the template context. This way the view is still responsible for retrieving objects. This is a good thing. However, this leads to another problem. One where we're violating the DRY principle.
Including and filtering
Tags are good because they're a concise way of sharing rendered HTML with other templates. Our eventlist tag does exactly that. But they're also a gateway into bad Django practice. Defining your own template tags means writing some Python code. Which gives us direct access to models. Which means we can query them. Not good. Not good as far as templates go because templates should be able to render anything I give them. If I want to pass to my tag context, say, a list of Python objects that merely emulate one of my models, it should be able to handle that. This isn't true if the template tags we define are going directly to the database for objects to render.
As for remedying the problem, we mentioned passing a query set, a list, something, to the template tag as an argument. This somewhat solves the problem because now eventlist is polymorphic to an extent. It doesn't care what objects it gets as long as they look like event objects.
Recall, however, that we're defining eventlist as an inclusion tag. This means a template dedicated to the list of events will be rendered whenever the tag is used. Keeping the HTML in the template is a smart idea — another win for us. The eventlist tag is now simply passing the list of events to render. Perfect. Except, what doesn't the template tag do now? It takes a list of objects and passes them into another template context. It does exactly what the Django include tag does.
The include tag takes a smaller template component — in our case a list of rendered event objects — and plugs it into a larger component. This sounds like an ideal candidate for our application. Our views can now control the context — the list of events that get displayed in any given block. And we're not repeating ourselves — we've got a template component that can be injected anywhere, and no need to define a custom template tag.
How about the template itself — the one we're including all over the site? Are there any drawbacks to simply including it somewhere else without using our own tag? One challenge with making template components generic enough to be used anywhere is that they need to support a variety of scenarios. What happens when the event object is in this state? When it doesn't have a title attribute? How do I handle CSS classes for events on this page?
Trying to handle all this in a single template that's used everywhere leads to messy template code. In a hurry, you've got if statements inside HTML attributes and deep nesting levels elsewhere. This is where defining custom template filters can alleviate some of these challenges. Typical Django filters will take an input value and return a slightly different version of it — like a formatted date. But we're not limited to only modifying the display of data. We can return different values entirely. Values used for the sole purpose of template rendering.
Template filter definitions are small. We might have an input value, we do a few checks, and we return a different value. Think of filters as dynamic template context modifiers — modifiers that can be shared throughout the application. Filters can also justify not having to define your own custom tags, ultimately leading to cleaner template code.
Filters, on the other hand, are different from tags — they don't spit out HTML markup or alter the overall template logic. Instead, filters modify existing values in the template context for the sake of presentation. For example, Django ships with a fileformat filter that'll display the argument, expressed in bytes, as a more readable representation. This type of functionality is best encapsulated inside a filter because we're simply modifying the presentation of a single value.
I used to find myself grappling over whether something should be implemented as a tag or as a filter. On the one hand, custom tags capacious changes — chunks of HTML or logic for manipulating the template. On the other hand, filters are concise. They take input and modify it. Filters exchange one piece of data for another. Django provides the machinery to do this, to slice up monolithic templates into reusable, independent components. My inclination is that custom template tags can often be avoided in favor of included templates and filters.
Modular templates
If every page in our application were rendered using a single gargantuan template, we wouldn't be burdened with choosing between custom tags and custom filters. We'd have a number of other problems, no doubt, but I digress. Instead, Django treats template files much like Python treats modules. Django templates are modular. Why write one large template, duplicated for individual views? That approach doesn't follow the DRY principle, so we need a mechanism that'll allow us to treat our templates as components.
A component can have sub-components. This abstraction works well in the Django template architecture because components are made up of smaller components. They can be decomposed and reconstructed, using smaller, loosely-coupled components. In Django-speak, this means that we can start with a template that represents the entire page. Logically, based on the application for which this template was designed, we can map out sections of this template that are likely to appear on all pages. Things such as navigation, footer, and so forth.
These sub-components need to fill slots. These slots are called blocks, in Django. Typically, the master template is extended by descendant templates whose job is to define what fills these slots. Now we're starting to move down the composite template structure. Beyond blocks, we need something to fill them with.
Eventually, fine-grained HTML markup is generated. Some of it conditional, some of it iterative, and some using variables from the template context. But even at this level, where we're filling in blocks, there are smaller pieces still. Pieces that don't necessarily belong to a specific block. Down yet another level, we're producing markup that might even appear twice on the same page — in two distinct blocks.
As the user interface of your Django project evolves and forms it's shape, you'll begin to notice these smaller chunks — those that might be of interest to multifarious blocks. The main blocks, the logical regions of the master template are easy to grasp early on in development. It's the smaller template components — the unbound HTML markup — that are more difficult to identify. These low-level template components must take into consideration both custom template tags and custom template filters.
Sharing data
The tools that the Django template system gives developers enables the sharing of data. Sharing between what exactly? You extend the template language because you want to reuse those elements — custom tags and filters — across templates in your application. But these new elements don't generate new data. They're not storing application data. Rather, the data new template elements share is transformed data. They take input and alter what the template ultimately sends to the browser.
Let's say you've got an application that lists events of interest to the user when they first login. These are probably queried from the database and rendered inside a for tag. But maybe the event abstraction isn't limited to a user. Maybe there are different event types that pertain to other abstractions in the application and aren't exclusive to the user. Here, we might want to reuse a much of the same logic that rendered events on the homepage. We're only changing the query.
We could follow the DRY principle here and implement a custom eventlist tag. This tag would accept some type of flag, indicating the query to execute in order to render the appropriate event list. Using this tag in our templates is then quite straightforward — {% eventlist 'home' %} or {% eventlist 'updates' %}. This tag is available in all templates — an easy way to share data across Django templates.
Our tag could even take care of rendering the corresponding HTML markup. We'd simply register these eventlist tag as an inclusion tag. We've now got our own template, isolated from other templates in our user interface dedicated to rendering event lists.
There are, however, a couple problems with this approach. One, the template tag itself is responsible for executing the query. This means that our view context passed to the template during rendering cannot alter the output of the event list. So if we want to use generic views that'll generate a list of events to render, we're stuck. The eventlist tag is expecting an arbitrary flag. We've given the Django template the added responsibility of performing database queries — which isn't a good thing. We could alter our template tag so that it accepts a query set from the template context. This way the view is still responsible for retrieving objects. This is a good thing. However, this leads to another problem. One where we're violating the DRY principle.
Including and filtering
Tags are good because they're a concise way of sharing rendered HTML with other templates. Our eventlist tag does exactly that. But they're also a gateway into bad Django practice. Defining your own template tags means writing some Python code. Which gives us direct access to models. Which means we can query them. Not good. Not good as far as templates go because templates should be able to render anything I give them. If I want to pass to my tag context, say, a list of Python objects that merely emulate one of my models, it should be able to handle that. This isn't true if the template tags we define are going directly to the database for objects to render.
As for remedying the problem, we mentioned passing a query set, a list, something, to the template tag as an argument. This somewhat solves the problem because now eventlist is polymorphic to an extent. It doesn't care what objects it gets as long as they look like event objects.
Recall, however, that we're defining eventlist as an inclusion tag. This means a template dedicated to the list of events will be rendered whenever the tag is used. Keeping the HTML in the template is a smart idea — another win for us. The eventlist tag is now simply passing the list of events to render. Perfect. Except, what doesn't the template tag do now? It takes a list of objects and passes them into another template context. It does exactly what the Django include tag does.
The include tag takes a smaller template component — in our case a list of rendered event objects — and plugs it into a larger component. This sounds like an ideal candidate for our application. Our views can now control the context — the list of events that get displayed in any given block. And we're not repeating ourselves — we've got a template component that can be injected anywhere, and no need to define a custom template tag.
How about the template itself — the one we're including all over the site? Are there any drawbacks to simply including it somewhere else without using our own tag? One challenge with making template components generic enough to be used anywhere is that they need to support a variety of scenarios. What happens when the event object is in this state? When it doesn't have a title attribute? How do I handle CSS classes for events on this page?
Trying to handle all this in a single template that's used everywhere leads to messy template code. In a hurry, you've got if statements inside HTML attributes and deep nesting levels elsewhere. This is where defining custom template filters can alleviate some of these challenges. Typical Django filters will take an input value and return a slightly different version of it — like a formatted date. But we're not limited to only modifying the display of data. We can return different values entirely. Values used for the sole purpose of template rendering.
Template filter definitions are small. We might have an input value, we do a few checks, and we return a different value. Think of filters as dynamic template context modifiers — modifiers that can be shared throughout the application. Filters can also justify not having to define your own custom tags, ultimately leading to cleaner template code.
Subscribe to:
Posts
(
Atom
)