Category Archives: Python

Bootstrap Django templates and admin: A complete step by step

Bootstrap has made the life of back-end developers a little easier. With the ability to make almost all the apps look cleaner then how they would originally appear, it has indeed been a great tool. However, despite the helpful tutorials and examples out there, as is the case with many, they all suffer from the same three flaws:

  1. No tutorial can be a “one size fits all” solution, and that is a reality. Which is why there are many tutorials out there and the reason why I am writing one as well.
  2. Deprecation with time. All tutorials suffer the same time deprecation, where packages are updated, deprecated or removed, configurations altered etc. Unfortunately, the tutorials mostly stay static and eventually end up being a good start guide but no longer step by step instructions. Though I would like to avoid this eventuality, I do not guarantee it.
  3. Assumption of knowledge. Every tutorial assumes a certain degree of know-how of the technologies involved, which sometimes, is just asking too much but is rarely avoidable, otherwise, a post as simple as this would have to be a book.

Since I am a novice at bootstrap (if it works its beautiful),  I decided, that I would go through the motions of bootstrapping a simple Django application, both the front-end template, as well as, the admin section. Depending on how long it would take to get this functional, this post may take more than a few days for me to publish and recognizing the fact that people rarely give details of what OS and what versions of the packages are involved, I will try to be more succinct in my explanation.

Knowledge assumptions:

  • python and Python-Django Framework
  • virtualenv and pip

Operating System and Python:

I am developing on Ubuntu, using Python 2.7.6 . Here are the particulars:

#command to find the release
cat /etc/lsb-release

#Result
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.1 LTS"

Setup:

Setup a virtual environment and a basic Django project, with a dummy app. The following snippet shows all the steps I followed to achieve this.

#command to create virtual env using virtualenvironment wrapper
mkvirtualenv bootstrap_test
#output
'''
New python executable in bootstrap_test/bin/python
Installing setuptools, pip...done.
'''

#activate venv
workon bootstrap_test

#install django
pip install django
#output
'''
Downloading/unpacking django
  Downloading Django-1.7-py2.py3-none-any.whl (7.4MB): 7.4MB downloaded
Installing collected packages: django
Successfully installed django
Cleaning up...
'''

#checking all packages
pip freeze
#output
'''
Django==1.7
argparse==1.2.1
wsgiref==0.1.2
'''

#create django project
django-admin.py startproject bstrap

#create dummy app inside project
django-admin.py startapp dummy

#checking bstrap project directory structure using tree
tree bstrap
#output
'''
bstrap
├── bstrap
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── dummy
│   ├── admin.py
│   ├── __init__.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
└── manage.py
'''

The above steps are pretty simplistic and if you are confused at this point, continuing might be a little hard. Before continuing to the next phase, go to ‘bstrap/bstrap/settings.py‘ and add dummy to the installed apps, so it looks something like:

# Application definition
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'dummy',
)

Now before we delve into bootstrapping, lets just check that the admin section of the app is working, obviously, first we run migrate, and set the admin user and password. Then do a runserver, and assuming that you did not specify an ip, the server will be launched at http://127.0.0.1:8000/.

Since we haven’t configured any views, only the admin site will be working at the moment, and that is all we need.

Navigate to http://127.0.0.1:8000/admin/ and you will see the standard django admin template after you login with the superuser you created during migration.

After ensuring that the app works; we begin with the simple bootstrap objective first…bootstrap django admin.

Django Admin Bootstrap:

The major reference for this particular section comes from Riccardo Forina Blog , who designed and implemented the pip package we will be using. For all the details as to how to achieve this, please visit the original blog post. Here I will be repeating what he did and showing the output of the work.  As per his instructions:

  1. install the pip package django-admin-bootstrapped in your virtual environment by running “pip install django-admin-bootstrapped”
  2. Add ‘django_admin_bootstrapped.bootstrap3‘, into the INSTALLED_APPS before 'django.contrib.admin'.

I followed the above two steps, re-ran my runserver, navigated to http://127.0.0.1:8000/admin/ and voilla!!!  the admin was bootstrapped….

 

 Django Templates Bootstrap:

So first, lets add the static configuration to the setting file. I have a configuration that I use in most of my application, which is as follows:

#BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.6/howto/static-files/

STATIC_URL = '/static/'
# STATIC_PATH = os.path.join(BASE_DIR,'static')
# STATIC_ROOT_URL = ''
STATIC_ROOT = os.path.join(BASE_DIR,'static_media')

STATICFILES_DIRS = (
  os.path.join(BASE_DIR, 'static/'),
)

#templates
TEMPLATE_PATH = os.path.join(BASE_DIR, 'templates')

TEMPLATE_DIRS = (
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    TEMPLATE_PATH,
)

Next, create a folder in your main project directory called “static”, download Bootstrap and add the unzipped files to the “static” folder you created, so that your project folder structure now looks something like this:

bstrap/
├── bstrap
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── settings.py
│   ├── settings.pyc
│   ├── urls.py
│   ├── urls.pyc
│   ├── wsgi.py
│   └── wsgi.pyc
├── db.sqlite3
├── dummy
│   ├── admin.py
│   ├── admin.pyc
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── migrations
│   │   ├── __init__.py
│   │   └── __init__.pyc
│   ├── models.py
│   ├── models.pyc
│   ├── tests.py
│   └── views.py
├── manage.py
└── static
    └── bootstrap
        ├── css
        │   ├── bootstrap.css
        │   ├── bootstrap.css.map
        │   ├── bootstrap.min.css
        │   ├── bootstrap-theme.css
        │   ├── bootstrap-theme.css.map
        │   └── bootstrap-theme.min.css
        ├── fonts
        │   ├── glyphicons-halflings-regular.eot
        │   ├── glyphicons-halflings-regular.svg
        │   ├── glyphicons-halflings-regular.ttf
        │   └── glyphicons-halflings-regular.woff
        └── js
            ├── bootstrap.js
            └── bootstrap.min.js

