第十八章: 集成已有的数据库和应用

第十八章: 集成已有的数据库和应用

Django最适合于所谓的green-field开发,即从头开始的一个项目,正如你在一块还长着青草 的未开垦的土地上从零开始建造一栋建筑通常。 然而,尽管Django偏心从头开始的项目,将这个框架和之前遗留的数据库和应用相整合仍然是可能的。 本章就将介绍一些整合的技巧。python

与遗留数据库整合

Django的数据库层从Python代码生成SQL schemas—可是对于遗留数据库,你已经拥有SQL schemas. 这种状况,你须要为已经存在的数据表建立model. 为此,Django自带了一个能够经过读取您的数据表结构来生成model的工具. 该辅助工具称为inspectdb,你能够经过执行manage.pyinspectdb来调用它.shell

使用 inspectdb

inspectdb工具自省你配置文件指向的数据库,针对每个表生成一个Django模型,而后将这些Python模型的代码显示在系统的标准输出里面。数据库

下面是一个从头开始的针对一个典型的遗留数据库的整合过程。 两个前提条件是安装了Django和一个传统数据库。django

经过运行django-admin.py startproject mysite (这里 mysite 是你的项目的名字)创建一个Django项目。 好的,那咱们在这个例子中就用这个 mysite 做为项目的名字。数组

编辑项目中的配置文件, mysite/settings.py ,告诉Django你的数据库链接参数和数据库名。 具体的说,要提供 DATABASE_NAME , DATABASE_ENGINE , DATABASE_USER , DATABASE_PASSWORD , DATABASE_HOST , 和 DATABASE_PORT 这些配置信息.。 (请注意其中的一些设置是可选的。 更多信息参见第5章)服务器

经过运行 pythonmysite/manage.pystartappmyapp (这里 myapp 是你的应用的名字)建立一个Django应用。 这里咱们使用myapp 作为应用名。网络

运行命令 pythonmysite/manage.pyinspectdb。这将检查DATABASE_NAME 数据库中全部的表并打印出为每张表生成的模型类。 看一看输出结果以了解inspectdb能作些什么。app

将标准shell的输出重定向,保存输出到你的应用的 models.py 文件里:框架

python mysite/manage.py inspectdb > mysite/myapp/models.py

编辑 mysite/myapp/models.py 文件以清理生成的 models 而且作一些必要的自定义。 针对这个,下一个节有些好的建议。工具

清理生成的Models

如你可能会预料到的,数据库自省不是完美的,你须要对产生的模型代码作些许清理。 这里提醒一点关于处理生成 models 的要点:

数据库的每个表都会被转化为一个model类 (也就是说,数据库的表和model 类之间是一对一的映射)。 这意味着你须要为多对多链接的表,重构其models 为 ManyToManyField 的对象。

所生成的每个model中的每一个字段都拥有本身的属性,包括id主键字段。 可是,请注意,若是某个model没有主键的话,那么Django会自动为其增长一个id主键字段。 这样一来,你也许但愿移除这样的代码行。

id = models.IntegerField(primary_key=True)

这样作并非仅仅由于这些行是冗余的,并且若是当你的应用须要向这些表中增长新记录时,这些行会致使某些问题。

每个字段类型,如CharField、DateField, 是经过查找数据库列类型如VARCHAR,DATE来肯定的。若是inspectdb没法把某个数据库字段映射到model字段上,它会使用 TextField字段进行代替,而且会在所生成model字段后面加入Python注释“该字段类型是猜的”。 对这要小心,若是必要的话,更改字段类型。

若是你的数据库中的某个字段在Django中找不到合适的对应物,你能够放心的略过它。 Django模型层不要求必须导入你数据库表中的每一个列。

若是数据库中某个列的名字是Python的保留字(好比pass、class或者for等),inspectdb会在每一个属性名后附加上_field,并将db_column属性设置为真实的字段名(也就是pass,class或者for等)。

例如,某张表中包含一个INT类型的列,其列名为for,那么所生成的model将会包含以下所示的一个字段:

for_field = models.IntegerField(db_column='for')

inspectdb 会在该字段后加注 ‘字段重命名,由于它是一个Python保留字’ 。

若是数据库中某张表引用了其余表(正如大多数数据库系统所作的那样),你须要适当的修改所生成 model的顺序,以使得这种引用可以正确映射。 例如,model Book拥有一个针对于model Author的外键,那么后者应该先于前者被定义。若是你想建立一个指向还没有定义的model的关系,那么可使用包含model名的字符串,而不是 model对象自己。

对于PostgreSQL,MySQL和SQLite数据库系统,inspectdb可以自动检测出主 键关系。 也就是说,它会在合适的位置插入primary_key=True。 而对于其余数据库系统,你必须为每个model中至少一个字段插入这样的语句,由于Django的model要求必须拥有一个 primary_key=True的字段。

外键检测仅对PostgreSQL,还有MySQL表中的某些特定类型生效。 至于其余数据库,外键字段将在假定其为INT列的状况下被自动生成为IntegerField。

与认证系统的整合

将Django与其余现有认证系统的用户名和密码或者认证方法进行整合是能够办到的。

例如,你所在的公司也许已经安装了LDAP,而且为每个员工都存储了相应的用户名和密码。 若是用户在LDAP和基于Django的应用上拥有独立的帐号,那么这时不管对于网络管理员仍是用户本身来讲,都是一件很使人头痛的事儿。

为了解决这样的问题,Django认证系统能让您以插件方式与其余认证资源进行交互。 您能够覆盖Diango默认的基于数据库的模式,您还可使用默认的系统与其余系统进行交互。

指定认证后台

在后台,Django维护了一个用于检查认证的后台列表。 当某我的调用 django.contrib.auth.authenticate() (如14章中所述)时,Django会尝试对其认证后台进行遍历认证。 若是第一个认证方法失败,Django会尝试认证第二个,以此类推,一直到尝试完。

认证后台列表在AUTHENTICATION_BACKENDS设置中进行指定。 它应该是指向知道如何认证的Python类的Python路径的名字数组。 这些类能够在你Python路径的任何位置。

默认状况下,AUTHENTICATION_BACKENDS被设置为以下:

('django.contrib.auth.backends.ModelBackend',)

那就是检测Django用户数据库的基本认证模式。

AUTHENTICATION_BACKENDS的顺序很重要,若是用户名和密码在多个后台中都是有效的,那么Django将会在第一个正确匹配后中止进一步的处理。

编写认证后台

一个认证后台其实就是一个实现了以下两个方法的类: get_user(id) 和 authenticate(**credentials) 。

方法 get_user 须要一个参数 id ,这个 id 能够是用户名,数据库ID或者其余任何数值,该方法会返回一个 User 对象。

方法 authenticate 使用证书做为关键参数。 大多数状况下,该方法看起来以下:

class MyBackend(object):
    def authenticate(self, username=None, password=None):
        # Check the username/password and return a User.

可是有时候它也能够认证某个短语,例如:

class MyBackend(object):
    def authenticate(self, token=None):
        # Check the token and return a User.

每个方法中, authenticate 都应该检测它所获取的证书,而且当证书有效时,返回一个匹配于该证书的 User 对象,若是证书无效那么返回 None 。 若是它们不合法,就返回None

如14章中所述,Django管理系统紧密链接于其本身后台数据库的 User 对象。 实现这个功能的最好办法就是为您的后台数据库(如LDAP目录,外部SQL数据库等)中的每一个用户都建立一个对应的Django User对象。 您能够提早写一个脚原本完成这个工做,也能够在某个用户第一次登录的时候在 authenticate 方法中进行实现。

如下是一个示例后台程序,该后台用于认证定义在 setting.py 文件中的username和password变量,而且在该用户第一次认证的时候建立一个相应的Django User 对象。

from django.conf import settings
from django.contrib.auth.models import User, check_password

class SettingsBackend(object):
    """
    Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

    Use the login name, and a hash of the password. For example:

    ADMIN_LOGIN = 'admin'
    ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
    """
    def authenticate(self, username=None, password=None):
        login_valid = (settings.ADMIN_LOGIN == username)
        pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
        if login_valid and pwd_valid:
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                # Create a new user. Note that we can set password
                # to anything, because it won't be checked; the password
                # from settings.py will.
                user = User(username=username, password='get from settings.py')
                user.is_staff = True
                user.is_superuser = True
                user.save()
            return user
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

更多认证模块的后台, 参考Django文档。

和遗留Web应用集成

同由其余技术驱动的应用同样,在相同的Web服务器上运行Django应用也是可行的。 最简单直接的办法就是利用Apaches配置文件httpd.conf,将不一样的URL类型分发至不一样的技术。 (请注意,第12章包含了在Apache/mod_python上配置Django的相关内容,所以在尝试本章集成以前花些时间去仔细阅读第12章或许是 值得的。)

关键在于只有在您的httpd.conf文件中进行了相关定义,Django对某个特定的URL类型的驱动才会被激活。 在第12章中解释的缺省部署方案假定您须要Django去驱动某个特定域上的每个页面。

<Location "/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
    PythonDebug On
</Location>

这里, <Location"/"> 这一行表示用Django处理每一个以根开头的URL.

精妙之处在于Django将<location>指令值限定于一个特定的目录树上。 举个例子,好比说您有一个在某个域中驱动大多数页面的遗留PHP应用,而且您但愿不中断PHP代码的运行而在../admin/位置安装一个Django 域。 要作到这一点,您只需将<location>值设置为/admin/便可。

<Location "/admin/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
    PythonDebug On
</Location>

有了这样的设置,只有那些以/admin/开头的URL地址才会触发Django去进行处理。 其余页面会使用已存在的设置。

请注意,把Diango绑定到的合格的URL(好比在本章例子中的 /admin/ )并不会影响其对URL的解析。 绝对路径对Django才是有效的(例如 /admin/people/person/add/ ),而非截断后的URL(例如 /people/person/add/ )。这意味着你的根URLconf必须包含前缀 /admin/ 。

相关文章
相关标签/搜索