Shopping carts and Django sessions

Django makes it very easy to build a shopping cart for our web-based e-commerce stores and applications. In fact, Django does an excellent job of solving a more general problem: persisting temporary data between browser requests in a simple way. This is what Django's session framework gives us.

Any pickleable Python object can be stored using the session framework. The session object is attached to incoming requests and is accessible from any of our views. Requests from users that are not logged in, or haven't even created accounts, get session objects too. Django manages all of this by associating a cookie in the user's browser with a session ID. When the browser issues a request, the SessionMiddleware attaches the appropriate session object to the request so that it is available in our views.

Sessions, and the cookies that manage them, have an expiration date. This expiration is not necessarily tied to the expiration of browser cookies and can be controlled in our views using the session object's set_expiry() method. This method allows for four expiration possibilities:

  • Never
  • When the user's browser is closed
  • After a specified amount of time has elapsed
  • The default global Django setting

If the user happens to clear their browser's cookies, then any previous session information will be lost and they will receive a new session object the next time they visit the site. The information attached to these abandoned sessions is retained by Django in the database until it's manually cleared using the cleanup command to django-admin.py.

As we can use Django's session framework to store any pickleable Python object (see http://docs.python.org/library/pickle.html for more information on pickling), it makes storing shopping cart information very easy. We will build a simple Cart class that represents a list of items with methods that can add and remove from the list. Each item in the list is represented by an inner-class called Item that manages the item's ID in a particular shopping cart and a quantity value. This code appears as shown:

class Cart(object):
    class Item(object):
        def __init__(self, itemid, product, quantity=1):
            self.itemid = itemid
            self.product = product
            self.quantity = quantity

    def __init__(self):
         self.items = list()
         self.unique_item_id = 0

    def _get_next_item_id(self):
        self.unique_item_id += 1
        return self.unique_item_id
    next_item_id = property(_get_next_item_id)

    def add_item(self, product, quantity=1):
        item = Item(self.next_item_id, product, quantity) 
        self.items.append(item)

    def remove_item(self, itemid):
        self.items = filter(lambda x: x.itemid != itemid, self.items)

Cart objects manage their own list of unique item IDs by incrementing a variable each time we add a new item. This is the purpose of the next_item_id property. At removal time, we can use the item's unique ID to easily find it in the items list and remove it.

As a further convenience, particularly when we need to access the contents of a shopping cart in our templates, we can make the Cart class iterable. This is a Python convention that allows container classes to function like sequences or lists. For example, using a non-iterable version of the Cart class, we could access the items list like so:

for item in cart_instance.items: 
        ...

By transforming our cart into an iterable, however, we can simplify the above to just:

for item in cart_instance:
        ...

What's the big deal? We saved a few keystrokes and some syntax. But in the future, if we need to change the interface to our items list, the iterable approach prevents us from relying on semantic knowledge of the internal workings of our shopping cart. The underlying storage of items in the cart can change and we only need to update our iterable code. To make our Cart class iterable, we add the following two methods:

def __iter__(self):
     return self.forward()
def forward(self):
     current_index = 0
     while (current_index<len(self.items)):
         item = self.items[current_index]
         current_index += 1
         yield item

Later, if our shopping cart needs change, the Cart class's forward() method will act as a single point of control. We can implement any changes needed, but retain the same iterable interface in our views and templates.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset