My personal collection of Python (and Django) recipes

And now something completely different: my personal, living collection of Python (and Django) recipes. Enjoy!

How to log SQL queries in Django

Most Django developers use Django debug toolbar for investigating SQL queries. My preference instead is for a simple logging configuration to show SQL queries in the console. In the development section of Django settings I use the following snippet:

LOGGING = {
    "version": 1,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler'
        }
    },
    "loggers": {
        "django.db.backends": {
            'handlers': ['console'],
            "level": "DEBUG",
        },
    },
}

With this configuration any SQL query is logged to the console. Useful both for production and development.

How to get Django SQL migration queries

Sometimes it's convenient to check what sort of SQL query a Django migration will apply. I found this trick particularly useful too when running workshops for showing to the students the exact SQL instructions that Django generates.

With the sqlmigrate command from Django you can see the SQL statements of any migration. For example given an app named blog and a migration for this app with id 0001 you can run:

python manage.py sqlmigrate blog 0001

With this command you'll see the SQL queries Django applies to the database.

Django: How to Fix "missing 1 required positional argument: on_delete"

Django 2.0 has some nice improvements over older versions. Among the new feature there are some minor changes such as with the ForeignKey field.

Whether you're upgrading Django or following an older tutorial you may stumble across this error:

TypeError: __init__() missing 1 required positional argument: 'on_delete'

Django raises it during the migration process:

python manage.py migrate

Up until Django 1.9 a model would look like the following:

from django.db import models

class Article(models.Model):
    category = models.ForeignKey(Category)
    title =  models.CharField(max_length=55)
    # ...
    
    def __str__(self):
        return self.title

ForeignKey is a Django field for defining a many-to-one relationship. Up until Django 1.9 the ForeignKey field required a single argument: the model to map to. Since Django 2.0 the ForeignKey field requires two positional arguments:

  • the model to map to
  • the on_delete argument

You can find more about on_delete by reading the documentation, but for a quick fix of "missing 1 required positional argument: on_delete" you can update the model:

from django.db import models

class Article(models.Model):
    category = models.ForeignKey('Category', on_delete=models.PROTECT)
    title =  models.CharField(max_length=55)
    # ...
    
    def __str__(self):
        return self.title

After fixing ForeignKey you'll be able to run migrations without any trouble. Be aware that on_delete is potentially destructive. Carefully read the documentation and choose wisely!

How to make an installable Python package

For a Python package to be installable the project should have a file named setup.py in the project root. setup.py should have a call to the setup function from setuptools which takes at least the following arguments:

from setuptools import setup

setup(
    name="tweeter",
    version="0.0.1",
    description="Tweet bot",
    author="You don't want to know",
    packages=["tweeter"],
    install_requires=["tweepy>=3.8.0"],
)
  • name is the package name, tweeter in my example
  • version is the package version
  • description is a short summary of the package's purpose
  • author is of course the package's author
  • packages is the name of your package
  • install_requires is a list of dependencies for the package, much like "dependencies" in package.json

Once a package is ready you can install it with pip, either from the Python package index (if you published it there) or from a Github repo (see below).

Reference: Writing the setup script.

How to install a Python package from a Github repo

Long story short: installing a zipped version is even better than cloning the whole repo, and reportedly faster. All you need to do is grab the URL from Github:

pip install https://github.com/valentinogagliardi/tweeter/archive/0.0.1.zip

Now you might ask how to install a Python package from a Github branch? Look again at the URL below. 0.0.1 is the branch. Easy peasy!

How to install a Python package from a Github repo with requirements.txt

It's easy as declaring the same URL in requirements.txt:

https://github.com/valentinogagliardi/tweeter/archive/0.0.1.zip

How to create a Django installation template

A Django project template is the natural solution when the default Django project format is not enough anymore. Learn how to make your own in this tutorial.

Python dictionaries and the get method

Python dictionaries are containers for key/value pairs (much like JavaScript objects):

my_dict = {
    "name": "John", 
    "city": "Rome", 
    "age": 44
}

To access or modify a value from the dictionary simply call the key in square brackets:

my_dict = {
    "name": "John", 
    "city": "Rome", 
    "age": 44
}

my_dict["name"]

my_dict["city"] = "Florence"

Now, things get interesting when trying to access a non-existent key:

my_dict = {
    "name": "John", 
    "city": "Rome", 
    "age": 44
    }

my_dict["not here"]

You get:

Traceback (most recent call last):
  File "<input>", line 1, in <module>
KeyError: 'not here'

The KeyError exception is raised any time Python can't find a dictionary's key. To catch these kind of errors you can use try/catch, but that would be not optimal, especially for multiple objects.

Luckily the dict object in Python has a get() method which is more convenient than direct access because:

  • it does not raise any exception
  • you can provide a default value

Here's dict.get() in action:

my_dict = {
    "name": "John", 
    "city": "Rome", 
    "age": 44
    }

# No error is thrown
my_dict.get("not here")

# Provide a default
my_dict.get("not here", "a default value")

How to get a random object for a model with the Django ORM

Imagine a simple Django model:

from django.db import models


class Link(models.Model):
    title = models.CharField(max_length=250, unique=True)
    url = models.URLField(max_length=250, unique=True)
    tags = models.ManyToManyField(Tag)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"{self.title}"

Imagine also you have a bunch of objects for this model in the database and you want to fetch a random object with the Django ORM. Here's how you'd go:

link = Link.objects.order_by("?").first()

Reference: order-by.

Python recipes: filtering lists with list comprehension

To filter a list in Python I prefer using list comprehension over high order functions (like in JavaScript):

numbers = [3, 4, 88, 85, 99, 150]


def greater_than(x, y=0):
    return x > y


filtered_numbers = [n for n in numbers if greater_than(n, 88)]

Python recipes: the sum built-in

The sum() built-in function in Python takes an iterable (like a list) and returns the sum of said iterable. If you pass a list of numbers and booleans the latter are converted respectively to 0 for False and 1 for True. Is that some sort of type coercion like in JS? Python:

sum([3, 4, 5, 6, False, True, True, True])

# Output: 21

The sum() function resembles JavaScript's reduce:

[3, 4, 5, 6, false, true, true, true].reduce(function(accumulator, element) {
  return accumulator + element;
});
Valentino Gagliardi

Hi! I’m Valentino! Educator and consultant, I help people learning to code with on-site and remote workshops. Looking for JavaScript and Python training? Let’s get in touch!

More from the blog: