Skip to content


Subversion checkout URL

You can clone with
Download ZIP
100644 299 lines (232 sloc) 13.5 KB
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
1 ==============================================================
2 Chapter 16: Integrating with Legacy Databases and Applications
3 ==============================================================
5 Django is best suited for so-called green-field development -- that is, starting
6 projects from scratch, as if you were constructing a building on a fresh field
7 of green grass. But despite the fact that Django favors from-scratch projects,
8 it's possible to integrate the framework into legacy databases and
9 applications. This chapter explains a few integration strategies.
11 Integrating with a Legacy Database
12 ==================================
14 Django's database layer generates SQL schemas from Python code -- but with
15 a legacy database, you already have the SQL schemas. In such a case,
16 you'll need to create models for your existing database tables. For this
17 purpose, Django comes with a tool that can generate model code by reading your
18 database table layouts. This tool is called ``inspectdb``, and you can call it
19 by executing the command `` inspectdb``.
21 Using ``inspectdb``
22 -------------------
24 The ``inspectdb`` utility introspects the database pointed to by your settings
25 file, determines a Django model representation for each of your tables, and
26 prints the Python model code to standard output.
28 Here's a walk-through of a typical legacy database integration process from
29 scratch. The only assumptions are that Django is installed and that you have a
30 legacy database.
32 1. Create a Django project by running
33 `` startproject mysite`` (where ``mysite`` is your
34 project's name). We'll use ``mysite`` as the project name in this
35 example.
37 2. Edit the settings file in that project, ``mysite/``,
38 to tell Django what your database connection parameters are and what
39 the name of the database is. Specifically, provide the
42 (Note that some of these settings are optional. Refer to Chapter 5 for
43 more information.)
45 3. Create a Django application within your project by running
46 ``python mysite/ startapp myapp`` (where ``myapp`` is your
47 application's name). We'll use ``myapp`` as the application name here.
49 4. Run the command ``python mysite/ inspectdb``. This will
50 examine the tables in the ``DATABASE_NAME`` database and print the
51 generated model class for each table. Take a look at the output to get
52 an idea of what ``inspectdb`` can do.
54 5. Save the output to the ```` file within your application by using
55 standard shell output redirection::
57 python mysite/ inspectdb > mysite/myapp/
59 6. Edit the ``mysite/myapp/`` file to clean up the generated
60 models and make any necessary customizations. We'll give
61 some hints for this in the next section.
63 Cleaning Up Generated Models
64 ----------------------------
66 As you might expect, the database introspection isn't perfect, and you'll need
67 to do some light cleanup of the resulting model code. Here are a few pointers
68 for dealing with the generated models:
70 1. Each database table is converted to a model class (i.e., there is a
71 one-to-one mapping between database tables and model classes). This means
72 that you'll need to refactor the models for any many-to-many join tables
73 into ``ManyToManyField`` objects.
75 2. Each generated model has an attribute for every field, including
76 ``id`` primary key fields. However, recall that Django automatically
77 adds an ``id`` primary key field if a model doesn't have a primary key.
78 Thus, you'll want to remove any lines that look like this::
80 id = models.IntegerField(primary_key=True)
82 Not only are these lines redundant, but also they can cause problems if your
83 application will be adding *new* records to these tables. The
84 ``inspectdb`` command cannot detect whether a field is autoincremented,
85 so it's up to you to change this to ``AutoField``, if necessary.
87 3. Each field's type (e.g., ``CharField``, ``DateField``) is determined by
88 looking at the database column type (e.g., ``VARCHAR``, ``DATE``). If
89 ``inspectdb`` cannot map a column's type to a model field type, it will
90 use ``TextField`` and will insert the Python comment
91 ``'This field type is a guess.'`` next to the field in the generated
92 model. Keep an eye out for that, and change the field type accordingly
93 if needed.
95 If a field in your database has no good Django equivalent, you can
96 safely leave it off. The Django model layer is not required to include
97 every field in your table(s).
99 4. If a database column name is a Python reserved word (such as ``pass``,
100 ``class``, or ``for``), ``inspectdb`` will append ``'_field'`` to the
101 attribute name and set the ``db_column`` attribute to the real field
102 name (e.g., ``pass``, ``class``, or ``for``).
104 For example, if a table has an ``INT`` column called ``for``, the generated
105 model will have a field like this::
107 for_field = models.IntegerField(db_column='for')
109 ``inspectdb`` will insert the Python comment
110 ``'Field renamed because it was a Python reserved word.'`` next to the
111 field.
113 5. If your database contains tables that refer to other tables (as most
114 databases do), you might need to rearrange the order of the generated
115 models so that models that refer to other models are ordered properly.
116 For example, if model ``Book`` has a ``ForeignKey`` to model ``Author``,
117 model ``Author`` should be defined before model ``Book``. If you need
118 to create a relationship on a model that has not yet been defined, you
119 can use the name of the model, rather than the model object itself.
121 6. ``inspectdb`` detects primary keys for PostgreSQL, MySQL, and SQLite.
122 That is, it inserts ``primary_key=True`` where appropriate. For other
123 databases, you'll need to insert ``primary_key=True`` for at least one
124 field in each model, because Django models are required to have a
125 ``primary_key=True`` field.
127 7. Foreign-key detection only works with PostgreSQL and with certain types
128 of MySQL tables. In other cases, foreign-key fields will be generated as
129 ``IntegerField``s, assuming the foreign-key column was an ``INT``
130 column.
132 Integrating with an Authentication System
133 =========================================
135 It's possible to integrate Django with an existing authentication system --
136 another source of usernames and passwords or authentication methods.
138 For example, your company may already have an LDAP setup that stores a username
139 and password for every employee. It would be a hassle for both the network
140 administrator and the users themselves if users had separate accounts in LDAP
141 and the Django-based applications.
143 To handle situations like this, the Django authentication system lets you
144 plug in other authentication sources. You can override Django's default
145 database-based scheme, or you can use the default system in tandem with other
146 systems.
148 Specifying Authentication Back-ends
149 -----------------------------------
151 Behind the scenes, Django maintains a list of "authentication back-ends" that it
152 checks for authentication. When somebody calls
153 ``django.contrib.auth.authenticate()`` (as described in Chapter 12), Django
154 tries authenticating across all of its authentication back-ends. If the first
155 authentication method fails, Django tries the second one, and so on, until all
156 back-ends have been attempted.
158 The list of authentication back-ends to use is specified in the
159 ``AUTHENTICATION_BACKENDS`` setting. This should be a tuple of Python path
160 names that point to Python classes that know how to authenticate. These classes
161 can be anywhere on your Python path.
163 By default, ``AUTHENTICATION_BACKENDS`` is set to the following::
165 ('django.contrib.auth.backends.ModelBackend',)
167 That's the basic authentication scheme that checks the Django users database.
169 The order of ``AUTHENTICATION_BACKENDS`` matters, so if the same username and
170 password are valid in multiple back-ends, Django will stop processing at the
171 first positive match.
173 Writing an Authentication Back-end
174 ----------------------------------
176 An authentication back-end is a class that implements two methods:
177 ``get_user(id)`` and ``authenticate(**credentials)``.
179 The ``get_user`` method takes an ``id`` -- which could be a username, database
180 ID, or whatever -- and returns a ``User`` object.
182 The ``authenticate`` method takes credentials as keyword arguments. Most of
183 the time it looks like this::
185 class MyBackend(object):
186 def authenticate(self, username=None, password=None):
187 # Check the username/password and return a User.
189 But it could also authenticate a token, like so::
191 class MyBackend(object):
192 def authenticate(self, token=None):
193 # Check the token and return a User.
195 Either way, ``authenticate`` should check the credentials it gets, and it
196 should return a ``User`` object that matches those credentials, if the
197 credentials are valid. If they're not valid, it should return ``None``.
199 The Django admin system is tightly coupled to Django's own database-backed
200 ``User`` object described in Chapter 12. The best way to deal with this is to
201 create a Django ``User`` object for each user that exists for your back-end
202 (e.g., in your LDAP directory, your external SQL database, etc.). Either you can
203 write a script to do this in advance or your ``authenticate`` method can do it
204 the first time a user logs in.
206 Here's an example back-end that authenticates against a username and password
207 variable defined in your ```` file and creates a Django ``User``
208 object the first time a user authenticates::
210 from django.conf import settings
211 from django.contrib.auth.models import User, check_password
213 class SettingsBackend(object):
214 """
215 Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
217 Use the login name, and a hash of the password. For example:
219 ADMIN_LOGIN = 'admin'
220 ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
221 """
222 def authenticate(self, username=None, password=None):
223 login_valid = (settings.ADMIN_LOGIN == username)
224 pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
225 if login_valid and pwd_valid:
226 try:
227 user = User.objects.get(username=username)
228 except User.DoesNotExist:
229 # Create a new user. Note that we can set password
230 # to anything, because it won't be checked; the password
231 # from will.
232 user = User(username=username, password='get from')
233 user.is_staff = True
234 user.is_superuser = True
236 return user
237 return None
239 def get_user(self, user_id):
240 try:
241 return User.objects.get(pk=user_id)
242 except User.DoesNotExist:
243 return None
245 Integrating with Legacy Web Applications
246 ========================================
248 It's possible to run a Django application on the same Web server as an
249 application powered by another technology. The most straightforward way of
250 doing this is to use Apache's configuration file, ``httpd.conf``, to delegate
251 different URL patterns to different technologies. (Note that Chapter 20 covers
252 Django deployment on Apache/mod_python, so it might be worth reading that
253 chapter first before attempting this integration.)
255 The key is that Django will be activated for a particular URL pattern only if
256 your ``httpd.conf`` file says so. The default deployment explained in Chapter
257 20 assumes you want Django to power every page on a particular domain::
259 <Location "/">
260 SetHandler python-program
261 PythonHandler django.core.handlers.modpython
262 SetEnv DJANGO_SETTINGS_MODULE mysite.settings
263 PythonDebug On
264 </Location>
266 Here, the ``<Location "/">`` line means "handle every URL, starting at the
267 root," with Django.
269 It's perfectly fine to limit this ``<Location>`` directive to a certain
270 directory tree. For example, say you have a legacy PHP application that powers
271 most pages on a domain and you want to install a Django admin site at
272 ``/admin/`` without disrupting the PHP code. To do this, just set the
273 ``<Location>`` directive to ``/admin/``::
275 <Location "/admin/">
276 SetHandler python-program
277 PythonHandler django.core.handlers.modpython
278 SetEnv DJANGO_SETTINGS_MODULE mysite.settings
279 PythonDebug On
280 </Location>
282 With this in place, only the URLs that start with ``/admin/`` will activate
283 Django. Any other page will use whatever infrastructure already existed.
285 Note that attaching Django to a qualified URL (such as ``/admin/`` in this
286 section's example) does not affect the Django URL parsing. Django works with the
287 absolute URL (e.g., ``/admin/people/person/add/``), not a "stripped" version of
288 the URL (e.g., ``/people/person/add/``). This means that your root URLconf
289 should include the leading ``/admin/``.
291 What's Next?
292 ============
294 Speaking of the Django admin site and bending the framework to fit legacy needs,
295 another common task is to customize the Django admin site. The `next chapter`_
296 focuses on such customization.
298 .. _next chapter: ../chapter17/
Something went wrong with that request. Please try again.