Dec 30, 2013

CentOS Install SSH server and configure for VirtualBox testing

I have had some tasks to configure and install a CentOS distribution for my bug-tracking needs. Hope to help out someone with similar problems and or tasks.

I'll be using a fresh install of a CentOS Minimal 6.5.  And a Mac Book Pro with VirtualBox installed (as my testing environment).

First after install of a clean CentOS you need an SSH server to work in your usual environment. So to connect to a Linux, installed in a VirtualBox you will need basically 2 things:
  • openssh-server install
  • disable firewall
  • VirtualBox Port configuration/Access configuration

OpenSSH server install

To install server you need to run in your VirtualBox Guest system (CentOS VM):
yum -y install openssh-server
And start and add it to auto launch at system startup:
chkconfig sshd on
service sshd start
Also make sure port 22 is opened in your VM. type:
netstat -tulpn | grep :22

Firewall settings

Because it's a VM you can simply disable the firewall. But be sure not to do this at deployment/production server of any kind.

To disable firewall and remove from auto lunch at system startup run:
service iptables stop
chkconfig iptables off

VirtualBOX

To connect your Guest machine to your Host machine (basically up a network between virtual CentOS and your host Mac OS (In my case) you have several options.
I'll stop on a simple one. You may do a Bridged network connection and have your router/whatever assign your VM an IP address inside your network.
You may also select NAT and configure port mappings. But it's fairly more complex rather then this setting and exceeds the scope of this article.

Connect

Now you need to reboot your CentOS or either reboot network inside your VM. As a result, after reboot you should get assigned IP address. You can view it using command:
$ ifconfig
eth0      Link encap:Ethernet  HWaddr XX:XX:XX:XX:XX:XX  
          inet addr:192.168.1.104  Bcast:192.168.1.255  Mask:255.255.255.0
Mine got an IP ending with 104.
And so now you can ssh connect to your VM terminal using something like:
$ ssh root@192.168.1.104
root@192.168.1.104's password: 
Last login: ...
[root@localhost ~]# 
Tadaa! Your system is up and ready for experiments. Just be sure to save your state with VM snapshots ;)

Comments?

Dec 13, 2013

Python Generators explained simply.

I'm often confused by recent obsession of generators in python. I'll try to explain them as simply as possible. Say you have never used iterators but coded in python. BUT I'm sure you have used dictionaries (dict) and have met requirement to iterate through it's keys and/or values. TADA! You have used generators already ;).

So generator is a function in python. Except for it uses a keyword yield in it's code. Thus making it the iterator. So you could call this function in a sequence like you would probably do iterating over a dictionary already. E.g.:
# typical iteration through dictionary key: value set
for key, value in dict.iteritems():
   # do someth

# Usage of your own iterator
for item in iterator_functuon():
   # do something with function generated output
So this function returns an iterator generator like the dict() type has by default. And has a next() function, like usual iterators have.
So in attempt to using human language:
Generators are simple functions converted to iterators.
So when a generator function calls it's magic word yield, the "state" of the generator function is frozen; the values of all variables are saved and the next line of code to be executed is recorded until next() is called again. Once it is, the generator function simply resumes where it left off. If next() is never called again, the state recorded during the yield call is (eventually) discarded.
So. that was the theory. Better to look at example well commented out:
def gen_odd_nums():
 # executed once (first call) usually like __init__() of a generator
    odd_num = 1
    while True:
     # saves context and return from function
        yield odd_num
        odd_num = odd_num + 2

# Typical generator usage
generator_object = gen_odd_nums()  # initialising a generator once
for i in range(5):
 # calls generator_object.__next__()
    print next(generator_object)
Output will be like:
1
3
5
7
9
That's probably all the magic about the generators. Best way to get it is to open your console. NOW! And try to do it yourself.

Comments?

Nov 29, 2013

AJAX form in Django with jQuery.form plugin

