Skip to content

Instantly share code, notes, and snippets.

@john1king
Last active December 26, 2015 14:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save john1king/5e80049d694b538dca26 to your computer and use it in GitHub Desktop.
Save john1king/5e80049d694b538dca26 to your computer and use it in GitHub Desktop.
Read Django Source Code

连接数据库

连接数据库的操作被封装在每个 backend 的 DatabaseWrapper._cursor 方法中,大部分都是对参数的处理。

使用 MySQLdb 连接数据库

import MySQLdb as Database
Database.connect(db='app', user='root')

连接当前配置文件中的数据库

from django.db import connection
cursor = connection.cursor()

也可以指定数据库配置名称

from django.db import connections
cursor = connections['test'].cursor()

单个文件运行 Django 模块

Django 的大量代码依赖于配置文件,好在使用默认的配置就能够很好的运行

import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'django.conf.global_settings'

描述器控制类和实例访问

https://github.com/django/django/blob/stable/1.6.x/django/db/models/manager.py#L241-L250

Python 的类方法和实例方法的名称不能重复,想要控制同名的类方法和实例方法有不同的行为就需要借助描述器

class FileDescriptor(object):

    def __init__(self, func):
        self.func = func

    def __get__(self, instance, type):
        if instance is None:
            def _func(filename, *args, **kwargs):
                return self.func(type(filename), *args, **kwargs)
        else:
            def _func(*args, **kwargs):
                return self.func(instance, *args, **kwargs)
        return _func


class File(object):

    def __init__(self, filename):
        self.filename = filename

    @FileDescriptor
    def write(self, content, mode="w"):
        return open(self.filename, mode).write(content)


File.write('foo.x', 'Foo!', 'w')

# same to
f = File('foo.x')
f.write('Foo!', 'w')

Django 是如何防止 SQL 注入的?

答案是 Django 并没有做这件事情,而是全部交给 backend 完成

下例中的代码用 Django 的 ORM 执行一个简单的 SQL注入查询,并打印生成的 SQL语句

from app.models import Foo
qs = Foo.objects.filter(name="foo or 1=1")
print qs.query
# => SELECT "app_foo"."id", "app_foo"."name" FROM "app_foo" WHERE "app_foo"."name" = foo or 1=1

可以看到生成的 SQL语句并不合法, Django 在打印 SQL语句的时候只进行了最简单的处理。真实的执行过程如下

# using 为数据库配置名称,默认为 'default'
compiler = qs.query.get_compiler(using=qs.db)
sql, params = compiler.as_sql()

# 连接数据库
cursor = compiler.connection.cursor()

cursor.excute(sql, params).fetchall()
# => []

查询语句主体和参数分成两部分由 backend 执行,所有不会有 SQL注入危险

用 tree.Node 来表示 foo="FOO" and (bar="BAR" or bar="ARB")

from django.utils import tree

bar = tree.Node([('bar', "BAR"), ('bar', 'ARB')], connector='OR')
foobar = tree.Node([('foo', 'FOO'), bar], connector='AND')

print foobar
# => (AND: ('foo', 'FOO'), (OR: ('bar', 'BAR'), ('bar', 'ARB')))

print foobar.__dict__
# =>
#  {'children': [('foo', 'FOO'), <django.utils.tree.Node at 0x10153f490>],
#   'connector': 'AND',
#   'negated': False,
#   'subtree_parents': []}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment