hi there sirdar jaeger lauper for infoworld at idg welcome to the latest installment in our ongoing
series in smart python where we've been building a mini social network application using python
sqlite and python's web and networking tools this time around we're going to clean up the code for
our app a little bit to make it a little less messy and a little less boilerplatey
one of the ways we're going to do this is with the use of the decorator pattern decorators in python
are used to wrap functions and to transform their inputs and outputs see when we first
started building this project you probably saw how decorators were used to indicate which functions
were attached to what routes in our application but that didn't really give us much insight into
how they worked or to what end what we're going to do now is create our own custom decorators and use
them as a way to remove some of the patterns in our project that have gotten a little bit tedious
first we're going to create a decorator that gets user login information from the user's token
if such a token exists this could be used in different ways depending on the route
for instance for the read message route we don't need a user login just to show the message but we
do need to have a user logged in if the user wants to post or reply to that message or potentially
delete the message if it's theirs our decorator is called get user and here's how it works all
decorators work by taking in the function they're decorating as an argument and then returning
something that is substituted for that function in this case we're creating a whole new function
on the fly called wrapper and we're returning that what wrapper does is it gets the user information
from the token just as we would in one of our existing route functions and then calls the
function we're decorating with the user object now passed in as the first parameter to that function
now here's how we decorate our functions with it because we already have decorators attached
to these routes we have to think carefully about the order in which we apply decorators
each decorator will pass its results to the one beneath it we want our route decorator to operate
first so i can do things like capture variables from the url so we'll put getuser after that
you'll notice now also that the signature for the function we're decorating has changed
originally it took no arguments and it looked up the user object all on its own now it takes
the automatically passed in user object as an argument and so we don't need to perform manual
lookup of the user object for this function here which already took an argument message id passed
to it by the route function the user argument is passed in front of it you'll also notice we
no longer need again to get the user object by hand in any of the decorated functions anymore
this is one of the big advantages of using a decorator pattern anything we want to apply
equally across a whole slew of functions we can do so in a single centralized way
if we ever want to change how the login system works for instance we can fix that by just
editing the contents of the decorator instead of editing every function that needs a login
now there's a few more boilerplate behaviors in this app that we can modify with decorators
for instance i mentioned before how there's some routes that take user information optionally
in other routes user information is mandatory you have to be logged in
for instance you can't post a message without having a valid login so let's create a decorator
that handles that use case and we'll call it login required the way this decorator works
is in conjunction with the previous one we take a function that's been decorated with getuser which
has user as its first passed in argument and then we inspect the user argument if user is
an empty object meaning no user is logged in we return back a not logged in message
if the user does exist we call our wrapped function and we return whatever it returns
this decorator will then go below the existing get user decorator
this is because login required expect a user object as the first argument so it'll take
the one passed to it by the get user decorator as you can see one of the issues with using multiple
decorators is that it can be confusing what order to put them in but the idea is each decorator will
pass on its input to the one below it until we finally reach the wrapped function itself
let's add login required to all the route functions that need it for instance the main
route can take a user but it doesn't need a login anything with a post action on it
though that's almost certainly going to require a login for instance we don't want to offer the
option of deleting a message if someone isn't logged in we don't want to do anything at all
one more decorator that will come in handy here is one that automatically retrieves a message object
whenever message id is supplied in the url originally the route decorators would pass
in message id as the first positional argument to each function that had it
so we can take advantage of that consistency we'll create a decorator named message
and it will take in as its first argument the message id passed along by the route decorator
this means we have to place it after the route decorator but before everything else there are
many cases where we want to have a message object but we don't necessarily need a user
object again if we want to present messages to users who don't have to be logged in to read them
notice also that our decorator choices will change the order of the arguments submitted
to the wrapped function whenever we use message and then get user the user object comes first
then the message object that's because the way we've constructed things each successive
decorator adds arguments from the left side it pushes the previous arguments to the right
also note that we don't take in the message id argument anymore
our message decorator has already intercepted that and turned it into a message object
as you can guess from all of this one of the issues with using a decorator stack pattern
like this is that it can be brittle if you're not careful we're lucky here because the order
in which we can do things is fairly regular if we needed more freedom in terms of which things
would be processed first we might have to pass our arguments as keywords instead of positionally
but still the advantages of using decorators here are pretty significant and have been well worth
making the transition to we now have one place in our application where user security is handled
we have one consistent mechanism for obtaining posts objects from urls
so this lets us focus on the parts of our app that are unique instead of copying and pasting
error-prone boilerplate that's it for this episode if you liked it please leave a comment below
don't forget to subscribe to the igg techtalk channel on youtube and for more smart python and
more dev with serdar be sure to follow me on facebook youtube and infowl.com