Here I will describe an example form using jQuery Form plugin. It will provide a good frontend, while Django serving a simple backend.
I made this AJAX form using unobtrusive javascript. With human language it means the form will function with JavaScript disabled. And so jQuery.form plugin will work as an AJAX speedup addition and not as a major requirement.

Features:
* Works both with JavaScript  and without.
* Uses standard Django ideology/hooks.
* Uses the most known jQuery plugin for forms AJAX handling.

Theory:
Main idea that Django supports both normal and AJAX request in a standard view. And has a handy request.is_ajax() request method. It returns True/False depending on if request search has HTTP_X_REQUESTED_WITH header for the string 'XMLHttpRequest'. jQuery.form plugin sure does have that.

Backend Django.
We will create an app called 'contact' for our task and place it in the example django project. Urls from the project will redirect to this app. OK. Here is what in our:
urls.py
from django.conf.urls import patterns, url

urlpatterns = patterns('contact.views',
    url(r'^$', 'contact_form', name="contact_form"),
)
Here ve have only one view serving all the needs. and looking at the root of the app. Enough to prove the idea.

forms.py
from django import forms


class ContactForm(forms.Form):
    name = forms.CharField(max_length=100)
    email = forms.EmailField(max_length=100)
    message = forms.CharField(widget=forms.Textarea(), max_length=1000)
The form is far too simple. But quite sufficient for the concept proof.

views.py
import json

from django.shortcuts import render
from django.http import HttpResponseBadRequest
from django.http import HttpResponse
from django.core.mail import mail_admins
from forms import ContactForm


def contact_form(request):
    if request.POST:
        form = ContactForm(request.POST)
        if form.is_valid():
            # Imaginable form purpose. Post to admins.
            message = """From: %s <%s>\r\nMessage:\r\n%s\r\n""" % (
                form.cleaned_data['name'],
                form.cleaned_data['email'],
                form.cleaned_data['message']
            )
            mail_admins('Contact form', message)

            # Only executed with jQuery form request
            if request.is_ajax():
                return HttpResponse('OK')
            else:
                # render() a form with data (No AJAX)
                # redirect to results ok, or similar may go here 
                pass
        else:
            if request.is_ajax():
                # Prepare JSON for parsing
                errors_dict = {}
                if form.errors:
                    for error in form.errors:
                        e = form.errors[error]
                        errors_dict[error] = unicode(e)

                return HttpResponseBadRequest(json.dumps(errors_dict))
            else:
                # render() form with errors (No AJAX)
                pass
    else:
        form = ContactForm()

    return render(request, 'contact/form.html', {'form':form})
Here we will stop in more detail. We will work out all form possible conditions. e.g. valid/invalid and ajax/plain.
The main form purpose is to actually send a email to admins about contact request for our imaginable site (mail_admins() command and message being created from submitted data).
We have 2 form view conditions here. GET (When we load start of this page) and POST (When we upload the data and parse the form).
In case this view would not have the AJAX part it will look like so:
def contact_form(request):
    if request.POST:
        form = ContactForm(request.POST)
        if form.is_valid():
            # Imaginable form purpose. Post to admins.
            message = """From: %s <%s>\r\nMessage:\r\n%s\r\n""" % (
                form.cleaned_data['name'],
                form.cleaned_data['email'],
                form.cleaned_data['message']
            )
            mail_admins('Contact form', message)
    else:
        form = ContactForm()

    return render(request, 'contact/form.html', {'form':form})
This view works normally with provided template and disabled JavaScript scripts in the template (you may compare it to the first view with AJAX additions). I have not used common possibilities, because I do not need them for demo. It's up to you to decide where to make redirect or so.
Note we are using here request.is_ajax() method and adding another dynamic web page functionality, thus making view behave right for our AJAX needs. And the logic goes into another direction when commented (No AJAX).  Here is where our logic changes go when we do not use jQuery form for request.
Errors here are treated specially. We need the field names of them and the error html to modify for4m in the frontend JavaScript.
Now I hope you understood what is made here. Now let's move on to the templates.
<!DOCTYPE html>
<head>
    <title>Contact</title>
    <script type="text/javascript"
        src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js">
    </script>
    <script type="text/javascript"
        src="http://malsup.github.com/min/jquery.form.min.js">
    </script>
    <script type="text/javascript">
        $(document).ready(function() {
            function block_form() {
                $("#loading").show();
                $('textarea').attr('disabled', 'disabled');
                $('input').attr('disabled', 'disabled');
            }

            function unblock_form() {
                $('#loading').hide();
                $('textarea').removeAttr('disabled');
                $('input').removeAttr('disabled');
                $('.errorlist').remove();
            }

            // prepare Options Object for plugin
            var options = {
                beforeSubmit: function(form, options) {
                    // return false to cancel submit
                    block_form();
                },
                success: function() {
                    unblock_form();
                    $("#form_ajax").show();
                    setTimeout(function() {
                        $("#form_ajax").hide();
                    }, 5000);
                },
                error:  function(resp) {
                    unblock_form();
                    $("#form_ajax_error").show();
                    // render errors in form fields
                    var errors = JSON.parse(resp.responseText);
                    for (error in errors) {
                        var id = '#id_' + error;
                        $(id).parent('p').prepend(errors[error]);
                    }
                    setTimeout(function() {
                        $("#form_ajax_error").hide();
                    }, 5000);
                }
            };

            $('#ajaxform').ajaxForm(options);
        });
    </script>
    <style>
        #form_ajax_error, .errorlist {
            color: red;
        }
    </style>
</head>
<body>
    <h1>Contact</h1>
    <form id="ajaxform" action="{% url contact_form %}" method="post">
        {% csrf_token %}
        {{ form.non_field_errors }}
        {{ form.as_p }}
        <div id="loading" style="display:none;">
            <span id="load_text">loading...</span>
        </div>
        <div id="form_ajax" style="display:none;">
            <span>Form submit successfully.</span>
        </div>
        <div id="form_ajax_error" style="display:none;">
            <span>Can not submit data due to errors.</span>
        </div>
        <p id="sendwrapper"><input type="submit" value="Send" id="sendbutton"/></p>
    </form>
</body>
</html>
Here I have all the script encoded for show purposes. However it is a bad practice. You should not do it in a real life project.
Here we render the form with default django tags and add 3 conditional form help text messages.
They will display and lightly fade after 5 seconds. Saying either script have submitted/loading/errors returned state.
They are controlled with the jQuery script. We also disabling the entire form with all the elements while AJAX is working. SO user can not change the data accidently.
The plugin itself is binded to the form with jQuery command $('#ajaxform').ajaxForm(options). It uses options object created earlier. Here we have 3 condition handlers, as functions. We are locking the form and displaying the loading text (beforeSubmit), submit succeeded (success) and the most interesting one is error handling function (error). It parses JSON provided by the backend in case of errors. Then modifying the DOM with provided errors, relying on the django form rendering logic to find the "id" of the HTML input that needs an error prepended.
Our form will look like so:

I have collected everything in a git repository. You can clone/copy and run it for yourself. The app has some hooks for debug to slow down request/response. So you will be able to see loading state, etc...
Repository with working copy from this app is here:
https://github.com/garmoncheg/ajax_form_example

Comments? Suggestions?

Nov 9, 2013

CouchDB restoring deleted/updated documents and their data

We are using CouchDB for production and happy with it. It is much more lightweight rather then MongoDB yet powerful. (For our needs at least). But sometimes you have situations that some code deleted/spoiled your Couch Database data. We had some bugs leading to deleting indexes. However compaction have not been run and here is the decision.

There are several ways for different situations. I'll try to cover them all.
So for deleted CouchDB documents you need to:

1. Make sure your document with this id is Deleted.

To do it you need to request CouchDB for this document. E.g. with this string:
$db/$id
Where $db is your CouchDB database name and $id is your deleted document id
it should return something like this:
{"error":"not_found","reason":"deleted"}

2. Get all the revisions of the deleted document.

With this request:
$db/$id?revs=true&open_revs=all 
Where $db is your CouchDB database name and $id is your deleted document id.

3. Parse response.

CouchDb provides a response with revisions in a special coded format. So to parse this JSON response you need to know it's syntax. It's fairly simple.
 Content - Type: application / json

 {
    "_id": "my-couchdb-id",
    "_rev": "6-65624dd5962e59ff09c47ba1be4f726c",
    "_deleted": true,
    "_revisions": {
        "start": 6,
        "ids": ["65624dd5962e59ff09c47ba1be4f726c", "826046dbd125b841e0dba657f65bbb78", "d1485b8d5cccc305f1dbccf65a07199d", "e1293576c567836c80cd973f36d345aa", "2bbc0c49496d3044c80b824be7e30193", "0aa0c2ce9796de25733b0cf46ee15129"]
    }
}
You can get multiple JSON's like I did. Because document was deleted and undeleted several times.
Anyway. here we have a JSON indicating current (last before deletion) revision of the document. ("_rev": "6-65624dd5962e59ff09c47ba1be4f726c",  ) Let's try to recover it.

4. Find out required revision hash.

Note "_revisions" key in later JSON, containing a list of "ids". Note first one has the same revision hash as the last revision. Our target is previous revision (before deletion). E.g. Or latest one if the document we are trying to recover was just updated, but not deleted. We might need next revision code.
SO in our example:
 "ids": ["65624dd5962e59ff09c47ba1be4f726c",
First one is equal to current and the next one is before it.
Note also key start ("start": 6, ). It indicates latest revision counter. You could stick to that. E.g. subtract 1 from latest revision and add it to the second hash with "-". So to recover previous revision you would need to create a code like this: "5-826046dbd125b841e0dba657f65bbb78". Hope you have understood about how have I come to this.

5. Retrieve previous revision.

To do that just ask couch with address:
$db/$id?rev=$previous_revision
Where $db and $id are your previous database name and deleted code, but $previous_revision is obviously your constructed revision counter + revision hash separated by the "-" symbol. 5-826046dbd125b841e0dba657f65bbb78 In our case.
We will get JSOn of a previous document (before deletion). And we may put it back with PUT/POST request.

It's also worth to mention about when you do not know what revision and/or document $id you want to recover. And want to just recover e.g. previous 10 deleted documents. Then you need to look at request:
$db/_changes
It will provide a list of documents manipulations in the database. It's simple enough. But behind the scope of this article.

That's basically it. Questions/comments?

Nov 6, 2013

OS X Mavericks and Last.FM scrobling of iTunes radio


I have used iTunes radio dramatically after installing a new version of iTunes. But the problem is my musical taste is recorded (Historically) at Last.FM profile. So It is something like combining your Last.FM profile with your iTunes Geniuos data. AFAIK iTunes uses it's data to play songs in a given station. After trying several decisions I have found a nice one that simply works.

You need an app called Bowtie. It's built for older OS X'es. But works nicely for this purpose. It has a themed desktop applet. So it's something like a nice bonus. Anyway you can connect it with Last.FM and that's it. Just one thing it sits in your memory. But I have found it does not consume too much resources or has obvious flaws that spoil Mac anyway.
One trick to install it you need to enable it install your system. It is not from apple store so...
as per stackoverflow question it can be done like so:

Go to System Preferences... > Security & Privacy and there should be a button saying Open Anyway, under the General tab.
Original answer at stackoverflow.com

So that's basically it. Enjoy.

Aug 22, 2013

install ssh-copy-id on Mac OS X best way

You often need to create identities for unknown/new servers. Then welcome to this article. I believe i have a best practice way of doing it on a MAC system.
First of all you need your identity file:

1. Generating ssh keys set

You need to use tool that any unix system usually has. It's ssh-keygen. (Skip if you have it already)
Last login: Wed Aug 21 16:07:34 on ttys002
console@username:~$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/username/.ssh/id_rsa): yourkeyname
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in yourkeyname.
Your public key has been saved in yourkeyname.pub.
The key fingerprint is:
XX.XX.XX.XX.XX.XX.XX.XX.XX.XX.XX.XX. console@username
The key's randomart image is:
+--[ RSA 2048]----+
|    +             |
|   +              |
|         +        |
| +                |
|     +            |
|   . +            |
|  . +             |
|   . ...........+ |
|    . .+....      |
+-----------------+
console@username:~$
Note this command input/output. You need to call command ssh-keygen -t rsa and generate a key on your computer. You could do it in some other way, for e.g. on a server. In this example we create 2 SSH keys. They both have yourkeyname in it's body. Except for one is public and called yourkeyname.pub. And you may share your public key, but never share your private. But it;s about simplifying install not cheap talks.
Let's copy your public key to server after you have your suitable key pair.

2. Copy to ssh public key to server

This step is not required and you could easily skip it. However it may already be done at your MAC.
Now let's copy your key with extension .pub to remote server. You could do it manually, but in case of this happening too often it may turn you nuts to type in those letters.
Github user  beautifulcode created a nice script to make your MAC OS X behave like linux in this occasion. I have forked it just in case and gonna use mine forked version farther. However all the credentials belong to author ;).
Ok. For all this to work you need to actually type in 2 commands into console:
https://raw.github.com/garmoncheg/ssh-copy-id-for-OSX/master/ssh-copy-id.sh
chmod +x /usr/local/bin/ssh-copy-id
This will download a script body from a github server (my fork) and change it's credentials to execution mode.

Now this is done and you can use your linux-like command style in MAC OS X. It may look like this.
ssh-copy-id -i yourkeyname.pub username@remoteservername
This will ask for password to your remote ssh server and user: username in our case.
3. Create an alias for your server
It's a long time to type in ssh yourusername@yourserver.com and you can shorten this to ssh srv for e.g. For this you need to create the alias in your ssh keys config.
To make it you need to enter your ~/.ssh directory and edit/create file called config. So type in something like:
console@username:~$ vi ~/.ssh/config
And edit it somehow like so:
# Add this to file
Host srv
                Hostname servername_or_ip
                User username
                IdentityFile ~/.ssh/yourkeyname
Save it (:wq if you use vim). And I recommend using the text editor of your choice, instead.
Now you have to only type ssh srv and here is your remote shell. But please keep in mind responsibility that lies on your shoulders with this. Intruder having your computer receives all the access you would have.

Comments?

Aug 6, 2013

Django: Add Permission model to admin panel

How to add a permissions model to your Django admin?
You may add those lines to anywhere in your code. But it is more convinient to add this to your admin.py or place where django admin custom code lies. Here are them:
from django.contrib.auth.models import Permission
admin.site.register(Permission)
Now you can manage them in in your Django admin panel. May look like this:
Hope this helps.

Jul 23, 2013

How to burn (write) an ISO image to USB drive in all new Mac OS X systems

There are many programs in the market that can handle it for you. But you can do it with default Mac OS X tools. Those steps must be made in terminal. You need to know what you are doing and have root password to your system.
Here is your step by step guide to do it:


1. Convert your image.

You need to change the image type from .iso to .img that dd util supports. To do this type a command like this:
hdiutil convert -format UDRW -o /path/to/destination/file.img /path/to/source/file.iso
You must see output similar to this:
Reading Image File Name Here  (Apple_UDF : 0)…... [ truncated ] .............................
Elapsed Time:  4m 57.725s
Speed: 20.9Mbytes/sec
Savings: 0.0%
created: /path/to/image/file.img.dmg

2. Remove the .dmg extension.

mv /path/to/image/file.img.dmg /path/to/image/file.img

3. Get your flash drive device name.

You can do it in various ways. one that is using standard utils here:
diskutil list
This will list all your disks mounted to your mac. Output should be similar to this:
$ diskutil list
/dev/disk0
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *750.2 GB   disk0
   1:                  Apple_HFS Vault                   750.2 GB   disk0s1
/dev/disk1
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *128.0 GB   disk1
   1:                        EFI                         209.7 MB   disk1s1
   2:                  Apple_HFS Mac OS HD               127.2 GB   disk1s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk1s3
/dev/disk3
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *16.0 GB    disk3
   1:                 DOS_FAT_32 UNTITLED                16.0 GB    disk3s1
So in my case flash drive is named disk3. Note it is not disk3s1, that indicates a partition name not a drive name.

4. Unmount your flash drive.

Note you need to unmount, not eject this flash drive. Command may be like so:
diskutil unmountDisk /dev/disk3
Change that disk3 to your device id.

4. Write your image to flash drive.

To handle this you would use a dd utility. Note param bs=1m, indicating block size to write. Missing this parameter would force dd to run byte by byte copy, resulting run for ages with several gigs of data.
sudo dd if=/path/to/image/file.img of=/dev/rdisk3 bs=1m
Note to change that disk3 to your flash drive id.
You may use rdisk instead of dd to add some speed to this. But this particular run was ok for me.
You may see an output like this, resulting you may eject and use your flash drive farther.
$ sudo dd if=/path/to/image/file.img of=/dev/rdisk3 bs=1m
Password:
6207+1 records in
6207+1 records out
6509363200 bytes transferred in 1124.570118 secs (5788312 bytes/sec)

Hope this help somebody someday. Cheers.
Please comment and share!

Jul 13, 2013

Python converting PDF to Image

I have a task to generate thumbnails of uploaded PDF's. And seems like there no really solid decisions yet. Just garbage on the surface in google results. after googling for a while I found out about many ways to do so. E.g. use python stdin/out to run external command line tool. It might work for you to. But it seemed not so pythonic for me. So I have searched fo better decision. My current is for now to install ImageMagick and MagicWand binding.

Install ImageMagick.

I have used PIL a while ago to work with images. But it made me cry, before I have met sorl-thumbnails. It helped me a lot. But now I have to deal with PDF's. And ImageMagick seems like a complete decision to master it all.  It has to convert pdf to my direct desirables - jpeg. So to install ImageMagick I have used brew. Like this:
brew install imagemagick
However there are many other ways to do so, depending on a platform. But I strongly recommend to look at brew.

Anyway installing ImageMagick is tricky. And in order to have it installed to work with pdf's we need to have freetype and ghostscript packages. In case of absence of ghostscript you could have error like so:
wand.exceptions.DelegateError: Postscript delegate failed 'file.pdf': No such file or directory @ error/pdf.c/ReadPDFImage/682
In case of freetype package absence you will have you PDF rendered without fonts. So be sure those 2 are certainly installed.

Installing Wand

There are several high level bindings for ImageMagick for python, But I have chosen wand as my favorable here.
This is strongly depends on a platform. But nowdays fortunately I can do:
pip install Wand
And I'm happy with it.
Wand is simple enough for my task so I can do convert PDF to image and do simple transformations of my choice with it.

Working with it

Now that we have those things installed we may convert a pdf into image and resize it afterwards.
from wand.image import Image
# Converting first page into JPG
with Image(filename="/thumbnail.pdf[0]") as img:
     img.save(filename="/temp.jpg")
# Resizing this image
with Image(filename="/temp.jpg") as img:
     img.resize(200, 150)
     img.save(filename="/thumbnail_resize.jpg")
I'm sure there are better solutions here.  Note this is a simplified example to show the whole point of this method.

Feel free to suggest better solution in comments.

Jul 11, 2013

Show system files in Finder