Now you have setup the basis for bootstrapping your templates. I created a templates folder and added an example template I copied from the bootstrap website.(Starter Template). I called it base.html.

Now in order to actually render and view the bootstrap result, I changed two files. The urls in the bstrap app to look like…

from django.conf.urls import patterns, include, url
from django.contrib import admin

urlpatterns = patterns('',
    # Examples:
     url(r'^$', 'dummy.views.home', name='home'),
    # url(r'^blog/', include('blog.urls')),

    url(r'^admin/', include(admin.site.urls)),
)

and the views in the dummy app to look like…

from django.shortcuts import render
from django.shortcuts import render_to_response

def home(request):
    return render_to_response('base.html')

And you are done! Running the runserver now, and navigating to http://127.0.0.1:8000/ should show you the “Starter Bootstrap template”…

 

Source Code:

https://bitbucket.org/ssaqibyawargmailcom/bstrap/

 

P.S if you are looking for an easy way to boostrap your forms in django, I recommend you navigate to django-bootstrap-form app and use that.

 

Hope this helps!!!

The hidden dependencies of Python packages

Ever since I started using pip and python  on Ubuntu, I have always had certain packages that would be silent about what dependencies they require in order to work, and only when they fail to install, is when you figure that out. I was just installing another virtual environment just now, and decided that I should probably put up a post of any such packages that I come across.

So lets begin with the one that had me searching for its dependencies just now, which would be lxml==3.3.3.

Here is the apt command with all the packages that I had to install.

sudo apt-get install libxml2 libxslt1.1 libxml2-dev libxslt1-dev python-libxml2 python-libxslt1 python-dev python-setuptools zlib1g-dev

Hope this helps!!!

P.S. This article is more like a reminder to self of the dependencies so that I do not need to go searching for them again…coming up next is psycopg2

Python XML comparator

Although I am not a big fan of xml formatting myself, but dealing with it is inevitable given how widely it is used. So when searching for a solution to compare two generic XML files, I came across several solution, but none who would completely satisfy a comparison of two generic XML trees in python.  Plus, the code shared was incomplete and without any particular instructions of how to complete it.

So I grabbed different solutions and came up with a solution to achieve the desired result. Therefore, the result was the following python class.

__author__ = 'syawar'

import xml.etree.ElementTree as ET
import logging

class xmlTree():
    """
    .. module:: xmltree
        :platform: Ubuntu, Linux
        :synopsis: convert the xml files passed to an iterable object

    ..
    this module handles conversion of an xml file to an iterable object
    """

    def __init__(self):
        self.logger = logging.getLogger(__name__)
        self.hdlr = logging.FileHandler('/var/log/xml-minipulations.log')
        self.formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
        self.hdlr.setFormatter(self.formatter)
        self.logger.addHandler(self.hdlr)
        self.logger.setLevel(logging.DEBUG)

    @staticmethod
    def convert_file_to_tree( filename):
        """

        :param filename:
        :return:
         the xml file in tree format
        """
        return ET.parse(filename)

    def xml_compare(self, x1, x2, excludes=[]):
        """
        Compares two xml etrees
        :param x1: the first tree
        :param x2: the second tree
        :param excludes: list of string of attributes to exclude from comparison
        :return:
            True if both files match
        """
        if x1.tag != x2.tag:
            self.logger.debug('Tags do not match: %s and %s' % (x1.tag, x2.tag))
            return False
        for name, value in x1.attrib.items():
            if not name in excludes:
                if x2.attrib.get(name) != value:
                    self.logger.debug('Attributes do not match: %s=%r, %s=%r'
                                 % (name, value, name, x2.attrib.get(name)))
                    return False
        for name in x2.attrib.keys():
            if not name in excludes:
                if name not in x1.attrib:
                    self.logger.debug('x2 has an attribute x1 is missing: %s'
                                 % name)
                    return False
        if not self.text_compare(x1.text, x2.text):
            self.logger.debug('text: %r != %r' % (x1.text, x2.text))
            return False
        if not self.text_compare(x1.tail, x2.tail):
            self.logger.debug('tail: %r != %r' % (x1.tail, x2.tail))
            return False
        cl1 = x1.getchildren()
        cl2 = x2.getchildren()
        if len(cl1) != len(cl2):
            self.logger.debug('children length differs, %i != %i'
                         % (len(cl1), len(cl2)))
            return False
        i = 0
        for c1, c2 in zip(cl1, cl2):
            i += 1
            if not self.xml_compare(c1, c2):
                self.logger.debug('children %i do not match: %s'
                             % (i, c1.tag))
                return False
        return True

    def text_compare(self, t1, t2):
        """
        Compare two text strings
        :param t1: text one
        :param t2: text two
        :return:
            True if a match
        """
        if not t1 and not t2:
            return True
        if t1 == '*' or t2 == '*':
            return True
        return (t1 or '').strip() == (t2 or '').strip()

Usage:

The usage is simple. Create object, either convert xml files to xml.etree.ElementTree objects by using convert_file_to_tree or just pass xml.etree.ElementTree to the xml_compare, which will return true if the files are the same and false if files are different.

The excludes list can be passed in case there is a property of a node that you do not want to compare. A prime example is the last-modified  field that most xml files used posses. the function call will then have excludes=[“last-modified”].

 

And that is all folks. Hope this helps!