Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 303 lines (235 sloc) 13.612 kb
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
1 ==============================================================
2 Chapter 18: Integrating with Legacy Databases and Applications
3 ==============================================================
4
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.
10
11 Integrating with a Legacy Database
12 ==================================
13
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 ``manage.py inspectdb``.
20
21 Using ``inspectdb``
22 -------------------
23
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.
27
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.
31
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
32 1. Create a Django project by running
33 ``django-admin.py startproject mysite`` (where ``mysite`` is your
34 project's name). We'll use ``mysite`` as the project name in this
35 example.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
36
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
37 2. Edit the settings file in that project, ``mysite/settings.py``,
38 to tell Django what your database connection parameters are and what
39 the name of the database is. Specifically, provide the
40 ``DATABASE_NAME``, ``DATABASE_ENGINE``, ``DATABASE_USER``,
41 ``DATABASE_PASSWORD``, ``DATABASE_HOST``, and ``DATABASE_PORT`` settings.
42 (Note that some of these settings are optional. Refer to Chapter 5 for
43 more information.)
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
44
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
45 3. Create a Django application within your project by running
46 ``python mysite/manage.py startapp myapp`` (where ``myapp`` is your
47 application's name). We'll use ``myapp`` as the application name here.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
48
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
49 4. Run the command ``python mysite/manage.py 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.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
53
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
54 5. Save the output to the ``models.py`` file within your application by using
55 standard shell output redirection::
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
56
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
57 python mysite/manage.py inspectdb > mysite/myapp/models.py
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
58
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
59 6. Edit the ``mysite/myapp/models.py`` 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.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
62
63 Cleaning Up Generated Models
64 ----------------------------
65
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:
69
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
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.
74
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::
79
80 id = models.IntegerField(primary_key=True)
81
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.
84
85 3. Each field's type (e.g., ``CharField``, ``DateField``) is determined by
86 looking at the database column type (e.g., ``VARCHAR``, ``DATE``). If
87 ``inspectdb`` cannot map a column's type to a model field type, it will
88 use ``TextField`` and will insert the Python comment
89 ``'This field type is a guess.'`` next to the field in the generated
90 model. Keep an eye out for that, and change the field type accordingly
91 if needed.
92
93 If a field in your database has no good Django equivalent, you can
94 safely leave it off. The Django model layer is not required to include
95 every field in your table(s).
96
97 4. If a database column name is a Python reserved word (such as ``pass``,
98 ``class``, or ``for``), ``inspectdb`` will append ``'_field'`` to the
99 attribute name and set the ``db_column`` attribute to the real field
100 name (e.g., ``pass``, ``class``, or ``for``).
101
102 For example, if a table has an ``INT`` column called ``for``, the generated
103 model will have a field like this::
104
105 for_field = models.IntegerField(db_column='for')
106
107 ``inspectdb`` will insert the Python comment
108 ``'Field renamed because it was a Python reserved word.'`` next to the
109 field.
110
111 5. If your database contains tables that refer to other tables (as most
112 databases do), you might need to rearrange the order of the generated
113 models so that models that refer to other models are ordered properly.
114 For example, if model ``Book`` has a ``ForeignKey`` to model ``Author``,
115 model ``Author`` should be defined before model ``Book``. If you need
116 to create a relationship on a model that has not yet been defined, you
117 can use a string containing the name of the model, rather than the model
118 object itself.
119
120 6. ``inspectdb`` detects primary keys for PostgreSQL, MySQL, and SQLite.
121 That is, it inserts ``primary_key=True`` where appropriate. For other
122 databases, you'll need to insert ``primary_key=True`` for at least one
123 field in each model, because Django models are required to have a
124 ``primary_key=True`` field.
125
126 7. Foreign-key detection only works with PostgreSQL and with certain types
127 of MySQL tables. In other cases, foreign-key fields will be generated as
128 ``IntegerField``s, assuming the foreign-key column was an ``INT``
129 column.
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
130
131 Integrating with an Authentication System
132 =========================================
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
133
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
134 It's possible to integrate Django with an existing authentication system --
135 another source of usernames and passwords or authentication methods.
136
137 For example, your company may already have an LDAP setup that stores a username
138 and password for every employee. It would be a hassle for both the network
139 administrator and the users themselves if users had separate accounts in LDAP
140 and the Django-based applications.
141
142 To handle situations like this, the Django authentication system lets you
143 plug in other authentication sources. You can override Django's default
144 database-based scheme, or you can use the default system in tandem with other
145 systems.
146
147 Specifying Authentication Backends
148 ----------------------------------
149
150 Behind the scenes, Django maintains a list of "authentication backends" that it
151 checks for authentication. When somebody calls
152 ``django.contrib.auth.authenticate()`` (as described in Chapter 14), Django
153 tries authenticating across all of its authentication backends. If the first
154 authentication method fails, Django tries the second one, and so on, until all
155 backends have been attempted.
156
157 The list of authentication backends to use is specified in the
158 ``AUTHENTICATION_BACKENDS`` setting. This should be a tuple of Python path
159 names that point to Python classes that know how to authenticate. These classes
160 can be anywhere on your Python path.
161
162 By default, ``AUTHENTICATION_BACKENDS`` is set to the following::
163
164 ('django.contrib.auth.backends.ModelBackend',)
165
166 That's the basic authentication scheme that checks the Django users database.
167
168 The order of ``AUTHENTICATION_BACKENDS`` matters, so if the same username and
169 password are valid in multiple backends, Django will stop processing at the
170 first positive match.
171
172 Writing an Authentication Backend
173 ---------------------------------
174
175 An authentication backend is a class that implements two methods:
176 ``get_user(id)`` and ``authenticate(**credentials)``.
177
178 The ``get_user`` method takes an ``id`` -- which could be a username, database
179 ID, or whatever -- and returns a ``User`` object.
180
181 The ``authenticate`` method takes credentials as keyword arguments. Most of
182 the time it looks like this::
183
184 class MyBackend(object):
185 def authenticate(self, username=None, password=None):
186 # Check the username/password and return a User.
187
188 But it could also authenticate a token, like so::
189
190 class MyBackend(object):
191 def authenticate(self, token=None):
192 # Check the token and return a User.
193
194 Either way, ``authenticate`` should check the credentials it gets, and it
195 should return a ``User`` object that matches those credentials, if the
196 credentials are valid. If they're not valid, it should return ``None``.
197
198 The Django admin system is tightly coupled to Django's own database-backed
199 ``User`` object described in Chapter 14. The best way to deal with this is to
fdc4800 @jacobian Fixed a bunch of reST markup errors.
jacobian authored
200 create a Django ``User`` object for each user that exists for your backend
201 (e.g., in your LDAP directory, your external SQL database, etc.). Either you can
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
202 write a script to do this in advance or your ``authenticate`` method can do it
203 the first time a user logs in.
204
205 Here's an example backend that authenticates against a username and password
206 variable defined in your ``settings.py`` file and creates a Django ``User``
207 object the first time a user authenticates::
208
209 from django.conf import settings
210 from django.contrib.auth.models import User, check_password
211
212 class SettingsBackend(object):
213 """
214 Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
215
216 Use the login name, and a hash of the password. For example:
217
218 ADMIN_LOGIN = 'admin'
219 ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
220 """
221 def authenticate(self, username=None, password=None):
222 login_valid = (settings.ADMIN_LOGIN == username)
223 pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
224 if login_valid and pwd_valid:
225 try:
226 user = User.objects.get(username=username)
227 except User.DoesNotExist:
228 # Create a new user. Note that we can set password
229 # to anything, because it won't be checked; the password
230 # from settings.py will.
231 user = User(username=username, password='get from settings.py')
232 user.is_staff = True
233 user.is_superuser = True
234 user.save()
235 return user
236 return None
237
238 def get_user(self, user_id):
239 try:
240 return User.objects.get(pk=user_id)
241 except User.DoesNotExist:
242 return None
243
244 For more on authentication backends, see the official Django documentation.
245
246 Integrating with Legacy Web Applications
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
247 ========================================
248
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
249 It's possible to run a Django application on the same Web server as an
250 application powered by another technology. The most straightforward way of
251 doing this is to use Apache's configuration file, ``httpd.conf``, to delegate
252 different URL patterns to different technologies. (Note that Chapter 12 covers
253 Django deployment on Apache/mod_python, so it might be worth reading that
254 chapter first before attempting this integration.)
255
256 The key is that Django will be activated for a particular URL pattern only if
257 your ``httpd.conf`` file says so. The default deployment explained in Chapter
258 12 assumes you want Django to power every page on a particular domain::
259
260 <Location "/">
261 SetHandler python-program
262 PythonHandler django.core.handlers.modpython
263 SetEnv DJANGO_SETTINGS_MODULE mysite.settings
264 PythonDebug On
265 </Location>
266
267 Here, the ``<Location "/">`` line means "handle every URL, starting at the
268 root," with Django.
269
270 It's perfectly fine to limit this ``<Location>`` directive to a certain
271 directory tree. For example, say you have a legacy PHP application that powers
272 most pages on a domain and you want to install a Django admin site at
273 ``/admin/`` without disrupting the PHP code. To do this, just set the
274 ``<Location>`` directive to ``/admin/``::
275
276 <Location "/admin/">
277 SetHandler python-program
278 PythonHandler django.core.handlers.modpython
279 SetEnv DJANGO_SETTINGS_MODULE mysite.settings
280 PythonDebug On
281 </Location>
282
283 With this in place, only the URLs that start with ``/admin/`` will activate
284 Django. Any other page will use whatever infrastructure already existed.
285
286 Note that attaching Django to a qualified URL (such as ``/admin/`` in this
287 section's example) does not affect the Django URL parsing. Django works with the
288 absolute URL (e.g., ``/admin/people/person/add/``), not a "stripped" version of
289 the URL (e.g., ``/people/person/add/``). This means that your root URLconf
290 should include the leading ``/admin/``.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
291
292 What's Next?
293 ============
294
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
295 If you're a native English speaker, you might not have noticed one of the
296 coolest features of Django's admin site: it's available in more than 50
297 different languages! This is made possible by Django's internationalization
298 framework (and the hard work of Django's volunteer translators). The
299 `next chapter`_ explains how to use this framework to provide localized Django
300 sites.
acc918f @jacobian Initial import of djangobook from private SVN repo.
jacobian authored
301
d40cfe7 @jacobian Restored *2.0* version of the book, not 1.0!
jacobian authored
302 .. _next chapter: ../chapter19/
Something went wrong with that request. Please try again.