Default Mac OS X system behavior is to hide system and hidden unix files (usually starting with a . symbol, e.g.: .bash_history will be out of sight). This makes spotting/opening one of this files with Finder extremely difficult.

you may use your terminal, typing command like "ls -a" that will show all of the files in that particular directory, including hidden and system one's. But sometimes it may become annoying to use terminal while using the GUI.

So. To change this default behavior in finder you need to enter those commands in the terminal window:
defaults write com.apple.finder AppleShowAllFiles TRUE
And after this you need to restart all your Finder processes like so:
killall Finder

Comments?

Jun 27, 2013

Django: How to debug Django

There are several ways of debugging Django. I'll try to cover most common and tell what I know/use time to time. This article may become useful to Django newbies and is not intended to be "the only truth". It may help you know in general about debugging techniques commonly used in Python/Django projects that author is aware of. Let's get started. There are several methods to discuss here.

1. Print out into console.

It may become handy while working with critical bugs that only appear in production for e.g. and are not traceable at development environment... The only way here to run your project manually and do debug in production. Those requirements are so rare that I only use standart (builtin) tools. There is a thing called PDB (Python Debugger). That comes in a standart set of usual python distribution. So it will be available to you at almost any environment where you have console access to your project.
Here is an official documentation for pdb module. And you can use it in many advanced ways. But for the sake of going through I'd just stop on a few usages here.
Assuming this all will be done on a production server. Because this is the most common usecase for this  technique. To debug with pdb you would need to edit your python (Django) script. And add an import statement. e.g.:
import pdb
And then somewhere in code in a place where you need to stop and watch.
pdb.set_trace()
This will basically run your server till the line pdb.set_trace() and stop there bringing you the python console, like the one you would run with executing a command python in your terminal. One main difference you would have all your variables accessed in this point of execution interruption. So you will have ability to type for e.g.:
>>> param
['exapmple', 'values', 'list']
>>> 
assuming this param variable is set before this in code. 
You may also have a list of commands that can lead you to debugging in most ways you can ever need. But most useful is h(elp) command. Anyway that was most of the things you may need to know about pdb and it's usage. Most of the time I set print's and use extremely useful (to my opinion) python command dir. This is how it works:
>>> print param
['exapmple', 'values', 'list']
>>> dir(param)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> 
print will show a your param value. However dir command acts in another way iterating through object's even hidden values. You may find useful to know what you can do. e.g. it shows methods along with class internal defaults. Anyway it may become useful more often.

Those tools are quite sufficient for server and "raw console" debugging. You may also try to study vim.  Most of the Linux distributions (purport production systems) have this editor already installed by default in distribution. There are many other new and much logically easier editors (From the perspective of Microsoft Word user :) ). But this VIM is a formed "golden standard". And you may require those skills at almost any time. 

Also all of this assumes you have run your server project in some accessible console. So you have ability to read console output. Mostly Django projects are run as a service. So you may need to kill this service and manually run command like python manage.py runserver.
That's almost everything you may need to know about server debugging.

2. High level debugging.

WARNING: Skip this if you are a PyCharm or other IDE user. 
Most of the time debugging is done on your dev station. So it's quite reasonable to use some GUI tools. I prefer to code in IDE. It helps me to debug and it's almost the main reason why I use IDE instead of e.g. TextMate or Sublime (Advanced text editors). So main thing is built in handy graphical debugger. Graphical means you can click your mouse through your logical constructions stored in a heap of your project. Anyway let's move on. My preferable IDE is PyCharm. And it's preferable because of it's simplicity and straightforwardness in working with Django based projects. Especially with those project's virtual environments. You can set up a virtual environment and connect PyCharm afterwards to a ready made project. And it is really simple, in comparison to PyDev in Eclipse, for e.g. I do not mind using Eclipse. But initial set up there might be quite hard and problematic to a junior dev. Anyway it all become kind of a "holly war" comments and I believe they are irrelevant to you, my reader. So let's move on. Assume I'm using PyCharm. 

I have a nice article about setting up both PyCharm and Eclipse to work with a Django project. They are relatively old but may give you a glimpse of how to set up your project with IDE. Farther text assumes you know how to set up a project with a PyCharm IDE. So go have a read about it if not. IN general debugging is a part of the process (python process), that is run by your IDE. So debugger connects to it and interrupts execution of your code where you tell him to. PyCharm has a project run management buttons. They look like this:
TO debug you must know how to set up project with this IDE. It's fairy simple. Note button with a green bug on it. It's our debugger. You must execute your project with this button and selected run configuration instead of green button with play sign :). 
You must see something like this in your debug console. Indicating you have debugger on and connected:
Note the red letters "pydev debugger: " here. It saying that debugger is running. Note tabs Console and Debugger here. Now it is time to set the breakpoints. This is done by clicking on a space near a line number (If you have them on). Set up break point may look something like that:
So now when program comes to this line it wil stop execution showing you the stack of this line. and you can click on it's objects. This is extremely handy tool to understand your project structure and your Django principles, how things work in your and typical Django project in general. So this may look something like this:
Note it is highlighted with blue line. (It may differ according to your theme). But this principle is general to almost all IDE's. Now go down to Debugger tabs mentioned earlier. It contains a request object in my case:
You may assume this can be repeated almost anywhere in your project code. So if some output is wrong you may quickly understand where to search next. Or what to change to fix your bug. It is also a nice exercise for a dev to look through his project's data and how they are modified during code execution. Although you must keep it in mind constantly, sometimes it is quite useful to look at it from a "bird's eye view".  Green play button continues code execution. And buttons on top near the tabs help you to navigate through your code without setting additional breakpoints. Keeping mouse on those buttons steady without a click will bring help with their meaning. So I wont stop here for explanations. They are quite obvious and require experimenting anyway. 

You may even learn data structures in Django this way. e.g. Setting breakpoints on where different data types appear and/or modified by copy-pasted text from a Django tutorial ;). Helps to understand what is made and why by the Django itself, not mentioning your own code. Imagine you can do so in templates, with querysets, requests, model instances, whatever fancy data structures you may come out. Anyway this is basically it with "in IDE" debugging. Let's move on.

3. Browser debugging.

May come in handy upon solving some obvious issues. Common tools for this are your browser's debug console. There are many techniques here too. As there are many browsers and tools too. But I will try to showcase the one's I'm using. So let's mention one more thing. There is a project called django-debug-toolbar. It was quite a standart while I was only starting to learn Django. Unfortunately now it is often missed. I do not need it now, because of IDE debugger and big screen to fit everything. But for a novice it may be handy to have your project's common variables and states in the browser itself. to help avoid typical mistyping or similar "easy" bugs. So it looks like that:
So it's a panel in the right of your page overlaying your page. That has several handy functions and various pluggable features. It is quite handy to view you production data for e.g. You just enable it and it is shown when you need it. You can watch server SQL requests, template context and page load time. Isn't it handy.

But let's really move to the browser debugging. I use Chrome. It has a nice built in debug console. It has improved (IMHO) in a recent year or so. Before this I was using Mozilla firefox with a plugin called Firebug. Both those tools are able to show most you may ever need. E.g. what is in browser of your Django project's page. e.g. request that comes to your Django server and response it outputs. Along with headers, cookies, content and so on. 
Chrome debugger may look like this:
It can be accessed by clicking "Inspect Element" in a right mouse button menu. It will show HTML of a page focusing on this element you have clicked. But it's not all it can do. It has a lot of tabs that I won't be stopping on, because they are relevant to JavaScript debugging, rather then Django's. So you may look at page loads and parts of a HTML, CSS styles and so on. Anyway JavaScript comes with Django project always at now-days. But it is a completely another story. 

Hope this will help you understand how debugging is done in Django projects. Have something to add or either I'm wrong somewhere? Dont hesitate co comment here.