Compare commits

...

372 Commits

Author SHA1 Message Date
Ramon Moraes 5864e1f8e9 [1.4.x] Updated xhtml2pdf URL in docs. 2016-02-16 07:10:13 -05:00
Tim Graham 018efef59a [1.4.x] Post-release version bump. 2015-08-18 13:32:24 -04:00
Tim Graham 9ff23eb7cc [1.4.x] Bumped version for 1.4.22 release. 2015-08-18 08:39:59 -04:00
Tim Graham 575f59f9bc [1.4.x] Fixed DoS possiblity in contrib.auth.views.logout()
Refs #20936 -- When logging out/ending a session, don't create a new, empty session.

Previously, when logging out, the existing session was overwritten by a
new sessionid instead of deleting the session altogether.

This behavior added overhead by creating a new session record in
whichever backend was in use: db, cache, etc.

This extra session is unnecessary at the time since no session data is
meant to be preserved when explicitly logging out.

Backport of 393c0e2422,
088579638b, and
2dee853ed4 from master

Thanks Florian Apolloner and Carl Meyer for review.

This is a security fix.
2015-08-18 08:35:42 -04:00
Tim Graham 8b0d63914f [1.4.x] Added stub release notes for security releases. 2015-08-18 08:35:33 -04:00
Tim Graham 3b324970e3 [1.4.x] Fixed #25119 -- Disabled wheel support. 2015-07-13 19:57:52 -04:00
Tim Graham 3df6495c12 [1.4.x] Post-release version bump. 2015-07-08 16:01:55 -04:00
Tim Graham 622a11513e [1.4.x] Bumped version for 1.4.21 release. 2015-07-08 07:39:43 -04:00
Tim Graham 1ba1cdce7d [1.4.x] Prevented newlines from being accepted in some validators.
This is a security fix; disclosure to follow shortly.

Thanks to Sjoerd Job Postmus for the report and draft patch.
2015-07-08 07:38:06 -04:00
Carl Meyer 2e47f3e401 [1.4.x] Fixed #19324 -- Avoided creating a session record when loading the session.
The session record is now only created if/when the session is modified. This
prevents a potential DoS via creation of many empty session records.

This is a security fix; disclosure to follow shortly.
2015-07-08 07:38:06 -04:00
Tim Graham c570a5ec3e [1.4.x] Added security release note stubs. 2015-07-08 07:38:06 -04:00
Tim Graham 91a395fa80 [1.4.x] Backported .gitignore and .hgignore from master. 2015-07-01 10:41:23 -04:00
Tim Graham 664ad1252c [1.4.x] Added link to download page to find supported versions.
Backport of 8c4827ec1d from master
2015-04-04 08:00:44 -04:00
Tim Graham b2a7878c10 [1.4.x] Post-release version bump. 2015-03-18 20:22:09 -04:00
Tim Graham 5388692144 [1.4.x] Bumped version for 1.4.20 release. 2015-03-18 08:43:42 -04:00
Tim Graham 2342693b31 [1.4.x] Made is_safe_url() reject URLs that start with control characters.
This is a security fix; disclosure to follow shortly.
2015-03-18 08:39:37 -04:00
Tim Graham 3b20558beb [1.4.x] Added stub release notes for security releases. 2015-03-18 08:39:12 -04:00
Carl Meyer 785e57e296 [1.4.x] Fix an encoding preamble so the tests pass on 2.7.9.
It seems there was a change in the parsing of encoding preambles in Python
2.7.9, compared to previous 2.7.x Pythons. This is a backport of the only piece
of e520a73eee that's needed to prevent an import
failure under 2.7.9.
2015-02-06 20:20:02 -07:00
Tim Graham e60557c249 [1.4.x] Fixed #24238 -- Removed unused WSGIRequestHandler.get_environ()
Also moved the test as it wasn't running.
2015-01-28 12:32:15 -05:00
Tim Graham 4376d6ef7b [1.4.x] Post-release version bump. 2015-01-27 12:26:26 -05:00
Tim Graham 7dd4c5221a [1.4.x] Bumped version for 1.4.19 release. 2015-01-27 11:55:02 -05:00
Benjamin Richter 1e39d0f628 [1.4.x] Fixed #24158 -- Allowed GZipMiddleware to work with streaming responses
Backport of django.utils.text.compress_sequence and fix for
django.middleware.gzip.GZipMiddleware when using iterators as
response.content.
2015-01-26 19:22:47 -05:00
Tim Graham 9435474068 [1.4.x] Designated Django 1.8 as the next LTS.
Backport of c38db4d7e0 from master
2015-01-19 12:09:43 -05:00
Tim Graham 99e6ac77f2 [1.4.x] Fixed a static view test on Windows.
Backport of a6f144fd4f from master
2015-01-14 13:57:59 -05:00
Tim Graham 4296a1da8b [1.4.x] Post-release version bump. 2015-01-13 14:16:07 -05:00
Tim Graham bd9dcd226b [1.4.x] Bumped version for 1.4.18 release. 2015-01-13 13:14:08 -05:00
Tim Graham 88b7957b34 [1.4.x] Added dates to release notes. 2015-01-13 13:10:54 -05:00
Tim Graham d020da6646 [1.4.x] Prevented views.static.serve() from using large memory on large files.
This is a security fix. Disclosure following shortly.
2015-01-05 13:43:54 -05:00
Tim Graham 4c241f1b71 [1.4.x] Fixed is_safe_url() to handle leading whitespace.
This is a security fix. Disclosure following shortly.
2015-01-05 13:43:32 -05:00
Carl Meyer 4f6fffc1dc [1.4.x] Stripped headers containing underscores to prevent spoofing in WSGI environ.
This is a security fix. Disclosure following shortly.

Thanks to Jedediah Smith for the report.
2015-01-05 13:43:15 -05:00
Tim Graham 113a8980f4 [1.4.x] Added stub release notes for security releases. 2015-01-05 13:42:52 -05:00
Tim Graham 2fd8054fda [1.4.x] Fixed #24081 -- Downgraded six to 1.8.0.
This reverts commit a25c444bc7.

six 1.9+ requires Python 2.6 so this commit restores Python 2.5 compatibility.
2015-01-05 13:41:06 -05:00
Tim Graham 032ffade8a [1.4.x] Removed wheel generation from Makefile. 2015-01-02 22:01:51 -05:00
Tim Graham 52136afda4 [1.4.x] Post-release version bump. 2015-01-02 21:49:44 -05:00
Tim Graham 592187e11b [1.4.x] Bumped version for 1.4.17 release. 2015-01-02 21:07:00 -05:00
Tim Graham 35dc639cd6 [1.4.x] Added dates to release notes.
Backport of 15cd71ed24 from master
2015-01-02 19:23:14 -05:00
Tim Graham a25c444bc7 [1.4.x] Updated six to 1.9.0.
Backport of 52f0b2b622 from master
2015-01-02 13:38:58 -05:00
Simon Charette 5940da16af [1.4.x] Fixed #23754 -- Always allowed reference to the primary key in the admin
This change allows dynamically created inlines "Add related" button to work
correcly as long as their associated foreign key is pointing to the primary
key of the related model.

Thanks to amorce for the report, Julien Phalip for the initial patch,
and Collin Anderson for the review.

Backport of f9c4e14aec from master
2014-11-25 14:04:56 -05:00
Tim Graham c83b024b37 [1.4.x] Removed thread customizations of six which are now built-in.
Backport of 7ef81b5cdd from master
2014-11-13 11:36:21 +01:00
Tim Graham a1dcd82b28 [1.4.x] Updated six to 1.8.0.
Backport of 81477c91f6 from master
2014-11-04 21:30:21 -05:00
Tim Graham 486b6ca3bc [1.4.x] Post-release version bump. 2014-10-22 13:33:07 -04:00
James Bennett 151d6dbf9c [1.4.x] Bump version numbers for bugfix release. 2014-10-22 12:36:19 -04:00
Tim Graham a92e386e26 [1.4.x] Added release dates to release notes.
Backport of 9dc782b631 from master
2014-10-22 12:25:45 -04:00
Tim Graham 643374bcf5 [1.4.x] Fixed #23631 -- Removed outdated note on MySQL timezone support.
Thanks marfire for the report.

Backport of 9db3653670 from master
2014-10-10 15:22:46 -04:00
Emmanuelle Delescolle f58392d8d8 [1.4.x] Fixed #23604 -- Allowed related m2m fields to be references in the admin.
Thanks Simon Charette for review.

Backport of a24cf21722 from master
2014-10-06 09:08:45 -04:00
Tim Graham df657a7682 [1.4.x] Required numpy < 1.9 for tests; refs #23489.
Backport of 4743a94429 from stable/1.7.x
2014-09-29 19:47:33 -04:00
Joseph Dougherty 3132edae41 [1.4.x] Fixed #23499 -- Error in built-in template tag "now" documentation
Backport of ab8248361e from master.
2014-09-17 09:26:45 +02:00
Claude Paroz ba2be27613 [1.4.x] Fixed #20036 -- Improved GEOS version string parsing
Thanks chikiro.spam at gmail.com for the report.
2014-09-11 20:54:33 +02:00
Simon Charette 065caafa70 [1.4.x] Fixed #23431 -- Allowed inline and hidden references to admin fields.
This fixes a regression introduced by the 53ff096982 security fix.

Thanks to @a1tus for the report and Tim for the review.

refs #23329.

Backport of 342ccbd from master
2014-09-08 14:22:29 -04:00
Tim Graham 78085844a7 [1.4.x] Added dates to release notes.
Backport of 0fd23545db from master
2014-09-02 21:36:44 -04:00
Tim Graham 89157fe11f [1.4.x] Post release version bump. 2014-09-02 21:07:29 -04:00
James Bennett 0517f498cd [1.4.x] Bump version numbers for bugfix release. 2014-09-02 15:43:24 -05:00
Simon Charette 4685026840 [1.4.x] Fixed #23329 -- Allowed inherited and m2m fields to be referenced in the admin.
Thanks to Trac alias Markush2010 and ross for the detailed reports.

Backport of 3cbb759 from master
2014-08-27 22:12:37 -04:00
Tim Graham 8adc56ca78 [1.4.x] Fixed spelling mistake in file docs.
Backport of a3e88e64a4 from master
2014-08-26 09:45:06 -04:00
Tim Graham 27c682ffa0 [1.4.x] Bumped version number post-release. 2014-08-20 16:36:42 -04:00
Tim Graham e484df76b6 [1.4.x] Added dates to release notes. 2014-08-20 16:33:50 -04:00
James Bennett 4fce0193d2 [1.4.x] Bump version numbers for security release. 2014-08-20 15:00:40 -05:00
Simon Charette 027bd34864 [1.4.x] Prevented data leakage in contrib.admin via query string manipulation.
This is a security fix. Disclosure following shortly.
2014-08-11 16:01:41 -04:00
Preston Holmes c9e3b9949c [1.4.x] Fixed #23066 -- Modified RemoteUserMiddleware to logout on REMOTE_USE change.
This is a security fix. Disclosure following shortly.
2014-08-11 12:15:06 -04:00
Tim Graham 30042d475b [1.4.x] Fixed #23157 -- Removed O(n) algorithm when uploading duplicate file names.
This is a security fix. Disclosure following shortly.
2014-08-11 10:14:06 -04:00
Florian Apolloner c2fe73133b [1.4.x] Prevented reverse() from generating URLs pointing to other hosts.
This is a security fix. Disclosure following shortly.
2014-08-11 09:04:23 -04:00
Tim Graham 4d5e972a2c [1.4.x] Added release note stub for 1.4.14. 2014-08-11 08:47:06 -04:00
Tim Graham 88cb7aa6aa [1.4.x] Added a warning that remove_tags() output shouldn't be considered safe.
Backport of 7efce77de2 from master
2014-08-11 07:11:30 -04:00
Tim Graham 399052d224 [1.4.x] Noted that django-jython requires Django 1.7.
Backport of 72e98d5c16 from stable/1.6.x
2014-08-08 12:47:31 -04:00
Tim Graham d23d19c15e [1.4.x] Fixed #23239 -- Clarified a phrase in the contrib.markup docs.
Backport of e0fb48c254 from stable/1.5.x
2014-08-06 08:30:49 -04:00
Erik Romijn bc03817b42 [1.4.x] Fixed #23149 -- Clarified note on HTTPOnly in cookie-based session docs
Backport of e26366da44 from master.
2014-08-02 19:01:23 +02:00
Tim Graham 778a555342 [1.4.x] Added tests/requirements/py2.txt.
This follows the convention used in other branches so we don't
need a special case in the build script for 1.4.
2014-07-25 09:46:15 -04:00
Ramiro Morales aa9c45c2e4 [1.4.x] Revert "Fixed #13794 -- Fixed to_field usage in BaseInlineFormSet."
This reverts commit b44519072e.

stable/1.4.x branch is in security-fixes-only mode.
2014-07-14 21:09:38 -03:00
Tim Graham b44519072e [1.4.x] Fixed #13794 -- Fixed to_field usage in BaseInlineFormSet.
Thanks sebastien at clarisys.fr for the report and gautier
for the patch.

Backport of 5e2c4a4bd1 from master
2014-07-14 12:38:00 -03:00
Tim Graham d29f3b9e87 [1.4.x] Fixed #22859 -- Improved crossDomain technique in CSRF example.
Thanks flisky for the report.

Backport of 0be4d64487 from master
2014-06-18 14:38:30 -04:00
Tim Graham d39fcff11a [1.4.x] Minor edits to latest release notes.
Backport of 860d31ac7a from master
2014-05-15 07:17:54 -04:00
Jacob Kaplan-Moss 37d6821d35 Bumped version numbers post-release. 2014-05-14 18:24:08 +02:00
Jacob Kaplan-Moss 53b98b5a7c Bumped version numbers for release. 2014-05-14 18:09:51 +02:00
Jacob Kaplan-Moss fe5b3e36a2 Added release notes for 1.4.13. 2014-05-14 18:07:32 +02:00
Tim Graham 7feb54bbae [1.4.x] Added additional checks in is_safe_url to account for flexible parsing.
This is a security fix. Disclosure following shortly.
2014-05-12 09:46:40 -04:00
Aymeric Augustin 28e23306aa [1.4.x] Dropped fix_IE_for_vary/attach.
This is a security fix. Disclosure following shortly.
2014-05-12 09:46:22 -04:00
Tim Graham e1812617cf [1.4.x] Added dates to release notes of today's release.
Backport of 68d264059a from master
2014-04-28 19:07:51 -04:00
Tim Graham 48a4729cd7 [1.4.x] Post release version bump. 2014-04-28 19:03:36 -04:00
James Bennett b1b680c8fe [1.4.x] Bump version numbers for 1.4.12 bugfix release. 2014-04-28 15:28:15 -05:00
Tim Graham b91c385e32 [1.4.x] Fixed #22486 -- Restored the ability to reverse views created using functools.partial.
Regression in 8b93b31.

Thanks rcoup for the report.

Backport of 3c06b2f2a3 from master
2014-04-23 09:22:02 -04:00
Tim Graham 1edb163592 [1.4.x] Post release version bump. 2014-04-22 11:50:20 -04:00
James Bennett 194159ba44 [1.4.x] Bump version numbers for 1.4.11 security release. 2014-04-21 17:38:26 -05:00
Erik Romijn 8010908313 [1.4.x] Added information on resolved security issues to release notes.
Backport of c07f3e60c2 from master
2014-04-21 18:31:44 -04:00
Erik Romijn aa80f498de [1.4.x] Fixed queries that may return unexpected results on MySQL due to typecasting.
This is a security fix. Disclosure will follow shortly.

Backport of 75c0d4ea3a from master
2014-04-21 18:31:44 -04:00
Aymeric Augustin 1170f285dd [1.4.x] Prevented leaking the CSRF token through caching.
This is a security fix. Disclosure will follow shortly.

Backport of c083e3815a from master
2014-04-21 18:31:44 -04:00
Tim Graham c1a8c420fe [1.4.x] Fixed a remote code execution vulnerabilty in URL reversing.
Thanks Benjamin Bach for the report and initial patch.

This is a security fix; disclosure to follow shortly.

Backport of 8b93b31487 from master
2014-04-21 18:31:44 -04:00
Matt Lauber ca3927dfb9 [1.4.x] Corrected the section identifier for MySQL unicode reference.
Backport of b2514c02e1 from master
2014-04-21 13:21:14 -04:00
Tim Graham 83420e70ef [1.4.x] Fixed random aggregation_regress test_more_more_more() failure
The cause was assuming that an unordered queryset returns the values
always in the same order.

Backport of 33dd8f5442
2014-04-19 13:01:52 -04:00
Tim Graham f2a9f71565 [1.4.x] Updated six to 1.6.1.
Backport of 2ec82c7387 from master
2014-03-24 07:35:13 -04:00
Claude Paroz f108b1f7d7 [1.4.x] Clarified striptags documentation
The fact that striptags cannot guarantee to really strip all
non-safe HTML content was not clear enough. Also see:
https://www.djangoproject.com/weblog/2014/mar/22/strip-tags-advisory/

Partial backport (doc-only) of 6ca6c36f82 from master.
2014-03-22 11:19:58 +01:00
Tim Graham b8713ee69a [1.4.x] Fixed #21195 -- Clarifed usage of template_name in tutorial part 4.
Backport of b66a51ad54 from master.
2014-03-05 22:35:37 +01:00
Tim Graham 74181c0a2c [1.4.x] Added release note stub for 1.4.11.
Backport of dfa28981ce from master.
2014-01-26 17:50:12 -05:00
Tim Graham 257f8528b7 [1.4.x] Fixed #21823 -- Upgraded six to 1.5.2
Backport of 780ae7e9f8 from master.
2014-01-26 15:52:39 -05:00
Tim Graham 85057522bc [1.4.x] Fixed #21869 -- Fixed docs building with Sphinx 1.2.1.
Thanks tragiclifestories for the report.

Backport of e1d18b9d2e from master
2014-01-24 09:05:59 -05:00
Jacob Kaplan-Moss 03d9b9ea0a [1.4.x] Added a note about LTS releases.
Backport of a44cbca2a5 from master.
2014-01-19 18:46:57 +01:00
Tim Graham 1036e3ec7c [1.4.x] Fixed #20052 -- Discouraged use of Jython given the current state of django-jython.
Thanks Josh Juneau (maintainer of django-jython) for the review.

Backport of a67e327db5 from master
2014-01-17 09:29:57 -05:00
Luke Plant 2c1d92bc64 Updated six to version 1.4.1
This is not a bugfix. But six only exists on Django 1.4.x branch to help
with future compatibility, so it is helpful if it keeps up with latest
Django.
2014-01-02 12:39:00 +00:00
Ben Spaulding 474e7dd6d0 [1.4.x] Fixed #21594 -- Added note about model formsets deleting objects.
This behavior has been fixed in 65e03a424e. refs #10284.

Backport of de1d5d5df5 from stable/1.6.x.
2013-12-11 14:49:22 -05:00
Aymeric Augustin 2d4f399ad4 [1.4.x] Fixed #21558 -- Support building CHM files.
Thanks Michał Pasternak.

Backport of cd9e85ec from master.
2013-12-04 16:52:30 +01:00
Alasdair Nicol 23126866ec [1.4.x] Fixed #21538 -- Added numpy to test/requirements/base.txt
Thanks Tim Graham for the report

Backport of c75dd664c from master
2013-12-02 13:45:56 -05:00
Aymeric Augustin 8e8584f959 [1.4.x] Removed obsolete deprecation notes. 2013-11-23 14:47:09 +01:00
Baptiste Mispelon 46755c50ee [1.4.x] Fix #20054: Removed links to modwsgi.org.
Backport of 957fcd0c9f from master.
2013-11-22 09:16:50 +01:00
Tim Graham c5d071f85a [1.4.x] Added 1.4.10 release notes to index. 2013-11-07 09:38:53 -05:00
James Bennett 30eb916bdb [1.4.x] Bump version info and add release notes for 1.4.10. 2013-11-06 08:17:26 -06:00
Florian Apolloner 848a759474 Fixed #21362 -- Restored Python 2.5 compatibility. 2013-11-02 18:18:18 +01:00
Aymeric Augustin b149d1fcd6 Merge pull request #1837 from loic/django14
Fixed SyntaxError on Python 2.5 caused by a @unittest.skipIf class decoration.
2013-11-01 01:21:34 -07:00
Loic Bistuer 7984b58e78 Fixed SyntaxError on Python 2.5 caused by a @unittest.skipIf class decoration. 2013-11-01 03:35:29 +07:00
Paolo Melchiorre d491702ed7 [1.4.x] Fixed typo in docs/releases/1.4.9.txt.
Backport of 3b0293370a from master
2013-10-25 07:55:50 -04:00
Tim Graham 11b750b031 [1.4.x] Bump version post-release. 2013-10-25 07:54:10 -04:00
James Bennett 8f36d1fd95 [1.4.x] Bump everything for 1.4.9 bugfix release. 2013-10-24 23:37:26 -05:00
Tim Graham 3a46f621fe [1.4.x] Bumped release date for 1.5.5 & 1.4.9.
Backport of 4ce5c119b5 from master
2013-10-23 18:29:53 -04:00
Shai Berger 6de3726423 Fixed #13245: Explained Oracle's behavior w.r.t db_table
and how to prevent table-name truncation

Thanks russellm & timo for discussion, and timo for review.

Backported from master 317040a73b
2013-10-23 15:05:05 +03:00
Tim Graham ead7c496a4 [1.4.x] Added 1.4.9 release notes
Backport of 2eb8f15516 from master
2013-10-22 20:25:20 -04:00
Florian Apolloner c4f29c91f9 [1.4.x] Fixed #21253 -- PBKDF2 with cached HMAC key
This gives a 2x speed increase compared to the existing implementation.

Thanks to Steve Thomas for the initial patch and Tim Graham for finishing it.

Backport of 1e4f53a6eb from master.
2013-10-21 20:03:12 +02:00
Aymeric Augustin ea04c81d37 [1.4.x] Fixed #21256 -- Error in datetime_safe.datetime.combine.
Backport of d9b6fb8 from master
2013-10-13 19:11:59 +02:00
Anssi Kääriäinen 037ec1054c [1.4.x] Fixed #21248 -- Skipped test_bcrypt if no py-bcrypt found
Pre 1.6 Django worked only with py-bcrypt, not with bcrypt. Skipped
test_bcrypt when using bcrypt to avoid false positives.

Backpatch of 9f8a36eb20 from stable/1.5.x.
2013-10-09 14:44:43 +03:00
Florian Apolloner e2403db95a [1.4.x] Fixed #21138 -- Increased the performance of our PBKDF2 implementation.
Thanks go to Michael Gebetsroither for pointing out this issue and help on
the patch.

Backport of 68540fe4df from master.
2013-09-24 21:20:19 +02:00
Florian Apolloner 0317edf0c7 Revert "[1.4.x] Ensure that passwords are never long enough for a DoS."
This reverts commit 3f3d887a68.

This fix is no longer necessary, our pbkdf2 (see next commit) implementation
no longer rehashes the password every iteration.
2013-09-24 21:19:20 +02:00
Tim Graham ca77e38d24 [1.4.x] Cleaned up 1.4.8 release notes
Backport of 8d29005524 from master
2013-09-15 14:29:40 -04:00
Tim Graham efee30e6b0 [1.4.x] Bump version post-release. 2013-09-15 12:59:10 -04:00
Claude Paroz 629813a804 [1.4.x] Fixed geos test to prevent random failure
Points in the test fixtures have 20 as max coordinate.

Backport of 87854b0bdf from master.
2013-09-15 11:45:16 +02:00
Russell Keith-Magee 6903d1690a [1.4.x] Removed usage of b"" string syntax for Python 2.5 compatibility.
Refs commit 3f3d887a68.
2013-09-15 14:02:38 +08:00
James Bennett 3ffc7b52f8 [1.4.x] Add release notes and bump version numbers for 1.4.8 security release. 2013-09-14 23:53:07 -06:00
Russell Keith-Magee 3f3d887a68 [1.4.x] Ensure that passwords are never long enough for a DoS.
* Limit the password length to 4096 bytes
  * Password hashers will raise a ValueError
  * django.contrib.auth forms will fail validation
 * Document in release notes that this is a backwards incompatible change

Thanks to Josh Wright for the report, and Donald Stufft for the patch.

This is a security fix; disclosure to follow shortly.

Backport of aae5a96d57 from master.
2013-09-15 13:49:16 +08:00
Tim Graham 75d2bcda10 Fixed #18923 -- Corrected usage of sensitive_post_parameters in contrib.auth
Thanks Collin Anderson for the report.

Backport of 425d076d0c from master
2013-09-13 10:18:55 -04:00
Tim Graham cca302cde6 [1.4.x] Fixed #20887 -- Added a warning to GzipMiddleware in light of BREACH.
Thanks EvilDMP for the report and Russell Keith-Magee
for the draft text.

Backport of da843e7dba from master
2013-09-11 08:18:56 -04:00
Florian Apolloner 434d122a74 Merge pull request #1616 from loic/fix1.4
Fixed failing test introduced by 87d2750b39.
2013-09-11 04:30:45 -07:00
Tim Graham fba6af5a1e [1.4.x] Bump version post-release. 2013-09-11 07:06:09 -04:00
Loic Bistuer 3203f684e8 Fixed failing test introduced by 87d2750b39.
The {% ssi %} tag in Django 1.4 doesn't support spaces in its argument.
Skip the test if run from a location that contains a space.
2013-09-11 18:05:39 +07:00
James Bennett 701c1a11bc [1.4.x] Bump version numbers for 1.4.7 security release. 2013-09-10 20:15:38 -05:00
Tim Graham d1dc8a0d00 Added 1.4.7 release notes
Backport of baec6a26dd from master
2013-09-10 21:09:47 -04:00
Tim Graham 87d2750b39 [1.4.x] Prevented arbitrary file inclusion with {% ssi %} tag and relative paths.
Thanks Rainer Koirikivi for the report and draft patch.

This is a security fix; disclosure to follow shortly.

Backport of 7fe5b656c9 from master
2013-09-10 21:05:47 -04:00
Садовский Николай 9ab7ed9b72 [1.4.x] Fixed #20707 -- Added explicit quota assignment to Oracle test user
To enable testing on Oracle 12c
2013-09-09 15:13:18 +03:00
Shai Berger 7826824aef [1.4.x] Fixed #20907 - Test failure on Oracle
Backport of the Oracle-specific part of commit a18e43c5bb
from master. This commit made get_indexes more consistent across
backends.

Thanks Tim Graham for pointer to the commit, akaariai and ikelly
for the original commit.
2013-08-18 01:45:01 +03:00
Shai Berger d9dc98159d [1.4.x] Fixed #20904: Test failure on Oracle
Just skip the failing test, the failure isn't really relevant; also,
both the test and the reason for its failure were removed in 1.5.

Thanks Tim Graham for advice on 1.5.
2013-08-17 23:12:01 +03:00
Luke Plant d5da495a2e [1.4.x] Fixed #20906 -- Fixed a dependence on set-ordering in tests
Backport of 1ae64e96c1 from master
2013-08-16 17:55:08 -04:00
Anssi Kääriäinen bf611f14ec [1.4.x] Fixed #20905 -- Fixed an Oracle-specific test case failure
Made a test checking ORM-generated query string case-insensitive.

Backport of ee0a7c741e from master
2013-08-16 12:23:05 -04:00
Florian Apolloner 08e5fcb3e6 Fixed regression in validation tests since example.com is available via https now. 2013-08-13 22:34:52 +02:00
Jacob Kaplan-Moss 0d4ef66f7c Bump version post-release. 2013-08-13 12:16:41 -05:00
Tim Graham d77ce64fe8 [1.4.x] Removed 1.5.2 release notes 2013-08-13 13:15:54 -04:00
Jacob Kaplan-Moss 506913cdd8 Stole the Makefile for building packages from master. 2013-08-13 11:24:46 -05:00
Tim Graham e61e20e497 Added 1.4.6/1.5.2 release notes. 2013-08-13 11:18:07 -05:00
Jacob Kaplan-Moss 30e17be1f6 Bumped version numbers for 1.4.6. 2013-08-13 11:09:05 -05:00
Jacob Kaplan-Moss ec67af0bd6 Fixed is_safe_url() to reject URLs that use a scheme other than HTTP/S.
This is a security fix; disclosure to follow shortly.
2013-08-13 11:00:13 -05:00
Tim Graham b50be6857c [1.4.x] Added missing release notes for older versions of Django
Backport of 3f6cc33cff from master
2013-08-12 14:11:10 -04:00
Tim Graham 8af0b1afd2 [1.4.x] Added a bugfix in docutils 0.11 -- docs will now build properly.
Backport of a3a59a3197 from master
2013-07-31 10:14:38 -04:00
SusanTan ed6ec47ff7 [1.4.x] Fixed #20779 -- Documented AdminSite.app_index_template; refs #8498.
Thanks CollinAnderson for the report.

Backport of 7de35a9ef3 from master
2013-07-31 07:09:52 -04:00
mark hellewell f3a961f009 [1.4.x] Fixed #18315 -- Documented QueryDict.popitem and QueryDict.pop
Thanks gcbirzan for the report.

Backport of 8c9240222f from master
2013-07-25 11:09:25 -04:00
Brenton Cleeland eda39fe704 [1.4.x] Fixed #20792 -- Corrected DISALLOWED_USER_AGENTS docs.
Thanks simonb for the report.

Backport of dab52d99fc from master
2013-07-25 07:39:53 -04:00
Matt Deacalion Stevens dfe36f10df [1.4.x] Atom specification URL updated
Changed to the URL of the official RFC for Atom, since Atomenabled.org
is just a holding page.

Backport of beefc97171 from master
2013-07-18 08:48:11 -04:00
Tim Graham 6b4b18e7e2 [1.4.x] Fixed #20756 -- Typo in uWSGI docs.
Backport of a3242dc9fe from master
2013-07-17 06:51:48 -04:00
Tim Graham 288d70fccc [1.4.x] Fixed #20730 -- Fixed "Programmatically creating permissions" error.
Thanks glarrain for the report.

Backport of 684a606a4e from master
2013-07-11 11:10:26 -04:00
Tim Graham e8971345b4 [1.4.x] Fixed #19196 -- Added test/requirements
Backport of 4d92a0bd86 from master
2013-07-10 12:12:15 -04:00
Tim Graham 7b7592cafa [1.4.x] Fixed #18944 -- Documented PasswordResetForm's from_email argument as a backwards incompatible change for 1.3
Thanks DrMeers for the report.

Backport of dab921751d from master
2013-07-08 15:06:45 -04:00
Baptiste Mispelon 165cc1dc2f [1.4.x] Fixed #20665 -- Missing backslash in sitemaps documentation
Backport of 5005303ae7 from master.
2013-06-27 09:45:58 +02:00
Aymeric Augustin e2b86571bf [1.4.x] Fixed oversight in e3b6fed3. Refs #20636. 2013-06-24 21:00:28 +02:00
Aymeric Augustin e3b6fed320 [1.4.x] Fixed #20636 -- Stopped stuffing values in the settings.
In Django < 1.6, override_settings restores the settings module that was
active when the override_settings call was executed, not when it was
run. This can make a difference when override_settings is applied to a
class, since it's executed when the module is imported, not when the
test case is run.

In addition, if the settings module for tests is stored alongside the
tests themselves, importing the settings module can trigger an import
of the tests. Since the settings module isn't fully imported yet,
class-level override_settings statements may store a reference to an
incorrect settings module. Eventually this will result in a crash during
test teardown because the settings module restored by override_settings
won't the one that was active during test setup.

While Django should prevent this situation in the future by failing
loudly in such dubious import sequences, that change won't be backported
to 1.5 and 1.4. However, these versions received the "allowed hosts"
patch and they're prone to "AttributeError: 'Settings' object has no
attribute '_original_allowed_hosts'". To mitigate this regression, this
commits stuffs _original_allowed_hosts on a random module instead of the
settings module.

This problem shouldn't occur in Django 1.6, see #20290, but this patch
will be forward-ported for extra safety.

Also tweaked backup variable names for consistency.

Backport of 0261922 from stable/1.5.x.

Conflicts:
	django/test/utils.py
2013-06-24 20:42:42 +02:00
Tim Graham c97cc85b74 [1.4.x] Fixed #20326 - Corrected form wizard get_form() example.
Thanks tris@ for the report.

Backport of 646a2216e9 from master
2013-05-31 08:09:17 -04:00
Gavin Wahl 9b5fe02215 [1.4.x] Fixed regroup example.
Chicago was missing.

Backport of e6ff238 from master.
2013-05-29 21:52:25 -04:00
Tim Graham 227d7f63e4 [1.4.x] Fixed #20523 - Incorrect form field for FilePathField.
Thanks sane4ka.sh@ for the report.

Backport of 1fdc3d256d from master
2013-05-28 12:00:04 -04:00
Tim Graham 1deeda5785 [1.5.x] Fixed #20492 - Removed a broken link in GIS docs.
Backport of fbab3209fc from master
2013-05-24 12:36:25 -04:00
Alasdair Nicol e149d8ebf0 [1.4.x] Updated link to jQuery Cookie plugin site
Backport of 81f454a322 from master
2013-05-24 12:26:23 -04:00
Wilfred Hughes 528345069d [1.4.x] Fixed a minor spelling mistake in the queryset documentation
Backport of d258cce482 from master
2013-05-14 10:32:38 -04:00
Alex Gaynor 6297673efd [1.5.X] Fixed #18883 -- added a missing self parameter in the docs
Backport of 17d57275f9 from master
2013-05-13 20:50:37 -04:00
Tim Graham fbac080691 [1.4.X] Fixed #18277 - Clarified startproject documentation.
Backport of 33503600b5 from master
2013-03-30 08:38:42 -04:00
Nimesh Ghelani d2b8834839 [1.4.x] Fixed #20150 -- Fixed an error in manager doc example
Backport of 485c024567 from master
2013-03-29 15:55:52 -04:00
Carl Meyer 4c6fb23dd4 [1.4.x] Bump version to no longer claim to be 1.4.5 final. 2013-03-28 15:11:17 -06:00
Donald Stufft 41af26dd53 Merge pull request #962 from dstufft/document-bcrypt-truncation-1.4.x
Document password truncation with BCryptPasswordHasher
2013-03-26 10:32:19 -07:00
Donald Stufft 843034a8d6 Document password truncation with BCryptPasswordHasher 2013-03-26 13:28:55 -04:00
Claude Paroz 577a27a9fc [1.4.x] Fixed #19926 -- Fixed a link to code example in queries docs
Thanks Randy Salvo for the report.
2013-03-02 20:13:47 +01:00
Aymeric Augustin 97a67b26f3 [1.4.x] Fixed #18144 -- Restored compatibility with SHA1 hashes with empty salt.
Thanks dahool for the report and initial version of the patch.

Backport of 633d8de from master.
2013-02-25 20:18:04 +01:00
Tim Graham 52bac4ede1 [1.4.x] Fixed #19911 - Updated generic view links.
Thanks marc@ for the report.
2013-02-25 13:01:15 -05:00
Tim Graham db1e8bdc33 [1.4.x] Fixed #19728 - Updated API stability doc to reflect current meaning of "stable".
Backport of 132d5822b0 from master.
2013-02-25 12:55:12 -05:00
Preston Holmes 0f555f813b [1.4.x] Fixed #19902 -- backport of as_view docs 2013-02-23 19:25:38 -08:00
Anssi Kääriäinen 3872bc51c9 [1.4.x] Made a couple of selenium tests wait for page loaded
The admin_widgets tests were issuing click() to the browser but
didn't wait for the effects of those clicks. This caused the resulting
request to be processed concurrently with the test case. When using
in-memory SQLite this caused weird failures.

Also added wait_page_loaded() to admin selenium tests for code
reuse.

Fixed #19856, cherry-pick of 50677b29af
2013-02-21 00:03:39 +02:00
James Bennett 67a937c2c2 [1.4.x] Bump version numbers to roll a clean package. 2013-02-20 13:53:27 -06:00
Carl Meyer 3adfc3f97d [1.4.x] Note that ALLOWED_HOSTS default changes in Django 1.5. 2013-02-20 12:26:54 -07:00
Carl Meyer 4cdfb24c98 [1.4.x] Fixed #19857 -- Fixed broken docs link in project template. 2013-02-19 18:36:44 -07:00
Carl Meyer 5d1791ffd2 [1.4.x] Don't characterize XML vulnerabilities as DoS-only. 2013-02-19 18:22:22 -07:00
James Bennett f61f800c29 [1.4.x] Bump version numbers for security release. 2013-02-19 14:17:23 -06:00
Carl Meyer 62d5338bf2 [1.4.x] Update 1.4.4 release notes for all security fixes. 2013-02-19 11:48:46 -07:00
Aymeric Augustin 0cc350a896 [1.4.x] Added a default limit to the maximum number of forms in a formset.
This is a security fix. Disclosure and advisory coming shortly.
2013-02-19 10:37:54 -07:00
Carl Meyer 0e7861aec7 [1.4.x] Checked object permissions on admin history view.
This is a security fix. Disclosure and advisory coming shortly.

Patch by Russell Keith-Magee.
2013-02-19 10:37:54 -07:00
Carl Meyer 1c60d07ba2 [1.4.x] Restrict the XML deserializer to prevent network and entity-expansion DoS attacks.
This is a security fix. Disclosure and advisory coming shortly.
2013-02-19 10:37:54 -07:00
Carl Meyer 9936fdb11d [1.4.x] Added ALLOWED_HOSTS setting for HTTP host header validation.
This is a security fix; disclosure and advisory coming shortly.
2013-02-19 10:37:54 -07:00
Tim Graham 57b62a74cb [1.4.x] Fixed #19824 - Corrected the class described for Field.primary_key from IntegerField to AutoField.
Thanks Keryn Knight.

Backport of 218bbef0c4 from master
2013-02-16 18:34:34 -05:00
Tim Graham 83e512fa6e [1.4.x] Fixed #19812 - Removed a duplicate phrase in the widget docs.
Thanks diegueus9 for the report and itsallvoodoo for the draft patch.

Backport of 7a80904b00 from master
2013-02-16 18:25:22 -05:00
Alex Hunley 3d6388941d [1.4.x] Fixed #19719 - Removed misleading example from ModelForm documentation
Backport of 976dc07baf from master
2013-02-16 18:09:43 -05:00
Tim Graham 9eb7d59665 [1.4.x] Fixed #19815 - Removed an unused import in tutorial 3.
Thanks pedro.calcao@ for the report.
2013-02-12 20:04:15 -05:00
Anssi Kääriäinen dec7dd99f0 [1.4.x] Removed try-except in django.db.close_connection()
The reason was that the except clause needed to remove a connection
from the django.db.connections dict, but other parts of Django do not
expect this to happen. In addition the except clause was silently
swallowing the exception messages.

Refs #19707, special thanks to Carl Meyer for pointing out that this
approach should be taken.
2013-02-13 00:39:43 +02:00
Claude Paroz b4fb448f83 Fixed WSGIPythonPath instruction in deployment docs
Partial backport of 3abf6105b6 from master. Refs #19042.
2013-02-11 08:42:09 +01:00
Anssi Kääriäinen 209f174e58 [1.4.x] Made custom m2m fields without through easier to use
The change in f105fbe52b made through=None
m2m fields fail in cases where they worked before. It isn't possible to
create such fields using public APIs. The fix is trivial, so it seems
worth fixing this for custom m2m field users.

This is not a backport from master. Master has gotten enough other
changes to related fields internal API that this fix alone isn't enough
to do any good.
2013-02-10 21:57:05 +02:00
Anssi Kääriäinen 9918b3f502 [1.4.x] Fixed #19707 -- Reset transaction state after requests
Backpatch of a4e97cf315.
2013-02-10 17:34:38 +02:00
Anssi Kääriäinen 498a5de07b [1.4.x] Fixed #19645 -- Added tests for TransactionMiddleware
Backpatch of f556df90be. Backpatching
these tests so that it will be easier to backpatch the fix for #19707.
2013-02-10 17:34:27 +02:00
Tim Graham 056b2b5f65 [1.4.x] Fixed #19756 - Corrected a ManyToMany example and added some links and markup.
Backport of 43efefae69 from master
2013-02-07 07:04:52 -05:00
Claude Paroz ec93ecdd10 [1.4.x] Fixed #19702 -- Changed a SQL command syntax to be MySQL 4-compatible
Thanks matf at op.pl for the report.
2013-02-02 14:24:35 +01:00
Claude Paroz 3610d11ba0 [1.5.x] Lowered field ordering requirement in ogrinspect test
This test was randomly failing depending on the library environment.
Backport of a1c470a6f from master.
2013-02-02 14:08:59 +01:00
Claude Paroz 6bd3896fcb [1.4.x] Fixed #18144 -- Added backwards compatibility with old unsalted MD5 passwords
Thanks apreobrazhensky at gmail.com for the report.
Backport of 63d6a50dd from master.
2013-02-02 12:10:38 +01:00
Tim Graham 89ba1b27b4 [1.4.x] Fixed #19555 - Removed '2012' from tutorial 1.
Thanks rodrigorosa.lg and others for the report.

Backport of 99315f709e from master
2013-01-17 16:41:05 -05:00
Tim Graham c26541f5cb [1.4.x] Addeded CSS to bold deprecation notices.
Thanks Sam Lai for mentioning this on the mailing list.

Backport of 227bd3f8db from master
2013-01-09 19:05:20 -05:00
Tim Graham c4a9e5bd8d [1.4.X] Fixed #19506 - Remove 'mysite' prefix in model example.
Thanks Mike O'Connor for the report.

Backport of 52a2588df6 from master
2012-12-21 15:53:44 -05:00
Ramiro Morales 6474105107 [1.4.x] Added PASSWORD_HASHERS to settings reference document.
abd0f304b1 from master.
2012-12-19 15:13:06 -03:00
Alex Gaynor 8ab2aceb65 [1.4.X] Fixed #18099 -- corrected a typo in the initial data docs. Thanks to Bradley Ayers for the patch.
Backport of f5a9e5e9 from master
2012-12-15 16:42:19 -05:00
Florian Apolloner f2530dcb17 [1.4.X] Fixed a test failure in the comment tests.
Backport of 1eb0da1c5b from master.
2012-12-10 23:37:12 +01:00
James Bennett 1f0af3c529 [1.4.x] Bump version numbers for security release. 2012-12-10 15:45:04 -06:00
Florian Apolloner 319627c184 [1.4.X] Fixed a security issue in get_host.
Full disclosure and new release forthcoming.
2012-12-10 22:14:16 +01:00
Florian Apolloner b2ae0a63ae [1.4.X] Fixed #18856 -- Ensured that redirects can't be poisoned by malicious users. 2012-12-10 22:14:16 +01:00
Julien Phalip 8c9a8fd5c4 [1.4.x] Fixed the admin_filters tests for Postgres.
Backport of c196e01100
2012-12-04 10:41:22 -08:00
Sebastián Magrí c72172244e [1.4.x] Fixed #19318 -- Ensured that the admin's SimpleListFilter options can be displayed as selected even if the lookup's first element is not a string.
Backport of 88e1715639
2012-12-03 20:58:54 -08:00
Anssi Kääriäinen 3e4058be9f [1.4.x] Fixed ordering-related failure in m2m_through_regress tests
Backpatch of dc569c8801
2012-11-24 16:10:16 +02:00
Aymeric Augustin 046300c43b [1.4.x] Restored Python 2.5 compatibility in m2m_through_regress tests.
Refs #18823.
2012-11-24 09:49:30 +01:00
Anssi Kääriäinen c7dcb1d808 [1.4.x] Fixed SQLite's collapsing of same-valued instances in bulk_create
SQLite used INSERT INTO tbl SELECT %s UNION SELECT %s, the problem
was that there should have been UNION ALL instead of UNION.

Refs #19351

Backpatch of a27582484c
2012-11-24 01:28:25 +02:00
Tim Graham 9ee9a7265a [1.4.X] Fixed #19317 - Added an image for warning blocks in the docs
Thanks tome for the suggestion and patch.

Backport of 3587991ba8 from master
2012-11-22 08:50:50 -05:00
Preston Holmes 19710955e4 [1.4.x] Added examples of using startproject/app with URLs
thanks to Brent O'Connor for the idea and intial docs
2012-11-21 22:08:31 -08:00
Luke Plant 9003d6fece [1.4.x] Corrected docs about default value of MESSAGE_STORAGE
Backport of a32f30c79c from master
2012-11-20 23:18:26 +00:00
Tim Graham 06c14a63a2 [1.4.X] Fixed #13997 - Added an example of constructing a MultiWidget and documented the value_from_datadict method.
Backport of 04775b4598 from master
2012-11-17 17:55:28 -05:00
Anssi Kääriäinen 25e041f270 [1.4.x] Fixed #19058 -- Fixed Oracle GIS crash
The problem is the same as in #10888 which was reintroduced when
bulk_insert was added. Thanks to Jani Tiainen for report, patch and
also testing the final patch on Oracle GIS.

Backpatch of 92d7f541da
2012-11-15 16:22:28 +02:00
Nicolas Ippolito fdb855e7b2 [1.4.X] Typo in comments doc
Backport of 17b14d4819 from master
2012-11-13 05:25:55 -05:00
Aymeric Augustin f8c005b4ec Fixed #19225 -- Typo in shortcuts docs.
Thanks SunPowered for the report.
2012-11-02 09:30:42 +01:00
Aymeric Augustin 2733253633 [1.4.x] Fixed #19208 -- Docs for mod_wsgi daemon mode
Thanks Graham Dumpleton for the patch.

Backport of bc00075 from master.
2012-10-29 21:40:32 +01:00
Claude Paroz ad2d57a2cc [1.4.x] Fixed #19172 -- Isolated poisoned_http_host tests from 500 handlers
Thanks bernardofontes for the report.

Backport of b774c5993 from master.
2012-10-29 17:30:51 +01:00
Anssi Kääriäinen 37c87b785d [1.4.x] Fixed #18823 -- Ensured m2m.clear() works when using through+to_field
There was a potential data-loss issue involved -- when clearing
instance's m2m assignments it was possible some other instance's
m2m data was deleted instead.

This commit also improved None handling for to_field cases.

Backpatch of 611c4d6f1c
2012-10-28 17:38:26 +02:00
Tim Graham baf1f1dcde [1.4.X] Fixed #9471 - Expanded ModelAdmin.raw_id_fields docs; thanks adroffne for the suggestion.
Backport of da958eb209 from master
2012-10-24 17:50:37 -04:00
Carl Meyer ce168bb899 [1.4.x] Fix an HTML-parser test that's failed in Python 2.6.8 since 5c79dd58.
The problem description in #18239 asserted that
http://bugs.python.org/issue670664 was fixed in Python 2.6.8, but based on
http://bugs.python.org/issue670664#msg146770 it appears that's not correct; the
fix was only applied in 2.7, 3.2, and Python trunk. Therefore we must use our
patched HTMLParser subclass in all Python 2.6 versions.

Backport of fcec904e4f from master. Fixes #19148.
2012-10-23 18:57:59 +02:00
Preston Holmes e86e4ce0bd Added 1.4.2 release notes 2012-10-21 07:50:30 -07:00
Tim Graham 6c1c490f64 [1.4.X] Fixed #13869 - Warned that QuerySet.iterator() doesn't affect DB driver caching; thanks jtiai for the suggestion.
Backport of 2f722d9728 from master
2012-10-20 15:23:06 -04:00
Tim Graham 13bbe9161d [1.4.x] Fixed arguments for get_inline_instances; refs #17006. 2012-10-20 09:57:51 -04:00
Tim Graham e7685b87c1 [1.4.X] Fixed #17006 - Documented ModelAdmin get_form() and get_formsets()
Backport of eed4faf16f from master
2012-10-20 08:42:24 -04:00
Tim Graham 700717db1f [1.4.X] Fixed #17388 - Noted in the custom model field docs that field methods need to handle None if the field may be null.
Backport of 4cef9a09f9 from master
2012-10-19 17:54:38 -04:00
Tim Graham fd90a90633 [1.4.X] Fixed #18046 - Documented that an index is created by default for ForeignKeys; thanks jbauer for the suggestion.
Backport of db598dd8a0 from master
2012-10-18 16:59:23 -04:00
Preston Holmes 773a29295a Added missed poisoned host header test changes 2012-10-18 11:18:25 -07:00
James Bennett 8c46ead92b [1.4.x] Bump ALL the version numbers. 2012-10-17 17:17:37 -05:00
James Bennett 0f54fed0b6 [1.4.x] Bump version numbers for security release. 2012-10-17 17:15:49 -05:00
Preston Holmes 58806ce153 Fixed an error in the set cookie documentation 2012-10-17 14:57:58 -07:00
Preston Holmes 92d3430f12 Fixed a security issue related to password resets
Full disclosure and new release are forthcoming

backport from master
2012-10-17 14:40:41 -07:00
Tim Graham 73991b0b32 [1.4.X] Fixed #18473 - Fixed a suggestion that GZipMiddleware needs to be first in the list of middleware.
Backport of 3e0857041b from master
2012-10-17 15:48:47 -04:00
Tim Graham 33d11463a0 [1.4.x] Fixed a couple links that didn't backport cleanly 2012-10-16 20:53:53 -04:00
Tim Graham 6ebb6f9188 [1.4.X] Fixed #18548 - Clarified note regarding reusing model instances when form validation fails.
Backport of fd02bcff4a from master
2012-10-16 20:47:07 -04:00
Tim Graham 81020708ea [1.4.X] Fixed #10936 - Tempered recommendation of SQLite - thanks Karen Tracey for the feedback.
Backport of 9190d89829 from master
2012-10-15 19:56:40 -04:00
Julien Phalip cc0478606a [1.4.x] Fixed #18881 -- Made the context option in {% trans %} and {% blocktrans %} accept literals wrapped in single quotes. Thanks to lanyjie for the report. 2012-10-13 10:51:53 -07:00
Claude Paroz 4cdc416d03 [1.4.x] Fixed #19119 -- Corrected default date input formats in docs
Thanks henrik@aisti.fi for the report.
Backport of 10dc4797ea from master.
2012-10-13 11:05:22 +02:00
Tim Graham d2891d1c07 [1.4.x] Fixed #18996 - Clarified overriden model methods not called on bulk operations
Backport of 443999a1ee from master.
2012-10-12 19:46:47 -04:00
Tim Graham e2dea54efe [1.4.X] Fixed #18256 - Added a potential pitfall when upgrading to MySQL 5.5.5
Backport of c870cb48cd from master
2012-10-12 19:17:50 -04:00
Tim Graham 8139a7990a [1.4.X] Fixed #10936 - Noted that using SQLite for development is a good idea
Backport of 470deb5cbb from master
2012-10-11 20:02:40 -04:00
Tim Graham a1d21c0877 [1.4.X] Fixed #16588 - Warned about field names that conflict with the model API
Backport of dd0cbc6bdc from master
2012-10-11 18:05:49 -04:00
Tim Graham cf17d5e267 [1.4.X] Fixed #14165 - Documented that TransactionMiddleware only applies to the default database.
Backport of 938ee7cd36 from master
2012-10-11 17:49:40 -04:00
Tim Graham 3ac70a5907 [1.4.X] Fixed #16817 - Added a guide of code coverage to contributing docs.
Thanks Pedro Lima for the draft patch.

Backport of 06f5da3d78 from master
2012-10-11 06:14:24 -04:00
Tim Graham 1be0515fe9 [1.4.x] Fixed #4501 - Documented how to use coverage.py with Django tests.
Thanks krzysiumed for the draft patch.

Backport of 7ef2781ca0 from master.
2012-10-10 20:06:52 -04:00
Tim Graham c06b724a00 [1.4.X] Fixed #17435 - Clarified that QuerySet.update returns the number of rows matched
Backport of 6d46c740d8 from master
2012-10-06 07:07:29 -04:00
Claude Paroz 0636c9583f Fixed #19072 -- Corrected an external file path in GeoIP docs
Thanks Flavio Curella for the report and the initial patch.
2012-10-05 23:18:17 +02:00
Tim Graham a35d7fd1e1 [1.4.X] Fixed #19051 - Fixed Selenium tearDownClass method; thanks glarrain for the report.
Backport of a1a5c0854f from master
2012-10-04 07:46:53 -04:00
Tim Graham 8868a067e0 [1.4.X] Fixed #19006 - Quoted filenames in Content-Disposition header.
Backport of 234ca6c61d from master
2012-10-03 17:47:48 -04:00
Tim Graham b1462e0a36 [1.4.X] Fixed #18413 - Noted that a model's files are not deleted when the model is deleted. Thanks lawgon for the report.
Backport of 1c03b23567 from master
2012-10-03 17:44:14 -04:00
Tim Graham cf482d6e2a [1.4.X] Fixed #15338 - Documented django.utils.decorators
Backport of d0345b7114 from master
2012-09-30 13:42:38 -04:00
Anssi Kääriäinen 4dba4ed548 [1.4.x] -- Fixed Python 2.5 compatibility issues 2012-09-30 16:36:01 +03:00
Anssi Kääriäinen 1f537335d9 [1.4.x] Fixed #18979 -- Avoid endless loop caused by "val in PermLookupDict"
Fixed by defining __iter__ which raises TypeError. This was done to
PermWrapper earlier.

Backport of 50d573d2c0
2012-09-27 15:54:48 +03:00
Tim Graham bd514f28e4 [1.4.X] Fixed #18057 - Documented that caches are not cleared after each test; thanks guettli for the suggestion.
Backport of 2aaa467a2a from master
2012-09-22 07:09:49 -04:00
Tim Graham 1189dca471 [1.4.X] Fixed #15325 - Added a link to RelatedManager in the ManytoManyField docs; thanks jammon for the suggestion.
Backport of 0fdfcee257 from master
2012-09-19 18:01:37 -04:00
Tim Graham 57cdbf3bf8 [1.4.X] Fixed #14829 - Added references to CBVs in the URLConf docs; thanks Andrew Willey for the suggestion.
Backport of acd74ffa35 from master
2012-09-19 18:01:24 -04:00
Tim Graham 3a64adef61 [1.4.X] Fixed #13586 - Added an example of how to connect a m2m_changed signal handler.
Backport of 1360bd4186 from master
2012-09-19 18:01:06 -04:00
Julien Phalip 336dfc3413 [1.4.X] Fixed #18530 -- Fixed a small regression in the admin filters where wrongly formatted dates passed as url parameters caused an unhandled ValidationError. Thanks to david for the report. 2012-09-15 16:33:56 -07:00
Tim Graham 421ce44e8b [1.4.x] Fixed #18131 - Documented ContentTypeManager.get_for_id; thanks sir_sigurd for the report.
Backport of 93e6733e4c from master.
2012-09-15 08:17:26 -04:00
Tim Graham 18d88a169f [1.4.x] Fixed #16929 - Documented how to extend UserAdmin with UserProfile fields; thanks charettes for the draft example.
Backport of 22242c510f from master.
2012-09-15 07:41:49 -04:00
Tim Graham 81c77d24ef [1.4.X] Added an example of using a form wizard with different templates; thanks Lorin Hochstein for the patch.
Backport of 553583958d from master
2012-09-15 07:18:28 -04:00
Tim Graham 1b5b8b874f [1.4.x] Fixed #11185 - Expanded docs on customizing widgets; thanks fadeev for the draft patch.
Backport of a73838fde3 from master.
2012-09-15 06:46:39 -04:00
Tim Graham 7c6630920e [1.4.x] Fixed some broken links in 1.4 docs 2012-09-08 15:21:13 -04:00
Tim Graham cd5181f84c [1.4.X] Fixed #18478 - Documented how to use a mutable default in a model field.
Backport of 6e2bb344e4 from master
2012-09-08 14:54:07 -04:00
Tim Graham b0e2cb8e47 [1.4.X] Fixed #15566 - Documented that update() doesn't honor DateField.auto_now
Thanks Shabda Raaj for the draft patch.

Backport of dc01e41d23 from master
2012-09-08 06:40:05 -04:00
Tim Graham 7e8483e70b [1.4.X] Fixed #18490 - Updated DateField input formats; thanks dloewenherz for the draft patch.
Backport of fa8fb2b383 from master
2012-09-07 19:50:55 -04:00
Aymeric Augustin 376a18993b [1.4.x] [py3] Added forwards-compatible aliases.
Related documentation will go to the master branch, since there's
no Python 3 documentation in the 1.5 branch.
2012-09-07 11:45:25 -04:00
Tim Graham 53f533f864 [1.4.X] Fixed #18904 - Typo in MySQL link; thanks Johie Anderson for the report.
Backport of e5983af7c1 from master
2012-09-05 17:52:01 -04:00
Anssi Kääriäinen 2326860851 [1.4.x] Fixed #17788 -- Added batch_size argument to qs.bulk_create()
The qs.bulk_create() method did not work with large batches together
with SQLite3. This commit adds a way to split the bulk into smaller
batches. The default batch size is unlimited except for SQLite3 where
the batch size is limited to 999 SQL parameters per batch.

Thanks to everybody who participated in the discussions at Trac.

Backpatch of 29132ebdef from master (with
documentation changes removed).
2012-09-02 19:17:15 +03:00
Tim Graham f0c469c7be [1.4.x] Merge pull request #315 from streeter/auth-docs-cleanup-patch
Clarify some of the password reset docs

Backport of 03ad7d3c20 from master.
2012-09-01 18:06:25 -04:00
Tim Graham c9f1a13f87 [1.4.X] Fixed #18840 - Added a mentioned of the upload_to parameter to the ModelForm example in file uploads.
Backport of c6088bc91d from master
2012-09-01 15:40:28 -04:00
Claude Paroz 92f7af3c36 [1.4.x] Fixed #18212 -- Standardized arguments of GenericIPAddressField
Unlike other model fields, the newly introduced (1.4)
GenericIPAddressField did not accept verbose_name and name as the
first positional arguments. This commit fixes it.
Thanks Dan McGee for the report and the patch.

Backport of 306d34873c from master.
2012-09-01 18:39:51 +02:00
Tim Graham c2f1aa5a3c [1.4.x] Fixed #13608 - Noted that template lookups use literal values.
Backport of 74c025d028 from master.
2012-09-01 09:27:29 -04:00
Tim Graham c088a42670 [1.4.X] Fixed #16936 - Updated javascript for CSRF protection.
Thanks Idan Gazit for the patch.

Backport of e376558ed2 from master
2012-09-01 06:03:56 -04:00
Tim Graham c274a9cbd0 [1.4.X] Fixed #18642 - Added a note suggesting the use of render() rather than render_to_response() if you need RequestContext in your template.
Backport of f99ecaff06 from master
2012-08-31 20:26:02 -04:00
Tim Graham 90fee02fee [1.4.X] Fixed #16309 - Made ModelAdmin.list_filter versionchanged annotations more specific to the sections that changed; thanks akaihola for the suggestion and patch.
Backport of 70896386ec from master
2012-08-31 18:01:54 -04:00
Claude Paroz f6159d426b [1.4.x] Added 1.4.1 and 1.3.2 release notes
Backport of 44f541ab47 from master.
2012-08-31 20:38:12 +02:00
Tim Graham df0c1055cd [1.4.X] Fixed #14556 - Documented how to use flatpages in URLconf; thanks Claude Paroz for the draft patch.
Backport of 7235cc7685 from master
2012-08-31 08:16:36 -04:00
Claude Paroz b8340d26e4 [1.4.x] Updated obsolete links in the documentation
Backport of 8a5d1a6b93 from master.
2012-08-30 07:47:56 -04:00
Tim Graham fa8a09fdc5 [1.4.X] Fixed #18647 - Removed link to Malcolm's blog which is gone.
Backport of 36df198e4b from master
2012-08-30 07:45:06 -04:00
Tim Graham f6351851b6 [1.4.X] Fixed #18871 - Fixed typo in multi-db docs.
Backport of 141e6cd2ac from master
2012-08-30 07:42:47 -04:00
Tim Graham 81bfe428e1 [1.4.X] Updated doc for settings.MEDIA_ROOT based on what's generated by settings.py; thanks ryan-blunden.
Backport of e6e01f619d from master
2012-08-25 08:27:48 -04:00
Tim Graham 4b8c6c4056 [1.4.x] Fixed #17069 -- Added log filter example to docs.
Backport of e06189f7ce from master.
2012-08-22 20:37:38 -04:00
Tim Graham 27c2ccc1ea [1.4.x] Fixed #18637 - Updated some documentation for aspects of models that are ModelForm specific, not admin specific.
Thanks Ben Sturmfels for the patch.

Backport of 13d47c3f33 from master.
2012-08-21 17:42:18 -04:00
Tim Graham 42aee6ffe5 [1.4.x] Fixed #14885 - Clarified that ModelForm cleaning may not fully complete if the form is invalid.
Thanks Ben Sturmfels for the patch.

Backport of 3fd89d9903 from master.
2012-08-21 17:36:41 -04:00
Tim Graham b05d2f51b8 [1.4.X] Fixed typo in form wizard docs.
Backport of 3631db88cb from master
2012-08-19 20:14:54 -04:00
Jeremy Cowgar eaa6e4e2d1 [1.4.X] Added load i18n code to the base wizard form template documentation as it uses the trans tag.
Backport of c23d306df8 from master
2012-08-19 19:07:48 -04:00
Tim Graham 232a308044 [1.4.X] Fixed #17180 - Emphasized the need to load the i18n template tag in each template that uses translations. Thanks stefan.freyr for the suggestion and buddylindsey for the draft patch.
Backport of 514a0013cd from master
2012-08-19 18:48:29 -04:00
Tim Saylor 03e79c3386 [1.4.X] Fixed a documentation typo on the widget page.
Backport of f8ef93a657 from master
2012-08-17 11:15:43 -04:00
Tim Graham e4b7e7d86d [1.4.X] Fixed #17183 - Added a note regarding LocaleMiddleware at the top of the i18n docs. Thanks krzysiumed for the patch.
Backport of b1f18e95a5 from master
2012-08-16 18:16:56 -04:00
Tim Graham 01b0231717 [1.4.X] Fixed #18223 - Corrected default transaction behavior in postgresql docs.
Thanks philipn for the report and mateusgondim for the patch.

Backport of 2079b730f1 from master
2012-08-16 18:15:52 -04:00
Raphaël Hertzog 57d9ccc4aa [1.4.x] Fixed #18239 -- Subclassed HTMLParser only for selected Python versions
Only Python versions affected by http://bugs.python.org/issue670664
should patch HTMLParser.
2012-08-16 21:12:30 +02:00
Tim Graham 3264894ee0 [1.4.X] Fixed #17680 - Clarified when logging is configured.
Backport of cb38fd9632 from master
2012-08-10 17:35:55 -04:00
Tim Graham fba0149e16 [1.4.X] Fixed #17016 - Added examples for file uploads in views.
Thanks Tim Saylor for the draft patch and Aymeric Augustin and Claude Paroz for feedback.

Backport of eff6ba2f64 from master
2012-08-10 17:32:34 -04:00
Tim Graham 6536f7597b [1.4.X] Fixed #17053 - Added a note about USE_THOUSAND_SEPARATOR setting to localizations docs.
Thanks shelldweller for the draft patch.
Backport of 4f3a6b853a from master
2012-08-06 18:18:00 -04:00
Tim Graham df8a2bf4cb [1.4.x] Merge pull request #233 from rafikdraoui/modeladmin-doc
Updated example of customized ModelAdmin in documentation for 1.4

Backport of a04f68b15d from master.
2012-08-05 13:27:05 -04:00
Tim Graham c54034a2ad [1.4.X] Fixed #16980 - Misc updates to the auth docs. Thanks Preston Holmes for the patch.
Backport of 865ff32b84 from master
2012-08-04 15:27:27 -04:00
Claude Paroz 49f9bb271d [1.4.x] Fixed #18713 -- Fixed custom comment app name in comments docs
Thanks Pratyush for the report.
2012-08-04 17:41:20 +02:00
Tim Graham 1d280026c3 [1.4.X] Fixed #15932 - Documented how to supress multiple reverse relations to the same model.
Thanks Claude Paroz for the patch.

Backport of b496be331c from master
2012-08-03 17:46:35 -04:00
Tim Graham c8e681f624 [1.4.X] Fixed #13904 - Documented how to avoid garbage collection messages in GIS.
Thanks Claude Peroz for the patch.

Backport of 083a3a4e39 from master
2012-08-03 05:20:03 -04:00
Tim Graham e9f458133c [1.4.X] Fixed #17704 - Updated the StackedInline section in Tutorial 2; thanks xbito for the draft patch.
Backport of 2a16eb0792 from master
2012-08-02 19:23:34 -04:00
Tim Graham d1ae3c899f [1.4.X] Fixed #16941 - Clarified naturaltime output when the time is more than a day old.
Thanks antoviaque for the patch.
Backport of 07e10fbe9f from master
2012-08-02 07:55:00 -04:00
Tim Graham 445b9663e7 [1.4.X] Fixed #18581 - Updated admin actions screenshots. Thanks Kevin London.
Backport of 39541be3bc from master
2012-08-02 07:53:12 -04:00
Simon Meers 68fd7f56e1 [1.4.x] Fixed #18472 - Added warning regarding set_language / i18n_patterns.
Backport of d7816c563b from master.
2012-08-02 21:16:30 +10:00
Tim Graham a276bdebb2 [1.4.X] Fixed #16168 - Added note regarding type requirements when overridng ModelForm fields.
Thanks Pieter Swinkels for the patch.

Backport of ebbc414d17 from master
2012-08-01 08:02:01 -04:00
Tim Graham 751a34e4b3 [1.4.x] Fixed #18122 - Clarified section title regarding applying permissions to generic views.
Backport of 964979e8ec from master.
2012-08-01 07:57:10 -04:00
Tim Graham fa6577f5b2 Fixed #17131 - Added per object permission notes to docs.
Thanks dchandek for the suggestion and mateusgondim for the patch.

Backport of 8d3e501502 from master
2012-07-30 18:12:40 -04:00
James Bennett 28a4d039a2 [1.4.x] Bump version numbers for security release. 2012-07-30 15:43:45 -05:00
Florian Apolloner e34685034b [1.4.x] Fixed a security issue in http redirects. Disclosure and new release forthcoming.
Backport of 4129201c3e from master.
2012-07-30 22:03:33 +02:00
Florian Apolloner c14f325c4e [1.4.x] Fixed second security issue in image uploading. Disclosure and release forthcoming.
Backport of b1d4634686 from master.
2012-07-30 22:00:17 +02:00
Florian Apolloner da33d67181 [1.4.x] Fixed a security issue in image uploading. Disclosure and release forthcoming.
Backport of dd16b17099 from master.
2012-07-30 22:00:17 +02:00
Tim Graham 94e91f75b9 [1.4.X] Fixed #18656 -- Fixed LocaleMiddleware link; thanks mitar for the report.
Backport of 07d70e9b26 from master
2012-07-28 13:33:19 -04:00
Florian Apolloner 498bf5c26c Merge pull request #212 from kevinlondon/ticket_18614_1_4
[1.4] Ticket 18614 -- Add Imports and change render_to_response to render
2012-07-25 13:33:33 -07:00
Ramiro Morales c2ff027861 [1.4.x] Made LiveServerTestCase to restore state on exit.
The piece of state is DB connections' allow_thread_sharing attribute
which gets munged when test are run when in-memory SQLite databases.

Thanks Anssi for suggesting the possible root cause and Julien for
implementing the fix.

Backport of ea667ee3ae from master.
2012-07-22 11:06:16 -03:00
Kevin London c6d06a9453 [1.4.x] Fixed #18614 -- Added imports and changed render_to_response to render. 2012-07-15 21:29:13 -07:00
Tim Graham 8ba78a0daf [1.4.X] Fixed #18577 - Clarified middleware initialization.
Thanks Lukasz Balcerzak for the patch.

Backport of 590de18add from master
2012-07-08 19:30:11 -04:00
Tim Graham dcede04715 [1.4.x] Fixed #18173 - Corrected ModelAdmin documentation for get_changelist.
Thanks Keryn Knight for the report and vanessagomes for the pckport of [d44aa98] from master.atch.

Backport of [d44aa98] from master.
2012-07-08 18:38:52 -04:00
Julien Phalip 9a2ca4266a [1.4.x] Fixed #17978 -- Fixed a minor layout issue when an inline contains a filter horizontal widget. Thanks to Aymeric Augustin for the report. 2012-07-07 15:44:26 -07:00
Claude Paroz fd88fe657b [1.4.x] Fixed #18164 -- Precised startapp template context content
Backport of 249c445446 from master.
2012-07-07 23:12:02 +02:00
Aymeric Augustin f1e416566a [1.4x] Fixed #18587 -- Typo in management command example
Thanks Frank Wiles.

Backport of 29ca3d3c4b from master.
2012-07-07 16:02:45 +02:00
Tim Graham f5db3bddb3 [1.4.X] Fixed #17997 - Documented that the debug server is now multithreaded by default.
Thanks trey.smith@ for the report and vanessagomes for the patch.

Backport of e4a1407a9c from master
2012-07-05 08:40:20 -04:00
Tim Graham c5e35afbcc [1.4.X] Fixed #17436 - Added warning about overriding Model.__init__()
Thanks zsiciarz for the draft patch.

Backport of 7313468f85 from master
2012-07-01 18:06:33 -04:00
Tim Graham 8bea1a7e4e [1.4.X] Fixed #16882 - Clarified why one should not use 'init_command' after initial database creation.
Backport of 9974069620 from master
2012-07-01 07:26:53 -04:00
Tim Graham 32bd77d392 [1.4.X] Fixed #18493 - Added instructions to locate the Django source files to the t
Thanks Claude Paroz for the draft patch.

Backport of c68f4c514c from master
2012-07-01 06:56:20 -04:00
Tim Graham fea5e0b80f [1.4.X] Fixed #17705 - Updated TabularInline image and doc in tutorial 2.
Thanks xbito for the draft patch.

Backport of c5fb8299ef from master
2012-06-30 18:21:50 -04:00
Raúl Cumplido 342e8a6246 [1.4.X] Fixed #18145 -- Improved documentation of unique_together type fields
Backport of 55ffcf8e7b from master
2012-06-30 17:24:26 -04:00
Tim Graham a89e76d151 [1.4.X] Fixed #17168 - Noted TransactionMiddleware only works with "default" database alias.
Thanks codeinthehole for the draft patch.

Backport of 5d81ad1af1 from master
2012-06-30 10:30:45 -04:00
Claude Paroz d92c38a281 [1.4.x] Fixed #18528 -- Fixed custom field value_to_string example
Thanks anuraguniyal for the report.
2012-06-29 15:11:13 +02:00
Tim Graham 9014b138e6 [1.4.X] Fixed #17511 - Removed reference to deprecated "reset" management command in FAQ; thanks voxpuibr@ for the report.
Backport of c8928b91b5 from master
2012-06-27 18:56:04 -04:00
Tim Graham 3631a028e2 [1.4.X] Fixed #18369 - Fixed argument name in render() function; thanks qsolo825@ for the report.
Backport of 1cf8287e3a from master
2012-06-27 18:43:14 -04:00
Luke Plant ff6ee5f06c [1.4.x] Added more explicit warnings about unconfigured reStructured Text usage in docs.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17915 bcc190cf-cafb-0310-a4f2-bffc1f526a37

Backport of 718f149b from master
2012-06-08 15:02:15 +01:00
Jacob Kaplan-Moss 45d43317b7 [1.4.X] Replaced documentation snippets using "gender" with less sensitive examples.
Backport of [7edf231] from master.
2012-06-06 13:55:09 +02:00
Luke Plant 0a8a6b92b2 [1.4.x] Noted that SECURE_PROXY_SSL_HEADER is needed by CSRF protection.
Both false positives and false negatives of HttpRequest.is_secure can be
dangerous.

Backport of 840ffd80ba from master
2012-06-04 21:49:42 +01:00
Luke Plant 3bd937aec2 [1.4.x] Rewrote security.txt SSL docs, noting SECURE_PROXY_SSL_HEADER.
Backport of 0199bdc0b from master
2012-06-04 21:46:37 +01:00
Karen Tracey 03f1d69f1e Merge pull request #109 from apollo13/fix4d2fdd
Fix test error.
2012-06-04 04:31:28 -07:00
Florian Apolloner 1c13cc023f [1.4.x] readd imports deleted in 4d2fdd 2012-06-04 13:24:05 +02:00
Julien Phalip 4d2fdd4185 [1.4.X] Fixed #18379 -- Made the sensitive_variables decorator work with object methods. 2012-06-03 23:59:01 -07:00
Michael Newman 0f69a16785 [1.4.x] Fixed #18135 -- Close connection used for db version checking
On MySQL when checking the server version, a new connection could be
created but never closed. This could result in open connections on
server startup.

Backport of 4423757c0c.
2012-05-27 21:51:03 +03:00
Adrian Holovaty d3fa8d92ea [1.4.x] Updated docs/intro/whatsnext.txt to reference Git instead of SVN 2012-05-16 23:19:09 +02:00
Claude Paroz 6bb85d98b0 [1.4.x] Fixed #18019 -- Use threaded runserver only when database supports it. 2012-05-15 09:23:52 +02:00
Claude Paroz 589af4971e [1.4.x] Fixed #18301 -- Fixed url name in password reset example.
Thanks nicknnn for the report.
2012-05-11 20:19:32 +02:00
Jannis Leidel 35423f6fb1 [1.4.X] Set the post process cache when finished instead of one by one.
This should prevent a race condition if running collectstatic is
canceled or its cache is accessed from other processes, leaving the
cache in a corrupt state.
2012-05-10 18:30:46 +02:00
Claude Paroz ffe620f203 Fixed #18270 -- Corrected variable name in password reset example.
Thanks schnippi for the report.
Backport of ec5423df05 from master.
2012-05-10 08:40:39 +02:00
Aymeric Augustin a3c8201b77 [1.4.x] Fixed #17976 -- Made forms.BooleanField pickleable.
Backport of 9350d1d59c from master.

This was a regression in Django 1.4.
Thanks bronger for the report and claudep for the patch.
2012-05-08 23:20:05 +02:00
Claude Paroz 521fe472e5 [1.4.X] Fixed #18186 -- Fixed ReportLab Web site links. Thanks pablog for the report and the initial patch.
Backport of r17928 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17929 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-22 17:20:39 +00:00
Ramiro Morales 839a71b0a5 [1.4.X] Added documentation notes about lack of database savepoints support when using MySQL+MyISAM.
Refs #15507 and r17341.

Backport of r17923.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17924 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-21 22:57:32 +00:00
Claude Paroz 143305126b [1.4.X] Fixed #18156 -- Updated signature of ModelAdmin change_view in docs to reflect r17466. Thanks arthurprat for the report.
Backport of r17918 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17919 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-20 13:01:04 +00:00
Claude Paroz 64bbf5187c [1.4.X] Fixed #18316 -- Fixed pre-1.3 PermWrapper? location in docs. Thanks Adrien Lemaire for the patch.
Backport of r17913 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17914 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-16 11:40:25 +00:00
Claude Paroz 2fa8b3f143 [1.4.X] Fixed #18118 -- Improved documentation for contrib.auth.hashers utility functions. Thanks Mathieu Agopian for the report and Ramiro Morales for the review.
Backport of r17905 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17906 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-13 11:40:44 +00:00
Claude Paroz 3f77b84489 [1.4.X] Fixed #18027 -- Removed an HTMLParser test that doesn't raise any more in recent Python versions. Thanks Arfever and Anssi Kaariainen for the report and the patch.
Backport of r17900 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17901 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-11 21:27:18 +00:00
Claude Paroz ee43524e22 [1.4.X] Fixed #18104 -- Added missing parentheses around two-lines deprecation string. Thanks Roy Smith for the report.
git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17897 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-11 17:20:59 +00:00
Claude Paroz 8ed9e9074c [1.4.X] Fixed #18095 -- Added missing 'cc' mention in EmailMessage recipients() description. Thanks Stéphane Raimbault for the report and the patch.
Backport of r17891 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17892 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-10 20:02:56 +00:00
Aymeric Augustin 01dfe35b38 [1.4.X] Fixed #18090 -- Applied filters when running prefetch_related backwards through a one-to-one relation. Backport of r17888 from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17889 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-10 06:06:14 +00:00
Claude Paroz 8adfdf08de [1.4.X] Fixed #17672 -- Precised MacPorts GeoDjango install instructions to install gdal with geos support. Thanks chosak for the report and the patch.
Backport of r17883 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17884 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-09 10:03:31 +00:00
Julien Phalip a6ba67ffd1 [1.4.X] Fixed #18086 -- Restored '-pk' as the default order in the admin changelist. This rectifies a slight change in behavior introduced in Django 1.4 and r17635.
Backport of r17881 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17882 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-09 04:32:42 +00:00
Ramiro Morales 9a3e9c27c2 [1.4.X] Fixed #18074 -- Fixed description of dumpdata command --database option.
Thanks aruseni for the report.

Backport of r17873 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17874 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-06 18:59:06 +00:00
Claude Paroz 61b13444c5 [1.4.X] Fixed #18009 -- Cleaned up a comment about removal of the old contrib.syndication Feed class. Thanks Keryn Knight for the report.
Backport of r17866 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17867 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-02 19:52:32 +00:00
Claude Paroz 456d4db251 [1.4.X] Fixed #18045 -- Corrected the documented default value of SESSION_COOKIE_HTTPONLY setting. Missing bit of r17135.
Backport of r17862 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17863 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-04-01 17:17:21 +00:00
Julien Phalip aafa73db54 [1.4.X] Fixed #17972 -- Ensured that admin filters on a foreign key respect the to_field attribute. This fixes a regression introduced in [14674] and Django 1.3. Thanks to graveyboat and Karen Tracey for the report.
Backport of r17854 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17858 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-31 18:46:18 +00:00
Aymeric Augustin 13822974dd [1.4.X] Removed documentation for SMTPConnection, which was removed at r15978. Backport of r17837 from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17838 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-31 12:12:12 +00:00
Claude Paroz 35124ae3e2 [1.4.X] Fixed #17999 -- Restored the links to examples from models documentation. Refs #17605.
Backport of r17832 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17833 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-30 18:03:54 +00:00
Claude Paroz 814385321b [1.4.X] Fixed #17993 -- Removed quotes around module parameter for wider compatibility. Thanks roberto@unbit.it for the report.
Backport of r17813 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17821 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-29 15:14:08 +00:00
Claude Paroz 515b3b85ed [1.4.X] Added missing indentation in models topic documentation.
Backport of r17812 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17820 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-29 15:12:43 +00:00
Claude Paroz 6c5933d175 [1.4.X] Make auth test pass even when LANGUAGE_CODE is not 'en'. Refs #17980. Thanks wassup for the report.
Backport of r17811 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17819 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-29 15:11:14 +00:00
Claude Paroz 277661c2af [1.4.X] Fixed #17733 -- Discouraged setting TIME_ZONE to None when USE_TZ is True. Thanks berdario for the report.
Backport of r17809 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17818 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-29 15:09:52 +00:00
Claude Paroz 37c0e10e8c [1.4.X] Fix lintian error in manpages.
Backport of r17808 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17817 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-29 15:08:38 +00:00
Claude Paroz ec2119e194 [1.4.X] Fixed #17963 -- Fixed internal links in the 1.4 release notes.
Backport of r17802 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17816 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-29 15:07:09 +00:00
Claude Paroz a815fd1652 [1.4.X] Bump the version in a docs example.
Backport of r17801 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17815 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-29 15:05:31 +00:00
Aymeric Augustin bbb2595f89 Create 1.4 release branch.
git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.4.X@17814 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2012-03-29 06:18:26 +00:00
284 changed files with 6778 additions and 1761 deletions

9
.gitignore vendored
View File

@ -1,4 +1,13 @@
*.egg-info
*.pot
*.py[co]
__pycache__
MANIFEST
dist/
docs/_build/
docs/locale/
node_modules/
tests/coverage_html/
tests/.coverage
build/
tests/report/

View File

@ -3,4 +3,13 @@ syntax:glob
*.egg-info
*.pot
*.py[co]
__pycache__
MANIFEST
dist/
docs/_build/
docs/locale/
node_modules/
tests/coverage_html/
tests/.coverage
build/
tests/report/

View File

@ -453,6 +453,7 @@ answer newbie questions, and generally made Django that much better:
Vinay Sajip <vinay_sajip@yahoo.co.uk>
Bartolome Sanchez Salado <i42sasab@uco.es>
Kadesarin Sanjek
Tim Saylor <tim.saylor@gmail.com>
Massimo Scamarcia <massimo.scamarcia@gmail.com>
Paulo Scardine <paulo@scardine.com.br>
David Schein

View File

@ -1,4 +1,4 @@
VERSION = (1, 4, 0, 'final', 0)
VERSION = (1, 4, 23, 'alpha', 0)
def get_version(version=None):
"""Derives a PEP386-compliant version number from VERSION."""

View File

@ -29,6 +29,10 @@ ADMINS = ()
# * Receive x-headers
INTERNAL_IPS = ()
# Hosts/domain names that are valid for this site.
# "*" matches anything, ".example.com" matches example.com and all subdomains
ALLOWED_HOSTS = ['*']
# Local time zone for this installation. All choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
# systems may support all possibilities). When USE_TZ is True, this is
@ -512,6 +516,7 @@ PASSWORD_HASHERS = (
'django.contrib.auth.hashers.BCryptPasswordHasher',
'django.contrib.auth.hashers.SHA1PasswordHasher',
'django.contrib.auth.hashers.MD5PasswordHasher',
'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher',
'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
'django.contrib.auth.hashers.CryptPasswordHasher',
)

View File

@ -20,13 +20,14 @@ DATABASES = {
}
}
# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.4/ref/settings/#allowed-hosts
ALLOWED_HOSTS = []
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# On Unix systems, a value of None will cause Django to use the same
# timezone as the operating system.
# If running in a Windows environment this must be set to the same as your
# system time zone.
# In a Windows environment this must be set to your system time zone.
TIME_ZONE = 'America/Chicago'
# Language code for this installation. All choices can be found here:

View File

@ -0,0 +1,6 @@
from django.core.exceptions import SuspiciousOperation
class DisallowedModelAdminToField(SuspiciousOperation):
"""Invalid to_field was passed to admin view via URL query string"""
pass

View File

@ -8,13 +8,13 @@ certain test -- e.g. being a DateField or ForeignKey.
import datetime
from django.db import models
from django.core.exceptions import ImproperlyConfigured
from django.utils.encoding import smart_unicode
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.utils.encoding import smart_unicode, force_unicode
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.contrib.admin.util import (get_model_from_relation,
reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
from django.contrib.admin.options import IncorrectLookupParameters
class ListFilter(object):
title = None # Human-readable title to appear in the right sidebar.
@ -102,7 +102,7 @@ class SimpleListFilter(ListFilter):
}
for lookup, title in self.lookup_choices:
yield {
'selected': self.value() == lookup,
'selected': self.value() == force_unicode(lookup),
'query_string': cl.get_query_string({
self.parameter_name: lookup,
}, []),
@ -129,7 +129,10 @@ class FieldListFilter(ListFilter):
return True
def queryset(self, request, queryset):
return queryset.filter(**self.used_parameters)
try:
return queryset.filter(**self.used_parameters)
except ValidationError, e:
raise IncorrectLookupParameters(e)
@classmethod
def register(cls, test, list_filter_class, take_priority=False):
@ -155,7 +158,10 @@ class FieldListFilter(ListFilter):
class RelatedFieldListFilter(FieldListFilter):
def __init__(self, field, request, params, model, model_admin, field_path):
other_model = get_model_from_relation(field)
rel_name = other_model._meta.pk.name
if hasattr(field, 'rel'):
rel_name = field.rel.get_related_field().name
else:
rel_name = other_model._meta.pk.name
self.lookup_kwarg = '%s__%s__exact' % (field_path, rel_name)
self.lookup_kwarg_isnull = '%s__isnull' % field_path
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
@ -299,7 +305,7 @@ class DateFieldListFilter(FieldListFilter):
else: # field is a models.DateField
today = now.date()
tomorrow = today + datetime.timedelta(days=1)
self.lookup_kwarg_since = '%s__gte' % field_path
self.lookup_kwarg_until = '%s__lt' % field_path
self.links = (

View File

@ -245,7 +245,7 @@ class BaseModelAdmin(object):
# if foo has been specificially included in the lookup list; so
# drop __id if it is the last part. However, first we need to find
# the pk attribute name.
pk_attr_name = None
rel_name = None
for part in parts[:-1]:
try:
field, _, _, _ = model._meta.get_field_by_name(part)
@ -255,13 +255,13 @@ class BaseModelAdmin(object):
return True
if hasattr(field, 'rel'):
model = field.rel.to
pk_attr_name = model._meta.pk.name
rel_name = field.rel.get_related_field().name
elif isinstance(field, RelatedObject):
model = field.model
pk_attr_name = model._meta.pk.name
rel_name = model._meta.pk.name
else:
pk_attr_name = None
if pk_attr_name and len(parts) > 1 and parts[-1] == pk_attr_name:
rel_name = None
if rel_name and len(parts) > 1 and parts[-1] == rel_name:
parts.pop()
if len(parts) == 1:
@ -269,6 +269,39 @@ class BaseModelAdmin(object):
clean_lookup = LOOKUP_SEP.join(parts)
return clean_lookup in self.list_filter or clean_lookup == self.date_hierarchy
def to_field_allowed(self, request, to_field):
"""
Returns True if the model associated with this admin should be
allowed to be referenced by the specified field.
"""
opts = self.model._meta
try:
field = opts.get_field(to_field)
except FieldDoesNotExist:
return False
# Always allow referencing the primary key since it's already possible
# to get this information from the change view URL.
if field.primary_key:
return True
# Make sure at least one of the models registered for this site
# references this field through a FK or a M2M relationship.
registered_models = set()
for model, admin in self.admin_site._registry.items():
registered_models.add(model)
for inline in admin.inlines:
registered_models.add(inline.model)
for related_object in opts.get_all_related_objects(include_hidden=True):
related_model = related_object.model
if (any(issubclass(model, related_model) for model in registered_models) and
related_object.field.rel.get_related_field() == field):
return True
return False
def has_add_permission(self, request):
"""
Returns True if the given request has permission to add an object.
@ -1317,15 +1350,21 @@ class ModelAdmin(BaseModelAdmin):
def history_view(self, request, object_id, extra_context=None):
"The 'history' admin view for this model."
from django.contrib.admin.models import LogEntry
# First check if the user can see this history.
model = self.model
obj = get_object_or_404(model, pk=unquote(object_id))
if not self.has_change_permission(request, obj):
raise PermissionDenied
# Then get the history for this object.
opts = model._meta
app_label = opts.app_label
action_list = LogEntry.objects.filter(
object_id = object_id,
content_type__id__exact = ContentType.objects.get_for_model(model).id
).select_related().order_by('action_time')
# If no history was found, see whether this object even exists.
obj = get_object_or_404(model, pk=unquote(object_id))
context = {
'title': _('Change history: %s') % force_unicode(obj),
'action_list': action_list,

View File

@ -41,7 +41,8 @@
text-align: left;
}
.selector .selector-filter label {
.selector .selector-filter label,
.inline-group .aligned .selector .selector-filter label {
width: 16px;
padding: 2px;
}

View File

@ -25,9 +25,9 @@ class AdminSeleniumWebDriverTestCase(LiveServerTestCase):
@classmethod
def tearDownClass(cls):
super(AdminSeleniumWebDriverTestCase, cls).tearDownClass()
if hasattr(cls, 'selenium'):
cls.selenium.quit()
super(AdminSeleniumWebDriverTestCase, cls).tearDownClass()
def wait_until(self, callback, timeout=10):
"""
@ -49,6 +49,20 @@ class AdminSeleniumWebDriverTestCase(LiveServerTestCase):
timeout
)
def wait_page_loaded(self):
"""
Block until page has started to load.
"""
from selenium.common.exceptions import TimeoutException
try:
# Wait for the next page to be loaded
self.wait_loaded_tag('body')
except TimeoutException:
# IE7 occasionnally returns an error "Internet Explorer cannot
# display the webpage" and doesn't load the next page. We just
# ignore it.
pass
def admin_login(self, username, password, login_url='/admin/'):
"""
Helper function to log into the admin.
@ -61,8 +75,7 @@ class AdminSeleniumWebDriverTestCase(LiveServerTestCase):
login_text = _('Log in')
self.selenium.find_element_by_xpath(
'//input[@value="%s"]' % login_text).click()
# Wait for the next page to be loaded.
self.wait_loaded_tag('body')
self.wait_page_loaded()
def get_css_value(self, selector, attribute):
"""
@ -102,4 +115,4 @@ class AdminSeleniumWebDriverTestCase(LiveServerTestCase):
`klass`.
"""
return (self.selenium.find_element_by_css_selector(selector)
.get_attribute('class').find(klass) != -1)
.get_attribute('class').find(klass) != -1)

View File

@ -10,6 +10,7 @@ from django.utils.translation import ugettext, ugettext_lazy
from django.utils.http import urlencode
from django.contrib.admin import FieldListFilter
from django.contrib.admin.exceptions import DisallowedModelAdminToField
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.util import (quote, get_fields_from_path,
lookup_needs_distinct, prepare_lookup_value)
@ -56,7 +57,10 @@ class ChangeList(object):
self.page_num = 0
self.show_all = ALL_VAR in request.GET
self.is_popup = IS_POPUP_VAR in request.GET
self.to_field = request.GET.get(TO_FIELD_VAR)
to_field = request.GET.get(TO_FIELD_VAR)
if to_field and not model_admin.to_field_allowed(request, to_field):
raise DisallowedModelAdminToField("The field %s cannot be referenced." % to_field)
self.to_field = to_field
self.params = dict(request.GET.items())
if PAGE_VAR in self.params:
del self.params[PAGE_VAR]
@ -258,7 +262,7 @@ class ChangeList(object):
if not (set(ordering) & set(['pk', '-pk', pk_name, '-' + pk_name])):
# The two sets do not intersect, meaning the pk isn't present. So
# we add it.
ordering.append('pk')
ordering.append('-pk')
return ordering

View File

@ -17,6 +17,8 @@ from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters
csrf_protect_m = method_decorator(csrf_protect)
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
class GroupAdmin(admin.ModelAdmin):
search_fields = ('name',)
@ -83,7 +85,7 @@ class UserAdmin(admin.ModelAdmin):
self.admin_site.admin_view(self.user_change_password))
) + super(UserAdmin, self).get_urls()
@sensitive_post_parameters()
@sensitive_post_parameters_m
@csrf_protect_m
@transaction.commit_on_success
def add_view(self, request, form_url='', extra_context=None):
@ -113,7 +115,7 @@ class UserAdmin(admin.ModelAdmin):
return super(UserAdmin, self).add_view(request, form_url,
extra_context)
@sensitive_post_parameters()
@sensitive_post_parameters_m
def user_change_password(self, request, id, form_url=''):
if not self.has_change_permission(request):
raise PermissionDenied
@ -170,4 +172,3 @@ class UserAdmin(admin.ModelAdmin):
admin.site.register(Group, GroupAdmin)
admin.site.register(User, UserAdmin)

View File

@ -11,6 +11,11 @@ class PermLookupDict(object):
def __getitem__(self, perm_name):
return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
def __iter__(self):
# To fix 'item in perms.someapp' and __getitem__ iteraction we need to
# define __iter__. See #18979 for details.
raise TypeError("PermLookupDict is not iterable.")
def __nonzero__(self):
return self.user.has_module_perms(self.module_name)

View File

@ -35,8 +35,14 @@ def check_password(password, encoded, setter=None, preferred='default'):
password = smart_str(password)
encoded = smart_str(encoded)
if len(encoded) == 32 and '$' not in encoded:
# Ancient versions of Django created plain MD5 passwords and accepted
# MD5 passwords with an empty salt.
if ((len(encoded) == 32 and '$' not in encoded) or
(len(encoded) == 37 and encoded.startswith('md5$$'))):
hasher = get_hasher('unsalted_md5')
# Ancient versions of Django accepted SHA1 passwords with an empty salt.
elif len(encoded) == 46 and encoded.startswith('sha1$$'):
hasher = get_hasher('unsalted_sha1')
else:
algorithm = encoded.split('$', 1)[0]
hasher = get_hasher(algorithm)
@ -329,14 +335,48 @@ class MD5PasswordHasher(BasePasswordHasher):
])
class UnsaltedSHA1PasswordHasher(BasePasswordHasher):
"""
Very insecure algorithm that you should *never* use; stores SHA1 hashes
with an empty salt.
This class is implemented because Django used to accept such password
hashes. Some older Django installs still have these values lingering
around so we need to handle and upgrade them properly.
"""
algorithm = "unsalted_sha1"
def salt(self):
return ''
def encode(self, password, salt):
assert salt == ''
hash = hashlib.sha1(password).hexdigest()
return 'sha1$$%s' % hash
def verify(self, password, encoded):
encoded_2 = self.encode(password, '')
return constant_time_compare(encoded, encoded_2)
def safe_summary(self, encoded):
assert encoded.startswith('sha1$$')
hash = encoded[6:]
return SortedDict([
(_('algorithm'), self.algorithm),
(_('hash'), mask_hash(hash)),
])
class UnsaltedMD5PasswordHasher(BasePasswordHasher):
"""
I am an incredibly insecure algorithm you should *never* use;
stores unsalted MD5 hashes without the algorithm prefix.
Incredibly insecure algorithm that you should *never* use; stores unsalted
MD5 hashes without the algorithm prefix, also accepts MD5 hashes with an
empty salt.
This class is implemented because Django used to store passwords
this way. Some older Django installs still have these values
lingering around so we need to handle and upgrade them properly.
This class is implemented because Django used to store passwords this way
and to accept such password hashes. Some older Django installs still have
these values lingering around so we need to handle and upgrade them
properly.
"""
algorithm = "unsalted_md5"
@ -344,9 +384,12 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
return ''
def encode(self, password, salt):
assert salt == ''
return hashlib.md5(password).hexdigest()
def verify(self, password, encoded):
if len(encoded) == 37 and encoded.startswith('md5$$'):
encoded = encoded[5:]
encoded_2 = self.encode(password, '')
return constant_time_compare(encoded, encoded_2)

View File

@ -1,4 +1,5 @@
from django.contrib import auth
from django.contrib.auth.backends import RemoteUserBackend
from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import SimpleLazyObject
@ -47,9 +48,11 @@ class RemoteUserMiddleware(object):
try:
username = request.META[self.header]
except KeyError:
# If specified header doesn't exist then return (leaving
# request.user set to AnonymousUser by the
# AuthenticationMiddleware).
# If specified header doesn't exist then remove any existing
# authenticated remote-user, or return (leaving request.user set to
# AnonymousUser by the AuthenticationMiddleware).
if request.user.is_authenticated():
self._remove_invalid_user(request)
return
# If the user is already authenticated and that user is the user we are
# getting passed in the headers, then the correct user is already
@ -57,6 +60,11 @@ class RemoteUserMiddleware(object):
if request.user.is_authenticated():
if request.user.username == self.clean_username(username, request):
return
else:
# An authenticated user is associated with the request, but
# it does not match the authorized user in the header.
self._remove_invalid_user(request)
# We are seeing this user for the first time in this session, attempt
# to authenticate the user.
user = auth.authenticate(remote_user=username)
@ -78,3 +86,17 @@ class RemoteUserMiddleware(object):
except AttributeError: # Backend has no clean_username method.
pass
return username
def _remove_invalid_user(self, request):
"""
Removes the current authenticated user in the request which is invalid
but only if the user is authenticated via the RemoteUserBackend.
"""
try:
stored_backend = auth.load_backend(request.session.get(auth.BACKEND_SESSION_KEY, ''))
except ImproperlyConfigured:
# backend failed to load
auth.logout(request)
else:
if isinstance(stored_backend, RemoteUserBackend):
auth.logout(request)

View File

@ -2,12 +2,58 @@ import os
from django.conf import global_settings
from django.contrib.auth import authenticate
from django.contrib.auth.context_processors import PermWrapper, PermLookupDict
from django.db.models import Q
from django.template import context
from django.test import TestCase
from django.test.utils import override_settings
class MockUser(object):
def has_module_perm(self, perm):
if perm == 'mockapp.someapp':
return True
return False
def has_perm(self, perm):
if perm == 'someperm':
return True
return False
class PermWrapperTests(TestCase):
"""
Test some details of the PermWrapper implementation.
"""
class EQLimiterObject(object):
"""
This object makes sure __eq__ will not be called endlessly.
"""
def __init__(self):
self.eq_calls = 0
def __eq__(self, other):
if self.eq_calls > 0:
return True
self.eq_calls += 1
return False
def test_permwrapper_in(self):
"""
Test that 'something' in PermWrapper doesn't end up in endless loop.
"""
perms = PermWrapper(MockUser())
def raises():
self.EQLimiterObject() in perms
self.assertRaises(raises, TypeError)
def test_permlookupdict_in(self):
pldict = PermLookupDict(MockUser(), 'mockapp')
def raises():
self.EQLimiterObject() in pldict
self.assertRaises(raises, TypeError)
class AuthContextProcessorTests(TestCase):
"""
Tests for the ``django.contrib.auth.context_processors.auth`` processor

View File

@ -9,6 +9,7 @@ from django.test import TestCase
from django.test.utils import override_settings
from django.utils.encoding import force_unicode
from django.utils import translation
from django.utils.translation import ugettext as _
class UserCreationFormTest(TestCase):
@ -333,6 +334,6 @@ class PasswordResetFormTest(TestCase):
form = PasswordResetForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form["email"].errors,
[u"The user account associated with this e-mail address cannot reset the password."])
[_(u"The user account associated with this e-mail address cannot reset the password.")])
PasswordResetFormTest = override_settings(USE_TZ=False)(PasswordResetFormTest)

View File

@ -1,5 +1,5 @@
from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
from django.contrib.auth.hashers import (is_password_usable,
from django.contrib.auth.hashers import (is_password_usable,
check_password, make_password, PBKDF2PasswordHasher, load_hashers,
PBKDF2SHA1PasswordHasher, get_hasher, UNUSABLE_PASSWORD)
from django.utils import unittest
@ -14,6 +14,10 @@ except ImportError:
try:
import bcrypt
# Django 1.4 works only with py-bcrypt, not with bcrypt. py-bcrypt has
# '_bcrypt' attribute, bcrypt doesn't.
if not hasattr(bcrypt, '_bcrypt'):
bcrypt = None
except ImportError:
bcrypt = None
@ -31,7 +35,7 @@ class TestUtilsHashPass(unittest.TestCase):
def test_pkbdf2(self):
encoded = make_password('letmein', 'seasalt', 'pbkdf2_sha256')
self.assertEqual(encoded,
self.assertEqual(encoded,
'pbkdf2_sha256$10000$seasalt$FQCNpiZpTb0zub+HBsH6TOwyRxJ19FwvjbweatNmK/Y=')
self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded))
@ -39,7 +43,7 @@ class TestUtilsHashPass(unittest.TestCase):
def test_sha1(self):
encoded = make_password('letmein', 'seasalt', 'sha1')
self.assertEqual(encoded,
self.assertEqual(encoded,
'sha1$seasalt$fec3530984afba6bade3347b7140d1a7da7da8c7')
self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded))
@ -47,18 +51,33 @@ class TestUtilsHashPass(unittest.TestCase):
def test_md5(self):
encoded = make_password('letmein', 'seasalt', 'md5')
self.assertEqual(encoded,
self.assertEqual(encoded,
'md5$seasalt$f5531bef9f3687d0ccf0f617f0e25573')
self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded))
def test_unsalted_md5(self):
encoded = make_password('letmein', 'seasalt', 'unsalted_md5')
encoded = make_password('letmein', '', 'unsalted_md5')
self.assertEqual(encoded, '0d107d09f5bbe40cade3de5c71e9e9b7')
self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded))
# Alternate unsalted syntax
alt_encoded = "md5$$%s" % encoded
self.assertTrue(is_password_usable(alt_encoded))
self.assertTrue(check_password(u'letmein', alt_encoded))
self.assertFalse(check_password('letmeinz', alt_encoded))
def test_unsalted_sha1(self):
encoded = make_password('letmein', '', 'unsalted_sha1')
self.assertEqual(encoded, 'sha1$$b7a875fc1ea228b9061041b7cec4bd3c52ab3ce3')
self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password('letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded))
# Raw SHA1 isn't acceptable
alt_encoded = encoded[6:]
self.assertRaises(ValueError, check_password, 'letmein', alt_encoded)
@skipUnless(crypt, "no crypt module to generate password.")
def test_crypt(self):
@ -93,14 +112,14 @@ class TestUtilsHashPass(unittest.TestCase):
def test_low_level_pkbdf2(self):
hasher = PBKDF2PasswordHasher()
encoded = hasher.encode('letmein', 'seasalt')
self.assertEqual(encoded,
self.assertEqual(encoded,
'pbkdf2_sha256$10000$seasalt$FQCNpiZpTb0zub+HBsH6TOwyRxJ19FwvjbweatNmK/Y=')
self.assertTrue(hasher.verify('letmein', encoded))
def test_low_level_pbkdf2_sha1(self):
hasher = PBKDF2SHA1PasswordHasher()
encoded = hasher.encode('letmein', 'seasalt')
self.assertEqual(encoded,
self.assertEqual(encoded,
'pbkdf2_sha1$10000$seasalt$91JiNKgwADC8j2j86Ije/cc4vfQ=')
self.assertTrue(hasher.verify('letmein', encoded))

View File

@ -95,6 +95,24 @@ class RemoteUserTest(TestCase):
response = self.client.get('/remote_user/', REMOTE_USER=self.known_user)
self.assertEqual(default_login, response.context['user'].last_login)
def test_user_switch_forces_new_login(self):
"""
Tests that if the username in the header changes between requests
that the original user is logged out
"""
User.objects.create(username='knownuser')
# Known user authenticates
response = self.client.get('/remote_user/',
**{'REMOTE_USER': self.known_user})
self.assertEqual(response.context['user'].username, 'knownuser')
# During the session, the REMOTE_USER changes to a different user.
response = self.client.get('/remote_user/',
**{'REMOTE_USER': "newnewuser"})
# Ensure that the current user is not the prior remote_user
# In backends that create a new user, username is "newnewuser"
# In backends that do not create new users, it is '' (anonymous user)
self.assertNotEqual(response.context['user'].username, 'knownuser')
def tearDown(self):
"""Restores settings to avoid breaking other tests."""
settings.MIDDLEWARE_CLASSES = self.curr_middleware

View File

@ -51,6 +51,7 @@ urlpatterns = urlpatterns + patterns('',
(r'^logout/next_page/$', 'django.contrib.auth.views.logout', dict(next_page='/somewhere/')),
(r'^remote_user/$', remote_user_auth_view),
(r'^password_reset_from_email/$', 'django.contrib.auth.views.password_reset', dict(from_email='staffmember@example.com')),
(r'^admin_password_reset/$', 'django.contrib.auth.views.password_reset', dict(is_admin_site=True)),
(r'^login_required/$', login_required(password_reset)),
(r'^login_required_login_url/$', login_required(password_reset, login_url='/somewhere/')),

View File

@ -7,6 +7,7 @@ from django.conf import settings
from django.contrib.sites.models import Site, RequestSite
from django.contrib.auth.models import User
from django.core import mail
from django.core.exceptions import SuspiciousOperation
from django.core.urlresolvers import reverse, NoReverseMatch
from django.http import QueryDict
from django.utils.encoding import force_unicode
@ -106,6 +107,47 @@ class PasswordResetTest(AuthViewsTestCase):
self.assertEqual(len(mail.outbox), 1)
self.assertEqual("staffmember@example.com", mail.outbox[0].from_email)
@override_settings(ALLOWED_HOSTS=['adminsite.com'])
def test_admin_reset(self):
"If the reset view is marked as being for admin, the HTTP_HOST header is used for a domain override."
response = self.client.post('/admin_password_reset/',
{'email': 'staffmember@example.com'},
HTTP_HOST='adminsite.com'
)
self.assertEqual(response.status_code, 302)
self.assertEqual(len(mail.outbox), 1)
self.assertTrue("http://adminsite.com" in mail.outbox[0].body)
self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email)
# Skip any 500 handler action (like sending more mail...)
@override_settings(DEBUG_PROPAGATE_EXCEPTIONS=True)
def test_poisoned_http_host(self):
"Poisoned HTTP_HOST headers can't be used for reset emails"
# This attack is based on the way browsers handle URLs. The colon
# should be used to separate the port, but if the URL contains an @,
# the colon is interpreted as part of a username for login purposes,
# making 'evil.com' the request domain. Since HTTP_HOST is used to
# produce a meaningful reset URL, we need to be certain that the
# HTTP_HOST header isn't poisoned. This is done as a check when get_host()
# is invoked, but we check here as a practical consequence.
with self.assertRaises(SuspiciousOperation):
self.client.post('/password_reset/',
{'email': 'staffmember@example.com'},
HTTP_HOST='www.example:dr.frankenstein@evil.tld'
)
self.assertEqual(len(mail.outbox), 0)
# Skip any 500 handler action (like sending more mail...)
@override_settings(DEBUG_PROPAGATE_EXCEPTIONS=True)
def test_poisoned_http_host_admin_site(self):
"Poisoned HTTP_HOST headers can't be used for reset emails on admin views"
with self.assertRaises(SuspiciousOperation):
self.client.post('/admin_password_reset/',
{'email': 'staffmember@example.com'},
HTTP_HOST='www.example:dr.frankenstein@evil.tld'
)
self.assertEqual(len(mail.outbox), 0)
def _test_confirm_start(self):
# Start by creating the email
response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
@ -265,9 +307,12 @@ class LoginTest(AuthViewsTestCase):
# Those URLs should not pass the security check
for bad_url in ('http://example.com',
'http:///example.com',
'https://example.com',
'ftp://exampel.com',
'//example.com'):
'///example.com',
'//example.com',
'javascript:alert("XSS")'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': login_url,
@ -287,7 +332,8 @@ class LoginTest(AuthViewsTestCase):
'/view/?param=https://example.com',
'/view?param=ftp://exampel.com',
'view/?param=//example.com',
'https:///',
'https://testserver/',
'HTTPS://testserver/',
'//testserver/',
'/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
@ -423,9 +469,12 @@ class LogoutTest(AuthViewsTestCase):
# Those URLs should not pass the security check
for bad_url in ('http://example.com',
'http:///example.com',
'https://example.com',
'ftp://exampel.com',
'//example.com'):
'///example.com',
'//example.com',
'javascript:alert("XSS")'):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': logout_url,
'next': REDIRECT_FIELD_NAME,
@ -443,7 +492,8 @@ class LogoutTest(AuthViewsTestCase):
'/view/?param=https://example.com',
'/view?param=ftp://exampel.com',
'view/?param=//example.com',
'https:///',
'https://testserver/',
'HTTPS://testserver/',
'//testserver/',
'/url%20with%20spaces/'): # see ticket #12534
safe_url = '%(url)s?%(next)s=%(good_url)s' % {

View File

@ -4,7 +4,7 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, QueryDict
from django.template.response import TemplateResponse
from django.utils.http import base36_to_int
from django.utils.http import base36_to_int, is_safe_url
from django.utils.translation import ugettext as _
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.cache import never_cache
@ -34,18 +34,11 @@ def login(request, template_name='registration/login.html',
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
netloc = urlparse.urlparse(redirect_to)[1]
# Use default setting if redirect_to is empty
if not redirect_to:
# Ensure the user-originating redirection url is safe.
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = settings.LOGIN_REDIRECT_URL
# Heavier security check -- don't allow redirection to a different
# host.
elif netloc and netloc != request.get_host():
redirect_to = settings.LOGIN_REDIRECT_URL
# Okay, security checks complete. Log the user in.
# Okay, security check complete. Log the user in.
auth_login(request, form.get_user())
if request.session.test_cookie_worked():
@ -78,27 +71,27 @@ def logout(request, next_page=None,
Logs out the user and displays 'You are logged out' message.
"""
auth_logout(request)
redirect_to = request.REQUEST.get(redirect_field_name, '')
if redirect_to:
netloc = urlparse.urlparse(redirect_to)[1]
# Security check -- don't allow redirection to a different host.
if not (netloc and netloc != request.get_host()):
return HttpResponseRedirect(redirect_to)
if next_page is None:
current_site = get_current_site(request)
context = {
'site': current_site,
'site_name': current_site.name,
'title': _('Logged out')
}
if extra_context is not None:
context.update(extra_context)
return TemplateResponse(request, template_name, context,
current_app=current_app)
else:
if redirect_field_name in request.REQUEST:
next_page = request.REQUEST[redirect_field_name]
# Security check -- don't allow redirection to a different host.
if not is_safe_url(url=next_page, host=request.get_host()):
next_page = request.path
if next_page:
# Redirect to this page until the session has been cleared.
return HttpResponseRedirect(next_page or request.path)
return HttpResponseRedirect(next_page)
current_site = get_current_site(request)
context = {
'site': current_site,
'site_name': current_site.name,
'title': _('Logged out')
}
if extra_context is not None:
context.update(extra_context)
return TemplateResponse(request, template_name, context,
current_app=current_app)
def logout_then_login(request, login_url=None, current_app=None, extra_context=None):
"""
@ -156,7 +149,7 @@ def password_reset(request, is_admin_site=False,
'request': request,
}
if is_admin_site:
opts = dict(opts, domain_override=request.META['HTTP_HOST'])
opts = dict(opts, domain_override=request.get_host())
form.save(**opts)
return HttpResponseRedirect(post_reset_redirect)
else:

View File

@ -44,9 +44,6 @@ def post_comment(request, next=None, using=None):
if not data.get('email', ''):
data["email"] = request.user.email
# Check to see if the POST data overrides the view's next argument.
next = data.get("next", next)
# Look up the object we're trying to comment about
ctype = data.get("content_type")
object_pk = data.get("object_pk")
@ -98,9 +95,9 @@ def post_comment(request, next=None, using=None):
]
return render_to_response(
template_list, {
"comment" : form.data.get("comment", ""),
"form" : form,
"next": next,
"comment": form.data.get("comment", ""),
"form": form,
"next": data.get("next", next),
},
RequestContext(request, {})
)
@ -131,7 +128,7 @@ def post_comment(request, next=None, using=None):
request = request
)
return next_redirect(data, next, comment_done, c=comment._get_pk_val())
return next_redirect(request, next, comment_done, c=comment._get_pk_val())
comment_done = confirmation_view(
template = "comments/posted.html",

View File

@ -10,7 +10,6 @@ from django.shortcuts import get_object_or_404, render_to_response
from django.views.decorators.csrf import csrf_protect
@csrf_protect
@login_required
def flag(request, comment_id, next=None):
@ -27,7 +26,7 @@ def flag(request, comment_id, next=None):
# Flag on POST
if request.method == 'POST':
perform_flag(request, comment)
return next_redirect(request.POST.copy(), next, flag_done, c=comment.pk)
return next_redirect(request, next, flag_done, c=comment.pk)
# Render a form on GET
else:
@ -54,7 +53,7 @@ def delete(request, comment_id, next=None):
if request.method == 'POST':
# Flag the comment as deleted instead of actually deleting it.
perform_delete(request, comment)
return next_redirect(request.POST.copy(), next, delete_done, c=comment.pk)
return next_redirect(request, next, delete_done, c=comment.pk)
# Render a form on GET
else:
@ -81,7 +80,7 @@ def approve(request, comment_id, next=None):
if request.method == 'POST':
# Flag the comment as approved.
perform_approve(request, comment)
return next_redirect(request.POST.copy(), next, approve_done, c=comment.pk)
return next_redirect(request, next, approve_done, c=comment.pk)
# Render a form on GET
else:

View File

@ -4,14 +4,15 @@ A few bits of helper functions for comment views.
import urllib
import textwrap
from django.http import HttpResponseRedirect
from django.core import urlresolvers
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.core.exceptions import ObjectDoesNotExist
from django.contrib import comments
from django.utils.http import is_safe_url
def next_redirect(data, default, default_view, **get_kwargs):
def next_redirect(request, default, default_view, **get_kwargs):
"""
Handle the "where should I go next?" part of comment views.
@ -21,9 +22,10 @@ def next_redirect(data, default, default_view, **get_kwargs):
Returns an ``HttpResponseRedirect``.
"""
next = data.get("next", default)
if next is None:
next = request.POST.get('next', default)
if not is_safe_url(url=next, host=request.get_host()):
next = urlresolvers.reverse(default_view)
if get_kwargs:
if '#' in next:
tmp = next.rsplit('#', 1)

View File

@ -9,6 +9,7 @@ from django.contrib.sites.models import Site
from django.http import HttpRequest, Http404
from django.test import TestCase
from django.utils.encoding import smart_str
from django.test.utils import override_settings
class FooWithoutUrl(models.Model):
@ -114,6 +115,7 @@ class ContentTypesTests(TestCase):
FooWithUrl: ContentType.objects.get_for_model(FooWithUrl),
})
@override_settings(ALLOWED_HOSTS=['example.com'])
def test_shortcut_view(self):
"""
Check that the shortcut view (used for the admin "view on site"

View File

@ -7,29 +7,7 @@ class GeoSQLCompiler(BaseGeoSQLCompiler, SQLCompiler):
pass
class SQLInsertCompiler(compiler.SQLInsertCompiler, GeoSQLCompiler):
def placeholder(self, field, val):
if field is None:
# A field value of None means the value is raw.
return val
elif hasattr(field, 'get_placeholder'):
# Some fields (e.g. geo fields) need special munging before
# they can be inserted.
ph = field.get_placeholder(val, self.connection)
if ph == 'NULL':
# If the placeholder returned is 'NULL', then we need to
# to remove None from the Query parameters. Specifically,
# cx_Oracle will assume a CHAR type when a placeholder ('%s')
# is used for columns of MDSYS.SDO_GEOMETRY. Thus, we use
# 'NULL' for the value, and remove None from the query params.
# See also #10888.
param_idx = self.query.columns.index(field.column)
params = list(self.query.params)
params.pop(param_idx)
self.query.params = tuple(params)
return ph
else:
# Return the common case for the placeholder
return '%s'
pass
class SQLDeleteCompiler(compiler.SQLDeleteCompiler, GeoSQLCompiler):
pass

View File

@ -9,6 +9,7 @@
"""
import re
from decimal import Decimal
from itertools import izip
from django.db.backends.oracle.base import DatabaseOperations
from django.contrib.gis.db.backends.base import BaseSpatialOperations
@ -287,3 +288,12 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations):
def spatial_ref_sys(self):
from django.contrib.gis.db.backends.oracle.models import SpatialRefSys
return SpatialRefSys
def modify_insert_params(self, placeholders, params):
"""Drop out insert parameters for NULL placeholder. Needed for Oracle Spatial
backend due to #10888
"""
# This code doesn't work for bulk insert cases.
assert len(placeholders) == 1
return [[param for pholder,param
in izip(placeholders[0], params[0]) if pholder != 'NULL'], ]

View File

@ -101,8 +101,11 @@ geos_version.argtypes = None
geos_version.restype = c_char_p
# Regular expression should be able to parse version strings such as
# '3.0.0rc4-CAPI-1.3.3', '3.0.0-CAPI-1.4.1' or '3.4.0dev-CAPI-1.8.0'
version_regex = re.compile(r'^(?P<version>(?P<major>\d+)\.(?P<minor>\d+)\.(?P<subminor>\d+))((rc(?P<release_candidate>\d+))|dev)?-CAPI-(?P<capi_version>\d+\.\d+\.\d+)$')
# '3.0.0rc4-CAPI-1.3.3', '3.0.0-CAPI-1.4.1', '3.4.0dev-CAPI-1.8.0' or '3.4.0dev-CAPI-1.8.0 r0'
version_regex = re.compile(
r'^(?P<version>(?P<major>\d+)\.(?P<minor>\d+)\.(?P<subminor>\d+))'
r'((rc(?P<release_candidate>\d+))|dev)?-CAPI-(?P<capi_version>\d+\.\d+\.\d+)( r\d+)?$'
)
def geos_version_info():
"""
Returns a dictionary containing the various version metadata parsed from
@ -112,8 +115,10 @@ def geos_version_info():
"""
ver = geos_version()
m = version_regex.match(ver)
if not m: raise GEOSException('Could not parse version info string "%s"' % ver)
return dict((key, m.group(key)) for key in ('version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor'))
if not m:
raise GEOSException('Could not parse version info string "%s"' % ver)
return dict((key, m.group(key)) for key in (
'version', 'release_candidate', 'capi_version', 'major', 'minor', 'subminor'))
# Version numbers and whether or not prepared geometry support is available.
_verinfo = geos_version_info()

View File

@ -662,7 +662,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
for i in range(len(mp)):
# Creating a random point.
pnt = mp[i]
new = Point(random.randint(1, 100), random.randint(1, 100))
new = Point(random.randint(21, 100), random.randint(21, 100))
# Testing the assignment
mp[i] = new
s = str(new) # what was used for the assignment is still accessible
@ -1050,15 +1050,17 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
print "\nEND - expecting GEOS_NOTICE; safe to ignore.\n"
def test28_geos_version(self):
"Testing the GEOS version regular expression."
"""Testing the GEOS version regular expression."""
from django.contrib.gis.geos.libgeos import version_regex
versions = [ ('3.0.0rc4-CAPI-1.3.3', '3.0.0'),
('3.0.0-CAPI-1.4.1', '3.0.0'),
('3.4.0dev-CAPI-1.8.0', '3.4.0') ]
for v, expected in versions:
m = version_regex.match(v)
self.assertTrue(m)
self.assertEqual(m.group('version'), expected)
versions = [('3.0.0rc4-CAPI-1.3.3', '3.0.0', '1.3.3'),
('3.0.0-CAPI-1.4.1', '3.0.0', '1.4.1'),
('3.4.0dev-CAPI-1.8.0', '3.4.0', '1.8.0'),
('3.4.0dev-CAPI-1.8.0 r0', '3.4.0', '1.8.0')]
for v_init, v_geos, v_capi in versions:
m = version_regex.match(v_init)
self.assertTrue(m, msg="Unable to parse the version string '%s'" % v_init)
self.assertEqual(m.group('version'), v_geos)
self.assertEqual(m.group('capi_version'), v_capi)
def suite():

View File

@ -68,23 +68,27 @@ class OGRInspectTest(TestCase):
layer_key=AllOGRFields._meta.db_table,
decimal=['f_decimal'])
expected = [
'# This is an auto-generated Django model module created by ogrinspect.',
'from django.contrib.gis.db import models',
'',
'class Measurement(models.Model):',
' f_decimal = models.DecimalField(max_digits=0, decimal_places=0)',
' f_int = models.IntegerField()',
' f_datetime = models.DateTimeField()',
' f_time = models.TimeField()',
' f_float = models.FloatField()',
' f_char = models.CharField(max_length=10)',
' f_date = models.DateField()',
' geom = models.PolygonField()',
' objects = models.GeoManager()',
]
self.assertTrue(model_def.startswith(
'# This is an auto-generated Django model module created by ogrinspect.\n'
'from django.contrib.gis.db import models\n'
'\n'
'class Measurement(models.Model):\n'
))
# The ordering of model fields might vary depending on several factors (version of GDAL, etc.)
self.assertIn(' f_decimal = models.DecimalField(max_digits=0, decimal_places=0)', model_def)
self.assertIn(' f_int = models.IntegerField()', model_def)
self.assertIn(' f_datetime = models.DateTimeField()', model_def)
self.assertIn(' f_time = models.TimeField()', model_def)
self.assertIn(' f_float = models.FloatField()', model_def)
self.assertIn(' f_char = models.CharField(max_length=10)', model_def)
self.assertIn(' f_date = models.DateField()', model_def)
self.assertTrue(model_def.endswith(
' geom = models.PolygonField()\n'
' objects = models.GeoManager()'
))
self.assertEqual(model_def, '\n'.join(expected))
def get_ogr_db_string():
# Construct the DB string that GDAL will use to inspect the database.

View File

@ -65,8 +65,8 @@ def markdown(value, arg=''):
safe_mode = True
else:
safe_mode = False
python_markdown_deprecation = "The use of Python-Markdown "
"< 2.1 in Django is deprecated; please update to the current version"
python_markdown_deprecation = ("The use of Python-Markdown "
"< 2.1 in Django is deprecated; please update to the current version")
# Unicode support only in markdown v1.7 or above. Version_info
# exist only in markdown v1.6.2rc-2 or above.
markdown_vers = getattr(markdown, "version_info", None)

View File

@ -128,6 +128,13 @@ class SessionBase(object):
self.accessed = True
self.modified = True
def is_empty(self):
"Returns True when there is no session_key and the session is empty"
try:
return not bool(self._session_key) and not self._session_cache
except AttributeError:
return True
def _get_new_session_key(self):
"Returns session key that isn't being used."
# Todo: move to 0-9a-z charset in 1.5
@ -230,7 +237,7 @@ class SessionBase(object):
"""
self.clear()
self.delete()
self.create()
self._session_key = None
def cycle_key(self):
"""

View File

@ -25,7 +25,7 @@ class SessionStore(SessionBase):
session_data = None
if session_data is not None:
return session_data
self.create()
self._session_key = None
return {}
def create(self):
@ -45,6 +45,8 @@ class SessionStore(SessionBase):
raise RuntimeError("Unable to create a new session key.")
def save(self, must_create=False):
if self.session_key is None:
return self.create()
if must_create:
func = self._cache.add
else:
@ -56,7 +58,7 @@ class SessionStore(SessionBase):
raise CreateError
def exists(self, session_key):
return (KEY_PREFIX + session_key) in self._cache
return session_key and (KEY_PREFIX + session_key) in self._cache
def delete(self, session_key=None):
if session_key is None:

View File

@ -30,11 +30,12 @@ class SessionStore(DBStore):
data = None
if data is None:
data = super(SessionStore, self).load()
cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE)
if self.session_key:
cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE)
return data
def exists(self, session_key):
if (KEY_PREFIX + session_key) in cache:
if session_key and (KEY_PREFIX + session_key) in cache:
return True
return super(SessionStore, self).exists(session_key)
@ -57,4 +58,4 @@ class SessionStore(DBStore):
"""
self.clear()
self.delete(self.session_key)
self.create()
self._session_key = None

View File

@ -20,7 +20,7 @@ class SessionStore(SessionBase):
)
return self.decode(force_unicode(s.session_data))
except (Session.DoesNotExist, SuspiciousOperation):
self.create()
self._session_key = None
return {}
def exists(self, session_key):
@ -37,7 +37,6 @@ class SessionStore(SessionBase):
# Key wasn't unique. Try again.
continue
self.modified = True
self._session_cache = {}
return
def save(self, must_create=False):
@ -47,6 +46,8 @@ class SessionStore(SessionBase):
create a *new* entry (as opposed to possibly updating an existing
entry).
"""
if self.session_key is None:
return self.create()
obj = Session(
session_key=self._get_or_create_session_key(),
session_data=self.encode(self._get_session(no_load=must_create)),

View File

@ -56,11 +56,11 @@ class SessionStore(SessionBase):
try:
session_data = self.decode(file_data)
except (EOFError, SuspiciousOperation):
self.create()
self._session_key = None
finally:
session_file.close()
except IOError:
self.create()
self._session_key = None
return session_data
def create(self):
@ -71,10 +71,11 @@ class SessionStore(SessionBase):
except CreateError:
continue
self.modified = True
self._session_cache = {}
return
def save(self, must_create=False):
if self.session_key is None:
return self.create()
# Get the session data now, before we start messing
# with the file it is stored within.
session_data = self._get_session(no_load=must_create)

View File

@ -14,30 +14,38 @@ class SessionMiddleware(object):
def process_response(self, request, response):
"""
If request.session was modified, or if the configuration is to save the
session every time, save the changes and set a session cookie.
session every time, save the changes and set a session cookie or delete
the session cookie if the session has been emptied.
"""
try:
accessed = request.session.accessed
modified = request.session.modified
empty = request.session.is_empty()
except AttributeError:
pass
else:
if accessed:
patch_vary_headers(response, ('Cookie',))
if modified or settings.SESSION_SAVE_EVERY_REQUEST:
if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = cookie_date(expires_time)
# Save the session data and refresh the client cookie.
request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None,
httponly=settings.SESSION_COOKIE_HTTPONLY or None)
# First check if we need to delete this cookie.
# The session should be deleted only if the session is entirely empty
if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
response.delete_cookie(settings.SESSION_COOKIE_NAME,
domain=settings.SESSION_COOKIE_DOMAIN)
else:
if accessed:
patch_vary_headers(response, ('Cookie',))
if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = cookie_date(expires_time)
# Save the session data and refresh the client cookie.
request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME,
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None,
httponly=settings.SESSION_COOKIE_HTTPONLY or None)
return response

View File

@ -150,6 +150,7 @@ class SessionTestsMixin(object):
self.session.flush()
self.assertFalse(self.session.exists(prev_key))
self.assertNotEqual(self.session.session_key, prev_key)
self.assertIsNone(self.session.session_key)
self.assertTrue(self.session.modified)
self.assertTrue(self.session.accessed)
@ -162,6 +163,11 @@ class SessionTestsMixin(object):
self.assertNotEqual(self.session.session_key, prev_key)
self.assertEqual(self.session.items(), prev_data)
def test_save_doesnt_clear_data(self):
self.session['a'] = 'b'
self.session.save()
self.assertEqual(self.session['a'], 'b')
def test_invalid_key(self):
# Submitting an invalid session key (either by guessing, or if the db has
# removed the key) results in a new key being generated.
@ -256,6 +262,20 @@ class SessionTestsMixin(object):
encoded = self.session.encode(data)
self.assertEqual(self.session.decode(encoded), data)
def test_session_load_does_not_create_record(self):
"""
Loading an unknown session key does not create a session record.
Creating session records on load is a DOS vulnerability.
"""
if self.backend is CookieSession:
raise unittest.SkipTest("Cookie backend doesn't have an external store to create records in.")
session = self.backend('deadbeef')
session.load()
self.assertFalse(session.exists(session.session_key))
# provided unknown key was cycled, not reused
self.assertNotEqual(session.session_key, 'deadbeef')
class DatabaseSessionTests(SessionTestsMixin, TestCase):
@ -413,6 +433,75 @@ class SessionMiddlewareTests(unittest.TestCase):
self.assertNotIn('httponly',
str(response.cookies[settings.SESSION_COOKIE_NAME]))
def test_session_delete_on_end(self):
request = RequestFactory().get('/')
response = HttpResponse('Session test')
middleware = SessionMiddleware()
# Before deleting, there has to be an existing cookie
request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc'
# Simulate a request that ends the session
middleware.process_request(request)
request.session.flush()
# Handle the response through the middleware
response = middleware.process_response(request, response)
# Check that the cookie was deleted, not recreated.
# A deleted cookie header looks like:
# Set-Cookie: sessionid=; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/
self.assertEqual(
'Set-Cookie: %s=; expires=Thu, 01-Jan-1970 00:00:00 GMT; '
'Max-Age=0; Path=/' % settings.SESSION_COOKIE_NAME,
str(response.cookies[settings.SESSION_COOKIE_NAME])
)
@override_settings(SESSION_COOKIE_DOMAIN='.example.local')
def test_session_delete_on_end_with_custom_domain(self):
request = RequestFactory().get('/')
response = HttpResponse('Session test')
middleware = SessionMiddleware()
# Before deleting, there has to be an existing cookie
request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc'
# Simulate a request that ends the session
middleware.process_request(request)
request.session.flush()
# Handle the response through the middleware
response = middleware.process_response(request, response)
# Check that the cookie was deleted, not recreated.
# A deleted cookie header with a custom domain looks like:
# Set-Cookie: sessionid=; Domain=.example.local;
# expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/
self.assertEqual(
'Set-Cookie: %s=; Domain=.example.local; expires=Thu, '
'01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/' % (
settings.SESSION_COOKIE_NAME,
),
str(response.cookies[settings.SESSION_COOKIE_NAME])
)
def test_flush_empty_without_session_cookie_doesnt_set_cookie(self):
request = RequestFactory().get('/')
response = HttpResponse('Session test')
middleware = SessionMiddleware()
# Simulate a request that ends the session
middleware.process_request(request)
request.session.flush()
# Handle the response through the middleware
response = middleware.process_response(request, response)
# A cookie should not be set.
self.assertEqual(response.cookies, {})
# The session is accessed so "Vary: Cookie" should be set.
self.assertEqual(response['Vary'], 'Cookie')
class CookieSessionTests(SessionTestsMixin, TestCase):

View File

@ -3,6 +3,7 @@ from django.contrib.sites.models import Site, RequestSite, get_current_site
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpRequest
from django.test import TestCase
from django.test.utils import override_settings
class SitesFrameworkTests(TestCase):
@ -39,6 +40,7 @@ class SitesFrameworkTests(TestCase):
site = Site.objects.get_current()
self.assertEqual(u"Example site", site.name)
@override_settings(ALLOWED_HOSTS=['example.com'])
def test_get_current_site(self):
# Test that the correct Site object is returned
request = HttpRequest()

View File

@ -190,8 +190,8 @@ class CachedFilesMixin(object):
if dry_run:
return
# delete cache of all handled paths
self.cache.delete_many([self.cache_key(path) for path in paths])
# where to store the new paths
hashed_paths = {}
# build a list of adjustable files
matches = lambda path: matches_patterns(path, self._patterns.keys())
@ -240,9 +240,12 @@ class CachedFilesMixin(object):
hashed_name = force_unicode(saved_name.replace('\\', '/'))
# and then set the cache accordingly
self.cache.set(self.cache_key(name), hashed_name)
hashed_paths[self.cache_key(name)] = hashed_name
yield name, hashed_name, processed
# Finally set the cache
self.cache.set_many(hashed_paths)
class CachedStaticFilesStorage(CachedFilesMixin, StaticFilesStorage):
"""

View File

@ -47,13 +47,18 @@ def get_image_dimensions(file_or_path, close=False):
file = open(file_or_path, 'rb')
close = True
try:
# Most of the time PIL only needs a small chunk to parse the image and
# get the dimensions, but with some TIFF files PIL needs to parse the
# whole file.
chunk_size = 1024
while 1:
data = file.read(1024)
data = file.read(chunk_size)
if not data:
break
p.feed(data)
if p.image:
return p.image.size
chunk_size = chunk_size*2
return None
finally:
if close:

View File

@ -1,13 +1,13 @@
import os
import errno
import urlparse
import itertools
from datetime import datetime
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
from django.core.files import locks, File
from django.core.files.move import file_move_safe
from django.utils.crypto import get_random_string
from django.utils.encoding import force_unicode, filepath_to_uri
from django.utils.functional import LazyObject
from django.utils.importlib import import_module
@ -63,13 +63,12 @@ class Storage(object):
"""
dir_name, file_name = os.path.split(name)
file_root, file_ext = os.path.splitext(file_name)
# If the filename already exists, add an underscore and a number (before
# the file extension, if one exists) to the filename until the generated
# filename doesn't exist.
count = itertools.count(1)
# If the filename already exists, add an underscore and a random 7
# character alphanumeric string (before the file extension, if one
# exists) to the filename until the generated filename doesn't exist.
while self.exists(name):
# file_ext includes the dot.
name = os.path.join(dir_name, "%s_%s%s" % (file_root, count.next(), file_ext))
name = os.path.join(dir_name, "%s_%s%s" % (file_root, get_random_string(7), file_ext))
return name

View File

@ -14,8 +14,6 @@ class BaseHandler(object):
response_fixes = [
http.fix_location_header,
http.conditional_content_removal,
http.fix_IE_for_attach,
http.fix_IE_for_vary,
]
def __init__(self):

View File

@ -35,4 +35,11 @@ class Command(BaseCommand):
# a strange error -- it causes this handle() method to be called
# multiple times.
shutdown_message = '\nServer stopped.\nNote that the test database, %r, has not been deleted. You can explore it on your own.' % db_name
call_command('runserver', addrport=addrport, shutdown_message=shutdown_message, use_reloader=False, use_ipv6=options['use_ipv6'])
use_threading = connection.features.test_db_allows_multiple_connections
call_command('runserver',
addrport=addrport,
shutdown_message=shutdown_message,
use_reloader=False,
use_ipv6=options['use_ipv6'],
use_threading=use_threading
)

View File

@ -8,6 +8,8 @@ from django.db import models, DEFAULT_DB_ALIAS
from django.utils.xmlutils import SimplerXMLGenerator
from django.utils.encoding import smart_unicode
from xml.dom import pulldom
from xml.sax import handler
from xml.sax.expatreader import ExpatParser as _ExpatParser
class Serializer(base.Serializer):
"""
@ -149,9 +151,13 @@ class Deserializer(base.Deserializer):
def __init__(self, stream_or_string, **options):
super(Deserializer, self).__init__(stream_or_string, **options)
self.event_stream = pulldom.parse(self.stream)
self.event_stream = pulldom.parse(self.stream, self._make_parser())
self.db = options.pop('using', DEFAULT_DB_ALIAS)
def _make_parser(self):
"""Create a hardened XML parser (no custom/external entities)."""
return DefusedExpatParser()
def next(self):
for event, node in self.event_stream:
if event == "START_ELEMENT" and node.nodeName == "object":
@ -290,3 +296,90 @@ def getInnerText(node):
else:
pass
return u"".join(inner_text)
# Below code based on Christian Heimes' defusedxml
class DefusedExpatParser(_ExpatParser):
"""
An expat parser hardened against XML bomb attacks.
Forbids DTDs, external entity references
"""
def __init__(self, *args, **kwargs):
_ExpatParser.__init__(self, *args, **kwargs)
self.setFeature(handler.feature_external_ges, False)
self.setFeature(handler.feature_external_pes, False)
def start_doctype_decl(self, name, sysid, pubid, has_internal_subset):
raise DTDForbidden(name, sysid, pubid)
def entity_decl(self, name, is_parameter_entity, value, base,
sysid, pubid, notation_name):
raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name)
def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name):
# expat 1.2
raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name)
def external_entity_ref_handler(self, context, base, sysid, pubid):
raise ExternalReferenceForbidden(context, base, sysid, pubid)
def reset(self):
_ExpatParser.reset(self)
parser = self._parser
parser.StartDoctypeDeclHandler = self.start_doctype_decl
parser.EntityDeclHandler = self.entity_decl
parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl
parser.ExternalEntityRefHandler = self.external_entity_ref_handler
class DefusedXmlException(ValueError):
"""Base exception."""
def __repr__(self):
return str(self)
class DTDForbidden(DefusedXmlException):
"""Document type definition is forbidden."""
def __init__(self, name, sysid, pubid):
super(DTDForbidden, self).__init__()
self.name = name
self.sysid = sysid
self.pubid = pubid
def __str__(self):
tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})"
return tpl.format(self.name, self.sysid, self.pubid)
class EntitiesForbidden(DefusedXmlException):
"""Entity definition is forbidden."""
def __init__(self, name, value, base, sysid, pubid, notation_name):
super(EntitiesForbidden, self).__init__()
self.name = name
self.value = value
self.base = base
self.sysid = sysid
self.pubid = pubid
self.notation_name = notation_name
def __str__(self):
tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})"
return tpl.format(self.name, self.sysid, self.pubid)
class ExternalReferenceForbidden(DefusedXmlException):
"""Resolving an external reference is forbidden."""
def __init__(self, context, base, sysid, pubid):
super(ExternalReferenceForbidden, self).__init__()
self.context = context
self.base = base
self.sysid = sysid
self.pubid = pubid
def __str__(self):
tpl = "ExternalReferenceForbidden(system_id='{}', public_id={})"
return tpl.format(self.sysid, self.pubid)

View File

@ -138,39 +138,6 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
self.style = color_style()
super(WSGIRequestHandler, self).__init__(*args, **kwargs)
def get_environ(self):
env = self.server.base_environ.copy()
env['SERVER_PROTOCOL'] = self.request_version
env['REQUEST_METHOD'] = self.command
if '?' in self.path:
path,query = self.path.split('?',1)
else:
path,query = self.path,''
env['PATH_INFO'] = urllib.unquote(path)
env['QUERY_STRING'] = query
env['REMOTE_ADDR'] = self.client_address[0]
if self.headers.typeheader is None:
env['CONTENT_TYPE'] = self.headers.type
else:
env['CONTENT_TYPE'] = self.headers.typeheader
length = self.headers.getheader('content-length')
if length:
env['CONTENT_LENGTH'] = length
for h in self.headers.headers:
k,v = h.split(':',1)
k=k.replace('-','_').upper(); v=v.strip()
if k in env:
continue # skip content length, type,etc.
if 'HTTP_'+k in env:
env['HTTP_'+k] += ','+v # comma-separate multiple headers
else:
env['HTTP_'+k] = v
return env
def log_message(self, format, *args):
# Don't bother logging requests for admin images or the favicon.
if (self.path.startswith(self.admin_media_prefix)
@ -199,6 +166,17 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
sys.stderr.write(msg)
def get_environ(self):
# Strip all headers with underscores in the name before constructing
# the WSGI environ. This prevents header-spoofing based on ambiguity
# between underscores and dashes both normalized to underscores in WSGI
# env vars. Nginx and Apache 2.4+ both do this as well.
for k, v in self.headers.items():
if '_' in k:
del self.headers[k]
return super(WSGIRequestHandler, self).get_environ()
class AdminMediaHandler(handlers.StaticFilesHandler):
"""

View File

@ -7,6 +7,7 @@ a string) and returns a tuple in this format:
(view_function, function_args, function_kwargs)
"""
import functools
import re
from threading import local
@ -230,6 +231,10 @@ class RegexURLResolver(LocaleRegexProvider):
self._reverse_dict = {}
self._namespace_dict = {}
self._app_dict = {}
# set of dotted paths to all functions and classes that are used in
# urlpatterns
self._callback_strs = set()
self._populated = False
def __repr__(self):
return smart_str(u'<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern))
@ -240,6 +245,18 @@ class RegexURLResolver(LocaleRegexProvider):
apps = {}
language_code = get_language()
for pattern in reversed(self.url_patterns):
if hasattr(pattern, '_callback_str'):
self._callback_strs.add(pattern._callback_str)
elif hasattr(pattern, '_callback'):
callback = pattern._callback
if isinstance(callback, functools.partial):
callback = callback.func
if not hasattr(callback, '__name__'):
lookup_str = callback.__module__ + "." + callback.__class__.__name__
else:
lookup_str = callback.__module__ + "." + callback.__name__
self._callback_strs.add(lookup_str)
p_pattern = pattern.regex.pattern
if p_pattern.startswith('^'):
p_pattern = p_pattern[1:]
@ -260,6 +277,7 @@ class RegexURLResolver(LocaleRegexProvider):
namespaces[namespace] = (p_pattern + prefix, sub_pattern)
for app_name, namespace_list in pattern.app_dict.items():
apps.setdefault(app_name, []).extend(namespace_list)
self._callback_strs.update(pattern._callback_strs)
else:
bits = normalize(p_pattern)
lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args))
@ -268,6 +286,7 @@ class RegexURLResolver(LocaleRegexProvider):
self._reverse_dict[language_code] = lookups
self._namespace_dict[language_code] = namespaces
self._app_dict[language_code] = apps
self._populated = True
@property
def reverse_dict(self):
@ -356,8 +375,13 @@ class RegexURLResolver(LocaleRegexProvider):
def _reverse_with_prefix(self, lookup_view, _prefix, *args, **kwargs):
if args and kwargs:
raise ValueError("Don't mix *args and **kwargs in call to reverse()!")
if not self._populated:
self._populate()
try:
lookup_view = get_callable(lookup_view, True)
if lookup_view in self._callback_strs:
lookup_view = get_callable(lookup_view, True)
except (ImportError, AttributeError), e:
raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e))
possibilities = self.reverse_dict.getlist(lookup_view)
@ -382,6 +406,8 @@ class RegexURLResolver(LocaleRegexProvider):
unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()])
candidate = (prefix_norm + result) % unicode_kwargs
if re.search(u'^%s%s' % (_prefix, pattern), candidate, re.UNICODE):
if candidate.startswith('//'):
candidate = '/%%2F%s' % candidate[2:]
return candidate
# lookup_view can be URL label, or dotted path, or callable, Any of
# these can be passed in at the top, but callables are not friendly in

View File

@ -50,7 +50,7 @@ class URLValidator(RegexValidator):
r'localhost|' #localhost...
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
r'(?::\d+)?' # optional port
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
r'(?:/?|[/?]\S+)\Z', re.IGNORECASE)
def __init__(self, verify_exists=False,
validator_user_agent=URL_VALIDATOR_USER_AGENT):
@ -133,11 +133,16 @@ class URLValidator(RegexValidator):
raise broken_error
integer_validator = RegexValidator(
re.compile('^-?\d+\Z'),
message=_('Enter a valid integer.'),
code='invalid',
)
def validate_integer(value):
try:
int(value)
except (ValueError, TypeError):
raise ValidationError('')
return integer_validator(value)
class EmailValidator(RegexValidator):
@ -160,14 +165,14 @@ email_re = re.compile(
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*" # dot-atom
# quoted-string, see also http://tools.ietf.org/html/rfc2822#section-3.2.5
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-\011\013\014\016-\177])*"'
r')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$)' # domain
r'|\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]$', re.IGNORECASE) # literal form, ipv4 address (SMTP 4.1.3)
r')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?\Z)' # domain
r'|\[(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]\Z', re.IGNORECASE) # literal form, ipv4 address (SMTP 4.1.3)
validate_email = EmailValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid')
slug_re = re.compile(r'^[-\w]+$')
slug_re = re.compile(r'^[-\w]+\Z')
validate_slug = RegexValidator(slug_re, _(u"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."), 'invalid')
ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\Z')
validate_ipv4_address = RegexValidator(ipv4_re, _(u'Enter a valid IPv4 address.'), 'invalid')
def validate_ipv6_address(value):
@ -205,7 +210,7 @@ def ip_address_validators(protocol, unpack_ipv4):
raise ValueError("The protocol '%s' is unknown. Supported: %s"
% (protocol, ip_address_validator_map.keys()))
comma_separated_int_list_re = re.compile('^[\d,]+$')
comma_separated_int_list_re = re.compile('^[\d,]+\Z')
validate_comma_separated_integer_list = RegexValidator(comma_separated_int_list_re, _(u'Enter only digits separated by commas.'), 'invalid')
@ -249,4 +254,3 @@ class MaxLengthValidator(BaseValidator):
clean = lambda self, x: len(x)
message = _(u'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).')
code = 'max_length'

View File

@ -42,8 +42,14 @@ backend = load_backend(connection.settings_dict['ENGINE'])
# Register an event that closes the database connection
# when a Django request is finished.
def close_connection(**kwargs):
for conn in connections.all():
conn.close()
# Avoid circular imports
from django.db import transaction
for conn in connections:
# If an error happens here the connection will be left in broken
# state. Once a good db connection is again available, the
# connection state will be cleaned up.
transaction.abort(conn)
connections[conn].close()
signals.request_finished.connect(close_connection)
# Register an event that resets connection.queries

View File

@ -83,6 +83,17 @@ class BaseDatabaseWrapper(object):
return
self.cursor().execute(self.ops.savepoint_commit_sql(sid))
def abort(self):
"""
Roll back any ongoing transaction and clean the transaction state
stack.
"""
if self._dirty:
self._rollback()
self._dirty = False
while self.transaction_state:
self.leave_transaction_management()
def enter_transaction_management(self, managed=True):
"""
Enters transaction management for a running thread. It must be balanced with
@ -470,6 +481,14 @@ class BaseDatabaseOperations(object):
"""
return None
def bulk_batch_size(self, fields, objs):
"""
Returns the maximum allowed batch size for the backend. The fields
are the fields going to be inserted in the batch, the objs contains
all the objects to be inserted.
"""
return len(objs)
def date_extract_sql(self, lookup_type, field_name):
"""
Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
@ -507,6 +526,17 @@ class BaseDatabaseOperations(object):
"""
return ''
def distinct_sql(self, fields):
"""
Returns an SQL DISTINCT clause which removes duplicate rows from the
result set. If any fields are given, only the given fields are being
checked for duplicates.
"""
if fields:
raise NotImplementedError('DISTINCT ON fields is not supported by this database backend')
else:
return 'DISTINCT'
def drop_foreignkey_sql(self):
"""
Returns the SQL command that drops a foreign key.
@ -562,17 +592,6 @@ class BaseDatabaseOperations(object):
"""
raise NotImplementedError('Full-text search is not implemented for this database backend')
def distinct_sql(self, fields):
"""
Returns an SQL DISTINCT clause which removes duplicate rows from the
result set. If any fields are given, only the given fields are being
checked for duplicates.
"""
if fields:
raise NotImplementedError('DISTINCT ON fields is not supported by this database backend')
else:
return 'DISTINCT'
def last_executed_query(self, cursor, sql, params):
"""
Returns a string of the query last executed by the given cursor, with
@ -866,6 +885,12 @@ class BaseDatabaseOperations(object):
conn = ' %s ' % connector
return conn.join(sub_expressions)
def modify_insert_params(self, placeholders, params):
"""Allow modification of insert parameters. Needed for Oracle Spatial
backend due to #10888.
"""
return params
class BaseDatabaseIntrospection(object):
"""
This class encapsulates all backend-specific introspection utilities

View File

@ -177,7 +177,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
# will tell you the default table type of the created
# table. Since all Django's test tables will have the same
# table type, that's enough to evaluate the feature.
cursor.execute("SHOW TABLE STATUS WHERE Name='INTROSPECT_TEST'")
cursor.execute("SHOW TABLE STATUS LIKE 'INTROSPECT_TEST'")
result = cursor.fetchone()
cursor.execute('DROP TABLE INTROSPECT_TEST')
self._storage_engine = result[1]
@ -407,11 +407,20 @@ class DatabaseWrapper(BaseDatabaseWrapper):
def get_server_version(self):
if not self.server_version:
new_connection = False
if not self._valid_connection():
self.cursor()
m = server_version_re.match(self.connection.get_server_info())
# Ensure we have a connection with the DB by using a temporary
# cursor
new_connection = True
self.cursor().close()
server_info = self.connection.get_server_info()
if new_connection:
# Make sure we close the connection
self.connection.close()
self.connection = None
m = server_version_re.match(server_info)
if not m:
raise Exception('Unable to determine MySQL version from version string %r' % self.connection.get_server_info())
raise Exception('Unable to determine MySQL version from version string %r' % server_info)
self.server_version = tuple([int(x) for x in m.groups()])
return self.server_version

View File

@ -167,6 +167,7 @@ class DatabaseCreation(BaseDatabaseCreation):
IDENTIFIED BY %(password)s
DEFAULT TABLESPACE %(tblspace)s
TEMPORARY TABLESPACE %(tblspace_temp)s
QUOTA UNLIMITED ON %(tblspace)s
""",
"""GRANT CONNECT, RESOURCE TO %(user)s""",
]

View File

@ -72,14 +72,14 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
FROM user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
user_tab_cols ta, user_tab_cols tb
WHERE user_constraints.table_name = %s AND
ta.table_name = %s AND
ta.table_name = user_constraints.table_name AND
ta.column_name = ca.column_name AND
ca.table_name = %s AND
ca.table_name = ta.table_name AND
user_constraints.constraint_name = ca.constraint_name AND
user_constraints.r_constraint_name = cb.constraint_name AND
cb.table_name = tb.table_name AND
cb.column_name = tb.column_name AND
ca.position = cb.position""", [table_name, table_name, table_name])
ca.position = cb.position""", [table_name])
relations = {}
for row in cursor.fetchall():
@ -87,36 +87,31 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
return relations
def get_indexes(self, cursor, table_name):
sql = """
SELECT LOWER(uic1.column_name) AS column_name,
CASE user_constraints.constraint_type
WHEN 'P' THEN 1 ELSE 0
END AS is_primary_key,
CASE user_indexes.uniqueness
WHEN 'UNIQUE' THEN 1 ELSE 0
END AS is_unique
FROM user_constraints, user_indexes, user_ind_columns uic1
WHERE user_constraints.constraint_type (+) = 'P'
AND user_constraints.index_name (+) = uic1.index_name
AND user_indexes.uniqueness (+) = 'UNIQUE'
AND user_indexes.index_name (+) = uic1.index_name
AND uic1.table_name = UPPER(%s)
AND uic1.column_position = 1
AND NOT EXISTS (
SELECT 1
FROM user_ind_columns uic2
WHERE uic2.index_name = uic1.index_name
AND uic2.column_position = 2
)
"""
Returns a dictionary of fieldname -> infodict for the given table,
where each infodict is in the format:
{'primary_key': boolean representing whether it's the primary key,
'unique': boolean representing whether it's a unique index}
"""
# This query retrieves each index on the given table, including the
# first associated field name
# "We were in the nick of time; you were in great peril!"
sql = """\
SELECT LOWER(all_tab_cols.column_name) AS column_name,
CASE user_constraints.constraint_type
WHEN 'P' THEN 1 ELSE 0
END AS is_primary_key,
CASE user_indexes.uniqueness
WHEN 'UNIQUE' THEN 1 ELSE 0
END AS is_unique
FROM all_tab_cols, user_cons_columns, user_constraints, user_ind_columns, user_indexes
WHERE all_tab_cols.column_name = user_cons_columns.column_name (+)
AND all_tab_cols.table_name = user_cons_columns.table_name (+)
AND user_cons_columns.constraint_name = user_constraints.constraint_name (+)
AND user_constraints.constraint_type (+) = 'P'
AND user_ind_columns.column_name (+) = all_tab_cols.column_name
AND user_ind_columns.table_name (+) = all_tab_cols.table_name
AND user_indexes.uniqueness (+) = 'UNIQUE'
AND user_indexes.index_name (+) = user_ind_columns.index_name
AND all_tab_cols.table_name = UPPER(%s)
"""
cursor.execute(sql, [table_name])
indexes = {}
for row in cursor.fetchall():
indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
indexes[row[0]] = {'primary_key': bool(row[1]),
'unique': bool(row[2])}
return indexes

View File

@ -83,7 +83,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_1000_query_parameters = False
supports_mixed_date_datetime_comparisons = False
has_bulk_insert = True
can_combine_inserts_with_and_without_auto_increment_pk = True
can_combine_inserts_with_and_without_auto_increment_pk = False
def _supports_stddev(self):
"""Confirm support for STDDEV and related stats functions
@ -104,6 +104,13 @@ class DatabaseFeatures(BaseDatabaseFeatures):
return has_support
class DatabaseOperations(BaseDatabaseOperations):
def bulk_batch_size(self, fields, objs):
"""
SQLite has a compile-time default (SQLITE_LIMIT_VARIABLE_NUMBER) of
999 variables per query.
"""
return (999 // len(fields)) if len(fields) > 0 else len(objs)
def date_extract_sql(self, lookup_type, field_name):
# sqlite doesn't support extract, so we fake it with the user-defined
# function django_extract that's registered in connect(). Note that
@ -208,7 +215,7 @@ class DatabaseOperations(BaseDatabaseOperations):
res.append("SELECT %s" % ", ".join(
"%%s AS %s" % self.quote_name(f.column) for f in fields
))
res.extend(["UNION SELECT %s" % ", ".join(["%s"] * len(fields))] * (num_values - 1))
res.extend(["UNION ALL SELECT %s" % ", ".join(["%s"] * len(fields))] * (num_values - 1))
return " ".join(res)
class DatabaseWrapper(BaseDatabaseWrapper):

View File

@ -911,6 +911,12 @@ class FilePathField(Field):
kwargs['max_length'] = kwargs.get('max_length', 100)
Field.__init__(self, verbose_name, name, **kwargs)
def get_prep_value(self, value):
value = super(FilePathField, self).get_prep_value(value)
if value is None:
return None
return smart_unicode(value)
def formfield(self, **kwargs):
defaults = {
'path': self.path,
@ -1010,6 +1016,12 @@ class IPAddressField(Field):
kwargs['max_length'] = 15
Field.__init__(self, *args, **kwargs)
def get_prep_value(self, value):
value = super(IPAddressField, self).get_prep_value(value)
if value is None:
return None
return smart_unicode(value)
def get_internal_type(self):
return "IPAddressField"
@ -1023,13 +1035,14 @@ class GenericIPAddressField(Field):
description = _("IP address")
default_error_messages = {}
def __init__(self, protocol='both', unpack_ipv4=False, *args, **kwargs):
def __init__(self, verbose_name=None, name=None, protocol='both',
unpack_ipv4=False, *args, **kwargs):
self.unpack_ipv4 = unpack_ipv4
self.default_validators, invalid_error_message = \
validators.ip_address_validators(protocol, unpack_ipv4)
self.default_error_messages['invalid'] = invalid_error_message
kwargs['max_length'] = 39
Field.__init__(self, *args, **kwargs)
Field.__init__(self, verbose_name, name, *args, **kwargs)
def get_internal_type(self):
return "GenericIPAddressField"
@ -1046,12 +1059,14 @@ class GenericIPAddressField(Field):
return value or None
def get_prep_value(self, value):
if value is None:
return value
if value and ':' in value:
try:
return clean_ipv6_address(value, self.unpack_ipv4)
except exceptions.ValidationError:
pass
return value
return smart_unicode(value)
def formfield(self, **kwargs):
defaults = {'form_class': forms.GenericIPAddressField}

View File

@ -239,7 +239,7 @@ class SingleRelatedObjectDescriptor(object):
def get_prefetch_query_set(self, instances):
vals = set(instance._get_pk_val() for instance in instances)
params = {'%s__pk__in' % self.related.field.name: vals}
return (self.get_query_set(instance=instances[0]),
return (self.get_query_set(instance=instances[0]).filter(**params),
attrgetter(self.related.field.attname),
lambda obj: obj._get_pk_val(),
True,
@ -531,9 +531,33 @@ def create_many_related_manager(superclass, rel):
self.reverse = reverse
self.through = through
self.prefetch_cache_name = prefetch_cache_name
self._pk_val = self.instance.pk
if self._pk_val is None:
raise ValueError("%r instance needs to have a primary key value before a many-to-many relationship can be used." % instance.__class__.__name__)
self._fk_val = self._get_fk_val(instance, source_field_name)
if self._fk_val is None:
raise ValueError('"%r" needs to have a value for field "%s" before '
'this many-to-many relationship can be used.' %
(instance, source_field_name))
# Even if this relation is not to pk, we require still pk value.
# The wish is that the instance has been already saved to DB,
# although having a pk value isn't a guarantee of that.
if instance.pk is None:
raise ValueError("%r instance needs to have a primary key value before "
"a many-to-many relationship can be used." %
instance.__class__.__name__)
def _get_fk_val(self, obj, field_name):
"""
Returns the correct value for this relationship's foreign key. This
might be something else than pk value when to_field is used.
"""
if not self.through:
# Make custom m2m fields with no through model defined usable.
return obj.pk
fk = self.through._meta.get_field(field_name)
if fk.rel.field_name and fk.rel.field_name != fk.rel.to._meta.pk.attname:
attname = fk.rel.get_related_field().get_attname()
return fk.get_prep_lookup('exact', getattr(obj, attname))
else:
return obj.pk
def get_query_set(self):
try:
@ -635,7 +659,11 @@ def create_many_related_manager(superclass, rel):
if not router.allow_relation(obj, self.instance):
raise ValueError('Cannot add "%r": instance is on database "%s", value is on database "%s"' %
(obj, self.instance._state.db, obj._state.db))
new_ids.add(obj.pk)
fk_val = self._get_fk_val(obj, target_field_name)
if fk_val is None:
raise ValueError('Cannot add "%r": the value for field "%s" is None' %
(obj, target_field_name))
new_ids.add(self._get_fk_val(obj, target_field_name))
elif isinstance(obj, Model):
raise TypeError("'%s' instance expected, got %r" % (self.model._meta.object_name, obj))
else:
@ -643,7 +671,7 @@ def create_many_related_manager(superclass, rel):
db = router.db_for_write(self.through, instance=self.instance)
vals = self.through._default_manager.using(db).values_list(target_field_name, flat=True)
vals = vals.filter(**{
source_field_name: self._pk_val,
source_field_name: self._fk_val,
'%s__in' % target_field_name: new_ids,
})
new_ids = new_ids - set(vals)
@ -657,11 +685,12 @@ def create_many_related_manager(superclass, rel):
# Add the ones that aren't there already
self.through._default_manager.using(db).bulk_create([
self.through(**{
'%s_id' % source_field_name: self._pk_val,
'%s_id' % source_field_name: self._fk_val,
'%s_id' % target_field_name: obj_id,
})
for obj_id in new_ids
])
if self.reverse or source_field_name == self.source_field_name:
# Don't send the signal when we are inserting the
# duplicate data row for symmetrical reverse entries.
@ -680,7 +709,7 @@ def create_many_related_manager(superclass, rel):
old_ids = set()
for obj in objs:
if isinstance(obj, self.model):
old_ids.add(obj.pk)
old_ids.add(self._get_fk_val(obj, target_field_name))
else:
old_ids.add(obj)
# Work out what DB we're operating on
@ -694,7 +723,7 @@ def create_many_related_manager(superclass, rel):
model=self.model, pk_set=old_ids, using=db)
# Remove the specified objects from the join table
self.through._default_manager.using(db).filter(**{
source_field_name: self._pk_val,
source_field_name: self._fk_val,
'%s__in' % target_field_name: old_ids
}).delete()
if self.reverse or source_field_name == self.source_field_name:
@ -714,7 +743,7 @@ def create_many_related_manager(superclass, rel):
instance=self.instance, reverse=self.reverse,
model=self.model, pk_set=None, using=db)
self.through._default_manager.using(db).filter(**{
source_field_name: self._pk_val
source_field_name: self._fk_val
}).delete()
if self.reverse or source_field_name == self.source_field_name:
# Don't send the signal when we are clearing the

View File

@ -377,7 +377,7 @@ class QuerySet(object):
obj.save(force_insert=True, using=self.db)
return obj
def bulk_create(self, objs):
def bulk_create(self, objs, batch_size=None):
"""
Inserts each of the instances into the database. This does *not* call
save() on each of the instances, does not send any pre/post save
@ -390,8 +390,10 @@ class QuerySet(object):
# this could be implemented if you didn't have an autoincrement pk,
# and 2) you could do it by doing O(n) normal inserts into the parent
# tables to get the primary keys back, and then doing a single bulk
# insert into the childmost table. We're punting on these for now
# because they are relatively rare cases.
# insert into the childmost table. Some databases might allow doing
# this by using RETURNING clause for the insert query. We're punting
# on these for now because they are relatively rare cases.
assert batch_size is None or batch_size > 0
if self.model._meta.parents:
raise ValueError("Can't bulk create an inherited model")
if not objs:
@ -407,13 +409,14 @@ class QuerySet(object):
try:
if (connection.features.can_combine_inserts_with_and_without_auto_increment_pk
and self.model._meta.has_auto_field):
self.model._base_manager._insert(objs, fields=fields, using=self.db)
self._batched_insert(objs, fields, batch_size)
else:
objs_with_pk, objs_without_pk = partition(lambda o: o.pk is None, objs)
if objs_with_pk:
self.model._base_manager._insert(objs_with_pk, fields=fields, using=self.db)
self._batched_insert(objs_with_pk, fields, batch_size)
if objs_without_pk:
self.model._base_manager._insert(objs_without_pk, fields=[f for f in fields if not isinstance(f, AutoField)], using=self.db)
fields= [f for f in fields if not isinstance(f, AutoField)]
self._batched_insert(objs_without_pk, fields, batch_size)
if forced_managed:
transaction.commit(using=self.db)
else:
@ -849,6 +852,20 @@ class QuerySet(object):
###################
# PRIVATE METHODS #
###################
def _batched_insert(self, objs, fields, batch_size):
"""
A little helper method for bulk_insert to insert the bulk one batch
at a time. Inserts recursively a batch from the front of the bulk and
then _batched_insert() the remaining objects again.
"""
if not objs:
return
ops = connections[self.db].ops
batch_size = (batch_size or max(ops.bulk_batch_size(fields, objs), 1))
for batch in [objs[i:i+batch_size]
for i in range(0, len(objs), batch_size)]:
self.model._base_manager._insert(batch, fields=fields,
using=self.db)
def _clone(self, klass=None, setup=False, **kwargs):
if klass is None:

View File

@ -885,6 +885,8 @@ class SQLInsertCompiler(SQLCompiler):
[self.placeholder(field, v) for field, v in izip(fields, val)]
for val in values
]
# Oracle Spatial needs to remove some values due to #10888
params = self.connection.ops.modify_insert_params(placeholders, params)
if self.return_id and self.connection.features.can_return_id_from_insert:
params = params[0]
col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))

View File

@ -25,6 +25,21 @@ class TransactionManagementError(Exception):
"""
pass
def abort(using=None):
"""
Roll back any ongoing transactions and clean the transaction management
state of the connection.
This method is to be used only in cases where using balanced
leave_transaction_management() calls isn't possible. For example after a
request has finished, the transaction state isn't known, yet the connection
must be cleaned up for the next request.
"""
if using is None:
using = DEFAULT_DB_ALIAS
connection = connections[using]
connection.abort()
def enter_transaction_management(managed=True, using=None):
"""
Enters transaction management for a running thread. It must be balanced with

View File

@ -570,20 +570,10 @@ class ImageField(FileField):
file = StringIO(data['content'])
try:
# load() is the only method that can spot a truncated JPEG,
# but it cannot be called sanely after verify()
trial_image = Image.open(file)
trial_image.load()
# Since we're about to use the file again we have to reset the
# file object if possible.
if hasattr(file, 'reset'):
file.reset()
# verify() is the only method that can spot a corrupt PNG,
# but it must be called immediately after the constructor
trial_image = Image.open(file)
trial_image.verify()
# load() could spot a truncated JPEG, but it loads the entire
# image in memory, which is a DoS vector. See #3848 and #18520.
# verify() must be called immediately after the constructor.
Image.open(file).verify()
except ImportError:
# Under PyPy, it is possible to import PIL. However, the underlying
# _imaging C module isn't available, so an ImportError will be

View File

@ -19,6 +19,9 @@ MAX_NUM_FORM_COUNT = 'MAX_NUM_FORMS'
ORDERING_FIELD_NAME = 'ORDER'
DELETION_FIELD_NAME = 'DELETE'
# default maximum number of forms in a formset, to prevent memory exhaustion
DEFAULT_MAX_NUM = 1000
class ManagementForm(Form):
"""
``ManagementForm`` is used to keep track of how many form instances
@ -111,7 +114,7 @@ class BaseFormSet(StrAndUnicode):
def _construct_forms(self):
# instantiate all the forms and put them in self.forms
self.forms = []
for i in xrange(self.total_form_count()):
for i in xrange(min(self.total_form_count(), self.absolute_max)):
self.forms.append(self._construct_form(i))
def _construct_form(self, i, **kwargs):
@ -360,9 +363,14 @@ class BaseFormSet(StrAndUnicode):
def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
can_delete=False, max_num=None):
"""Return a FormSet for the given form class."""
if max_num is None:
max_num = DEFAULT_MAX_NUM
# hard limit on forms instantiated, to prevent memory-exhaustion attacks
# limit defaults to DEFAULT_MAX_NUM, but developer can increase it via max_num
absolute_max = max(DEFAULT_MAX_NUM, max_num)
attrs = {'form': form, 'extra': extra,
'can_order': can_order, 'can_delete': can_delete,
'max_num': max_num}
'max_num': max_num, 'absolute_max': absolute_max}
return type(form.__name__ + 'FormSet', (formset,), attrs)
def all_valid(formsets):

View File

@ -487,15 +487,18 @@ class TimeInput(Input):
pass
return super(TimeInput, self)._has_changed(self._format_value(initial), data)
# Defined at module level so that CheckboxInput is picklable (#17976)
def boolean_check(v):
return not (v is False or v is None or v == '')
class CheckboxInput(Widget):
def __init__(self, attrs=None, check_test=None):
super(CheckboxInput, self).__init__(attrs)
# check_test is a callable that takes a value and returns True
# if the checkbox should be checked for that value.
if check_test is None:
self.check_test = lambda v: not (v is False or v is None or v == '')
else:
self.check_test = check_test
self.check_test = boolean_check if check_test is None else check_test
def render(self, name, value, attrs=None):
final_attrs = self.build_attrs(attrs, type='checkbox', name=name)

View File

@ -9,7 +9,7 @@ import warnings
from pprint import pformat
from urllib import urlencode, quote
from urlparse import urljoin
from urlparse import urljoin, urlparse
try:
from cStringIO import StringIO
except ImportError:
@ -114,7 +114,7 @@ class CompatCookie(SimpleCookie):
from django.conf import settings
from django.core import signing
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
from django.core.files import uploadhandler
from django.http.multipartparser import MultiPartParser
from django.http.utils import *
@ -126,6 +126,8 @@ from django.utils import timezone
RESERVED_CHARS="!*'();:@&=+$,/?%#[]"
absolute_http_url_re = re.compile(r"^https?://", re.I)
host_validation_re = re.compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9:]+\])(:\d+)?$")
class Http404(Exception):
pass
@ -212,7 +214,13 @@ class HttpRequest(object):
server_port = str(self.META['SERVER_PORT'])
if server_port != (self.is_secure() and '443' or '80'):
host = '%s:%s' % (host, server_port)
return host
allowed_hosts = ['*'] if settings.DEBUG else settings.ALLOWED_HOSTS
if validate_host(host, allowed_hosts):
return host
else:
raise SuspiciousOperation(
"Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): %s" % host)
def get_full_path(self):
# RFC 3986 requires query string arguments to be in the ASCII range.
@ -731,20 +739,22 @@ class HttpResponse(object):
raise Exception("This %s instance cannot tell its position" % self.__class__)
return sum([len(str(chunk)) for chunk in self._container])
class HttpResponseRedirect(HttpResponse):
class HttpResponseRedirectBase(HttpResponse):
allowed_schemes = ['http', 'https', 'ftp']
def __init__(self, redirect_to):
super(HttpResponseRedirectBase, self).__init__()
parsed = urlparse(redirect_to)
if parsed.scheme and parsed.scheme not in self.allowed_schemes:
raise SuspiciousOperation("Unsafe redirect to URL with scheme '%s'" % parsed.scheme)
self['Location'] = iri_to_uri(redirect_to)
class HttpResponseRedirect(HttpResponseRedirectBase):
status_code = 302
def __init__(self, redirect_to):
super(HttpResponseRedirect, self).__init__()
self['Location'] = iri_to_uri(redirect_to)
class HttpResponsePermanentRedirect(HttpResponse):
class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
status_code = 301
def __init__(self, redirect_to):
super(HttpResponsePermanentRedirect, self).__init__()
self['Location'] = iri_to_uri(redirect_to)
class HttpResponseNotModified(HttpResponse):
status_code = 304
@ -790,3 +800,43 @@ def str_to_unicode(s, encoding):
else:
return s
def validate_host(host, allowed_hosts):
"""
Validate the given host header value for this site.
Check that the host looks valid and matches a host or host pattern in the
given list of ``allowed_hosts``. Any pattern beginning with a period
matches a domain and all its subdomains (e.g. ``.example.com`` matches
``example.com`` and any subdomain), ``*`` matches anything, and anything
else must match exactly.
Return ``True`` for a valid host, ``False`` otherwise.
"""
# All validation is case-insensitive
host = host.lower()
# Basic sanity check
if not host_validation_re.match(host):
return False
# Validate only the domain part.
if host[-1] == ']':
# It's an IPv6 address without a port.
domain = host
else:
domain = host.rsplit(':', 1)[0]
for pattern in allowed_hosts:
pattern = pattern.lower()
match = (
pattern == '*' or
pattern.startswith('.') and (
domain.endswith(pattern) or domain == pattern[1:]
) or
pattern == domain
)
if match:
return True
return False

View File

@ -31,57 +31,3 @@ def conditional_content_removal(request, response):
if request.method == 'HEAD':
response.content = ''
return response
def fix_IE_for_attach(request, response):
"""
This function will prevent Django from serving a Content-Disposition header
while expecting the browser to cache it (only when the browser is IE). This
leads to IE not allowing the client to download.
"""
useragent = request.META.get('HTTP_USER_AGENT', '').upper()
if 'MSIE' not in useragent and 'CHROMEFRAME' not in useragent:
return response
offending_headers = ('no-cache', 'no-store')
if response.has_header('Content-Disposition'):
try:
del response['Pragma']
except KeyError:
pass
if response.has_header('Cache-Control'):
cache_control_values = [value.strip() for value in
response['Cache-Control'].split(',')
if value.strip().lower() not in offending_headers]
if not len(cache_control_values):
del response['Cache-Control']
else:
response['Cache-Control'] = ', '.join(cache_control_values)
return response
def fix_IE_for_vary(request, response):
"""
This function will fix the bug reported at
http://support.microsoft.com/kb/824847/en-us?spid=8722&sid=global
by clearing the Vary header whenever the mime-type is not safe
enough for Internet Explorer to handle. Poor thing.
"""
useragent = request.META.get('HTTP_USER_AGENT', '').upper()
if 'MSIE' not in useragent and 'CHROMEFRAME' not in useragent:
return response
# These mime-types that are decreed "Vary-safe" for IE:
safe_mime_types = ('text/html', 'text/plain', 'text/sgml')
# The first part of the Content-Type field will be the MIME type,
# everything after ';', such as character-set, can be ignored.
mime_type = response.get('Content-Type', '').partition(';')[0]
if mime_type not in safe_mime_types:
try:
del response['Vary']
except KeyError:
pass
return response

View File

@ -50,7 +50,8 @@ More details about how the caching works:
from django.conf import settings
from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
from django.utils.cache import (get_cache_key, get_max_age, has_vary_header,
learn_cache_key, patch_response_headers)
class UpdateCacheMiddleware(object):
@ -93,8 +94,15 @@ class UpdateCacheMiddleware(object):
if not self._should_update_cache(request, response):
# We don't need to update the cache, just return.
return response
if not response.status_code == 200:
return response
# Don't cache responses that set a user-specific (and maybe security
# sensitive) cookie in response to a cookie-less request.
if not request.COOKIES and response.cookies and has_vary_header(response, 'Cookie'):
return response
# Try to get the timeout from the "max-age" section of the "Cache-
# Control" header before reverting to using the default cache_timeout
# length.

View File

@ -1,6 +1,6 @@
import re
from django.utils.text import compress_string
from django.utils.text import compress_string, compress_sequence
from django.utils.cache import patch_vary_headers
re_accepts_gzip = re.compile(r'\bgzip\b')
@ -12,8 +12,9 @@ class GZipMiddleware(object):
on the Accept-Encoding header.
"""
def process_response(self, request, response):
# The response object can tell us whether content is a string or an iterable
# It's not worth attempting to compress really short responses.
if len(response.content) < 200:
if not response._base_content_is_iter and len(response.content) < 200:
return response
patch_vary_headers(response, ('Accept-Encoding',))
@ -32,15 +33,23 @@ class GZipMiddleware(object):
if not re_accepts_gzip.search(ae):
return response
# Return the compressed content only if it's actually shorter.
compressed_content = compress_string(response.content)
if len(compressed_content) >= len(response.content):
return response
# The response object can tell us whether content is a string or an iterable
if response._base_content_is_iter:
# If the response content is iterable we don't know the length, so delete the header.
del response['Content-Length']
# Wrap the response content in a streaming gzip iterator (direct access to inner response._container)
response.content = compress_sequence(response._container)
else:
# Return the compressed content only if it's actually shorter.
compressed_content = compress_string(response.content)
if len(compressed_content) >= len(response.content):
return response
response.content = compressed_content
response['Content-Length'] = str(len(response.content))
if response.has_header('ETag'):
response['ETag'] = re.sub('"$', ';gzip"', response['ETag'])
response.content = compressed_content
response['Content-Encoding'] = 'gzip'
response['Content-Length'] = str(len(response.content))
return response

View File

@ -15,6 +15,10 @@ class TransactionMiddleware(object):
def process_exception(self, request, exception):
"""Rolls back the database and leaves transaction management"""
if transaction.is_dirty():
# This rollback might fail because of network failure for example.
# If rollback isn't possible it is impossible to clean the
# connection's state. So leave the connection in dirty state and
# let request_finished signal deal with cleaning the connection.
transaction.rollback()
transaction.leave_transaction_management()
@ -22,6 +26,21 @@ class TransactionMiddleware(object):
"""Commits and leaves transaction management."""
if transaction.is_managed():
if transaction.is_dirty():
transaction.commit()
# Note: it is possible that the commit fails. If the reason is
# closed connection or some similar reason, then there is
# little hope to proceed nicely. However, in some cases (
# deferred foreign key checks for exampl) it is still possible
# to rollback().
try:
transaction.commit()
except Exception:
# If the rollback fails, the transaction state will be
# messed up. It doesn't matter, the connection will be set
# to clean state after the request finishes. And, we can't
# clean the state here properly even if we wanted to, the
# connection is in transaction but we can't rollback...
transaction.rollback()
transaction.leave_transaction_management()
raise
transaction.leave_transaction_management()
return response

View File

@ -1,5 +1,6 @@
"""Default tags used by the template system, available to all templates."""
import os
import sys
import re
from datetime import datetime
@ -309,6 +310,7 @@ class RegroupNode(Node):
return ''
def include_is_allowed(filepath):
filepath = os.path.abspath(filepath)
for root in settings.ALLOWED_INCLUDE_ROOTS:
if filepath.startswith(root):
return True

View File

@ -63,6 +63,7 @@ real_rollback = transaction.rollback
real_enter_transaction_management = transaction.enter_transaction_management
real_leave_transaction_management = transaction.leave_transaction_management
real_managed = transaction.managed
real_abort = transaction.abort
def nop(*args, **kwargs):
return
@ -73,6 +74,7 @@ def disable_transaction_methods():
transaction.enter_transaction_management = nop
transaction.leave_transaction_management = nop
transaction.managed = nop
transaction.abort = nop
def restore_transaction_methods():
transaction.commit = real_commit
@ -80,6 +82,7 @@ def restore_transaction_methods():
transaction.enter_transaction_management = real_enter_transaction_management
transaction.leave_transaction_management = real_leave_transaction_management
transaction.managed = real_managed
transaction.abort = real_abort
def assert_and_parse_html(self, html, user_msg, msg):
@ -1143,4 +1146,11 @@ class LiveServerTestCase(TransactionTestCase):
if hasattr(cls, 'server_thread'):
# Terminate the live server's thread
cls.server_thread.join()
# Restore sqlite connections' non-sharability
for conn in connections.all():
if (conn.settings_dict['ENGINE'] == 'django.db.backends.sqlite3'
and conn.settings_dict['NAME'] == ':memory:'):
conn.allow_thread_sharing = False
super(LiveServerTestCase, cls).tearDownClass()

View File

@ -3,6 +3,7 @@ from __future__ import with_statement
import warnings
from django.conf import settings, UserSettingsHolder
from django.core import mail
from django import http
from django.test.signals import template_rendered, setting_changed
from django.template import Template, loader, TemplateDoesNotExist
from django.template.loaders import cached
@ -69,12 +70,18 @@ def setup_test_environment():
- Set the email backend to the locmem email backend.
- Setting the active locale to match the LANGUAGE_CODE setting.
"""
Template.original_render = Template._render
Template._original_render = Template._render
Template._render = instrumented_test_render
mail.original_email_backend = settings.EMAIL_BACKEND
# Storing previous values in the settings module itself is problematic.
# Store them in arbitrary (but related) modules instead. See #20636.
mail._original_email_backend = settings.EMAIL_BACKEND
settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
http._original_allowed_hosts = settings.ALLOWED_HOSTS
settings.ALLOWED_HOSTS = ['*']
mail.outbox = []
deactivate()
@ -87,11 +94,14 @@ def teardown_test_environment():
- Restoring the email sending functions
"""
Template._render = Template.original_render
del Template.original_render
Template._render = Template._original_render
del Template._original_render
settings.EMAIL_BACKEND = mail.original_email_backend
del mail.original_email_backend
settings.EMAIL_BACKEND = mail._original_email_backend
del mail._original_email_backend
settings.ALLOWED_HOSTS = http._original_allowed_hosts
del http._original_allowed_hosts
del mail.outbox

View File

@ -106,21 +106,6 @@ def _long_to_bin(x, hex_format_string):
return binascii.unhexlify(hex_format_string % x)
def _fast_hmac(key, msg, digest):
"""
A trimmed down version of Python's HMAC implementation
"""
dig1, dig2 = digest(), digest()
if len(key) > dig1.block_size:
key = digest(key).digest()
key += chr(0) * (dig1.block_size - len(key))
dig1.update(key.translate(_trans_36))
dig1.update(msg)
dig2.update(key.translate(_trans_5c))
dig2.update(dig1.digest())
return dig2
def pbkdf2(password, salt, iterations, dklen=0, digest=None):
"""
Implements PBKDF2 as defined in RFC 2898, section 5.2
@ -146,11 +131,21 @@ def pbkdf2(password, salt, iterations, dklen=0, digest=None):
hex_format_string = "%%0%ix" % (hlen * 2)
inner, outer = digest(), digest()
if len(password) > inner.block_size:
password = digest(password).digest()
password += '\x00' * (inner.block_size - len(password))
inner.update(password.translate(_trans_36))
outer.update(password.translate(_trans_5c))
def F(i):
def U():
u = salt + struct.pack('>I', i)
for j in xrange(int(iterations)):
u = _fast_hmac(password, u, digest).digest()
dig1, dig2 = inner.copy(), outer.copy()
dig1.update(u)
dig2.update(dig1.digest())
u = dig2.digest()
yield _bin_to_long(u)
return _long_to_bin(reduce(operator.xor, U()), hex_format_string)

View File

@ -19,8 +19,11 @@ class datetime(real_datetime):
def strftime(self, fmt):
return strftime(self, fmt)
def combine(self, date, time):
return datetime(date.year, date.month, date.day, time.hour, time.minute, time.microsecond, time.tzinfo)
@classmethod
def combine(cls, date, time):
return cls(date.year, date.month, date.day,
time.hour, time.minute, time.second,
time.microsecond, time.tzinfo)
def date(self):
return date(self.year, self.month, self.day)

View File

@ -183,3 +183,15 @@ try:
codecs.lookup(DEFAULT_LOCALE_ENCODING)
except:
DEFAULT_LOCALE_ENCODING = 'ascii'
# Forwards compatibility with Django 1.5
def python_2_unicode_compatible(klass):
# Always use the Python 2 branch of the decorator here in Django 1.4
klass.__unicode__ = klass.__str__
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
return klass
smart_text = smart_unicode
force_text = force_unicode
smart_bytes = smart_str

View File

@ -1,98 +1,108 @@
import HTMLParser as _HTMLParser
import re
import sys
current_version = sys.version_info
class HTMLParser(_HTMLParser.HTMLParser):
"""
Patched version of stdlib's HTMLParser with patch from:
http://bugs.python.org/issue670664
"""
def __init__(self):
_HTMLParser.HTMLParser.__init__(self)
self.cdata_tag = None
use_workaround = (
(current_version < (2, 7, 3)) or
(current_version >= (3, 0) and current_version < (3, 2, 3))
)
def set_cdata_mode(self, tag):
try:
self.interesting = _HTMLParser.interesting_cdata
except AttributeError:
self.interesting = re.compile(r'</\s*%s\s*>' % tag.lower(), re.I)
self.cdata_tag = tag.lower()
if not use_workaround:
HTMLParser = _HTMLParser.HTMLParser
else:
class HTMLParser(_HTMLParser.HTMLParser):
"""
Patched version of stdlib's HTMLParser with patch from:
http://bugs.python.org/issue670664
"""
def __init__(self):
_HTMLParser.HTMLParser.__init__(self)
self.cdata_tag = None
def clear_cdata_mode(self):
self.interesting = _HTMLParser.interesting_normal
self.cdata_tag = None
def set_cdata_mode(self, tag):
try:
self.interesting = _HTMLParser.interesting_cdata
except AttributeError:
self.interesting = re.compile(r'</\s*%s\s*>' % tag.lower(), re.I)
self.cdata_tag = tag.lower()
# Internal -- handle starttag, return end or -1 if not terminated
def parse_starttag(self, i):
self.__starttag_text = None
endpos = self.check_for_whole_start_tag(i)
if endpos < 0:
return endpos
rawdata = self.rawdata
self.__starttag_text = rawdata[i:endpos]
def clear_cdata_mode(self):
self.interesting = _HTMLParser.interesting_normal
self.cdata_tag = None
# Now parse the data between i+1 and j into a tag and attrs
attrs = []
match = _HTMLParser.tagfind.match(rawdata, i + 1)
assert match, 'unexpected call to parse_starttag()'
k = match.end()
self.lasttag = tag = rawdata[i + 1:k].lower()
# Internal -- handle starttag, return end or -1 if not terminated
def parse_starttag(self, i):
self.__starttag_text = None
endpos = self.check_for_whole_start_tag(i)
if endpos < 0:
return endpos
rawdata = self.rawdata
self.__starttag_text = rawdata[i:endpos]
while k < endpos:
m = _HTMLParser.attrfind.match(rawdata, k)
if not m:
break
attrname, rest, attrvalue = m.group(1, 2, 3)
if not rest:
attrvalue = None
elif attrvalue[:1] == '\'' == attrvalue[-1:] or \
attrvalue[:1] == '"' == attrvalue[-1:]:
attrvalue = attrvalue[1:-1]
attrvalue = self.unescape(attrvalue)
attrs.append((attrname.lower(), attrvalue))
k = m.end()
# Now parse the data between i+1 and j into a tag and attrs
attrs = []
match = _HTMLParser.tagfind.match(rawdata, i + 1)
assert match, 'unexpected call to parse_starttag()'
k = match.end()
self.lasttag = tag = rawdata[i + 1:k].lower()
end = rawdata[k:endpos].strip()
if end not in (">", "/>"):
lineno, offset = self.getpos()
if "\n" in self.__starttag_text:
lineno = lineno + self.__starttag_text.count("\n")
offset = len(self.__starttag_text) \
- self.__starttag_text.rfind("\n")
while k < endpos:
m = _HTMLParser.attrfind.match(rawdata, k)
if not m:
break
attrname, rest, attrvalue = m.group(1, 2, 3)
if not rest:
attrvalue = None
elif attrvalue[:1] == '\'' == attrvalue[-1:] or \
attrvalue[:1] == '"' == attrvalue[-1:]:
attrvalue = attrvalue[1:-1]
attrvalue = self.unescape(attrvalue)
attrs.append((attrname.lower(), attrvalue))
k = m.end()
end = rawdata[k:endpos].strip()
if end not in (">", "/>"):
lineno, offset = self.getpos()
if "\n" in self.__starttag_text:
lineno = lineno + self.__starttag_text.count("\n")
offset = len(self.__starttag_text) \
- self.__starttag_text.rfind("\n")
else:
offset = offset + len(self.__starttag_text)
self.error("junk characters in start tag: %r"
% (rawdata[k:endpos][:20],))
if end.endswith('/>'):
# XHTML-style empty tag: <span attr="value" />
self.handle_startendtag(tag, attrs)
else:
offset = offset + len(self.__starttag_text)
self.error("junk characters in start tag: %r"
% (rawdata[k:endpos][:20],))
if end.endswith('/>'):
# XHTML-style empty tag: <span attr="value" />
self.handle_startendtag(tag, attrs)
else:
self.handle_starttag(tag, attrs)
if tag in self.CDATA_CONTENT_ELEMENTS:
self.set_cdata_mode(tag) # <--------------------------- Changed
return endpos
self.handle_starttag(tag, attrs)
if tag in self.CDATA_CONTENT_ELEMENTS:
self.set_cdata_mode(tag) # <--------------------------- Changed
return endpos
# Internal -- parse endtag, return end or -1 if incomplete
def parse_endtag(self, i):
rawdata = self.rawdata
assert rawdata[i:i + 2] == "</", "unexpected call to parse_endtag"
match = _HTMLParser.endendtag.search(rawdata, i + 1) # >
if not match:
return -1
j = match.end()
match = _HTMLParser.endtagfind.match(rawdata, i) # </ + tag + >
if not match:
if self.cdata_tag is not None: # *** add ***
self.handle_data(rawdata[i:j]) # *** add ***
return j # *** add ***
self.error("bad end tag: %r" % (rawdata[i:j],))
# --- changed start ---------------------------------------------------
tag = match.group(1).strip()
if self.cdata_tag is not None:
if tag.lower() != self.cdata_tag:
self.handle_data(rawdata[i:j])
return j
# --- changed end -----------------------------------------------------
self.handle_endtag(tag.lower())
self.clear_cdata_mode()
return j
# Internal -- parse endtag, return end or -1 if incomplete
def parse_endtag(self, i):
rawdata = self.rawdata
assert rawdata[i:i + 2] == "</", "unexpected call to parse_endtag"
match = _HTMLParser.endendtag.search(rawdata, i + 1) # >
if not match:
return -1
j = match.end()
match = _HTMLParser.endtagfind.match(rawdata, i) # </ + tag + >
if not match:
if self.cdata_tag is not None: # *** add ***
self.handle_data(rawdata[i:j]) # *** add ***
return j # *** add ***
self.error("bad end tag: %r" % (rawdata[i:j],))
# --- changed start ---------------------------------------------------
tag = match.group(1).strip()
if self.cdata_tag is not None:
if tag.lower() != self.cdata_tag:
self.handle_data(rawdata[i:j])
return j
# --- changed end -----------------------------------------------------
self.handle_endtag(tag.lower())
self.clear_cdata_mode()
return j

View File

@ -4,6 +4,7 @@ import re
import sys
import urllib
import urlparse
import unicodedata
from email.utils import formatdate
from django.utils.datastructures import MultiValueDict
@ -224,3 +225,35 @@ else:
"""
p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2)
return p1[0:2] == p2[0:2]
def is_safe_url(url, host=None):
"""
Return ``True`` if the url is a safe redirection (i.e. it doesn't point to
a different host and uses a safe scheme).
Always returns ``False`` on an empty url.
"""
if url is not None:
url = url.strip()
if not url:
return False
# Chrome treats \ completely as /
url = url.replace('\\', '/')
# Chrome considers any URL with more than two slashes to be absolute, but
# urlaprse is not so flexible. Treat any url with three slashes as unsafe.
if url.startswith('///'):
return False
url_info = urlparse.urlparse(url)
# Forbid URLs like http:///example.com - with a scheme, but without a hostname.
# In that URL, example.com is not the hostname but, a path component. However,
# Chrome will still consider example.com to be the hostname, so we must not
# allow this syntax.
if not url_info[1] and url_info[0]:
return False
# Forbid URLs that start with control characters. Some browsers (like
# Chrome) ignore quite a few control characters at the start of a
# URL and might consider the URL as scheme relative.
if unicodedata.category(unicode(url[0]))[0] == 'C':
return False
return (not url_info[1] or url_info[1] == host) and \
(not url_info[0] or url_info[0] in ['http', 'https'])

View File

@ -117,3 +117,9 @@ def mark_for_escaping(s):
return EscapeUnicode(s)
return EscapeString(str(s))
# Forwards compatibility with Django 1.5
EscapeBytes = EscapeString
EscapeText = EscapeUnicode
SafeBytes = SafeString
SafeText = SafeUnicode

788
django/utils/six.py Normal file
View File

@ -0,0 +1,788 @@
"""Utilities for writing code that runs on Python 2 and 3"""
# Copyright (c) 2010-2014 Benjamin Peterson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from __future__ import absolute_import
import functools
import operator
import sys
import types
__author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.8.0"
# Useful for very coarse version differentiation.
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY3:
string_types = str,
integer_types = int,
class_types = type,
text_type = str
binary_type = bytes
MAXSIZE = sys.maxsize
else:
string_types = basestring,
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
if sys.platform.startswith("java"):
# Jython always uses 32 bits.
MAXSIZE = int((1 << 31) - 1)
else:
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object):
def __len__(self):
return 1 << 31
try:
len(X())
except OverflowError:
# 32-bit
MAXSIZE = int((1 << 31) - 1)
else:
# 64-bit
MAXSIZE = int((1 << 63) - 1)
del X
def _add_doc(func, doc):
"""Add documentation to a function."""
func.__doc__ = doc
def _import_module(name):
"""Import module, returning the module after the last dot."""
__import__(name)
return sys.modules[name]
class _LazyDescr(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, tp):
result = self._resolve()
setattr(obj, self.name, result) # Invokes __set__.
# This is a bit ugly, but it avoids running this again.
delattr(obj.__class__, self.name)
return result
class MovedModule(_LazyDescr):
def __init__(self, name, old, new=None):
super(MovedModule, self).__init__(name)
if PY3:
if new is None:
new = name
self.mod = new
else:
self.mod = old
def _resolve(self):
return _import_module(self.mod)
def __getattr__(self, attr):
_module = self._resolve()
value = getattr(_module, attr)
setattr(self, attr, value)
return value
class _LazyModule(types.ModuleType):
def __init__(self, name):
super(_LazyModule, self).__init__(name)
self.__doc__ = self.__class__.__doc__
def __dir__(self):
attrs = ["__doc__", "__name__"]
attrs += [attr.name for attr in self._moved_attributes]
return attrs
# Subclasses should override this
_moved_attributes = []
class MovedAttribute(_LazyDescr):
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
super(MovedAttribute, self).__init__(name)
if PY3:
if new_mod is None:
new_mod = name
self.mod = new_mod
if new_attr is None:
if old_attr is None:
new_attr = name
else:
new_attr = old_attr
self.attr = new_attr
else:
self.mod = old_mod
if old_attr is None:
old_attr = name
self.attr = old_attr
def _resolve(self):
module = _import_module(self.mod)
return getattr(module, self.attr)
class _SixMetaPathImporter(object):
"""
A meta path importer to import six.moves and its submodules.
This class implements a PEP302 finder and loader. It should be compatible
with Python 2.5 and all existing versions of Python3
"""
def __init__(self, six_module_name):
self.name = six_module_name
self.known_modules = {}
def _add_module(self, mod, *fullnames):
for fullname in fullnames:
self.known_modules[self.name + "." + fullname] = mod
def _get_module(self, fullname):
return self.known_modules[self.name + "." + fullname]
def find_module(self, fullname, path=None):
if fullname in self.known_modules:
return self
return None
def __get_module(self, fullname):
try:
return self.known_modules[fullname]
except KeyError:
raise ImportError("This loader does not know module " + fullname)
def load_module(self, fullname):
try:
# in case of a reload
return sys.modules[fullname]
except KeyError:
pass
mod = self.__get_module(fullname)
if isinstance(mod, MovedModule):
mod = mod._resolve()
else:
mod.__loader__ = self
sys.modules[fullname] = mod
return mod
def is_package(self, fullname):
"""
Return true, if the named module is a package.
We need this method to get correct spec objects with
Python 3.4 (see PEP451)
"""
return hasattr(self.__get_module(fullname), "__path__")
def get_code(self, fullname):
"""Return None
Required, if is_package is implemented"""
self.__get_module(fullname) # eventually raises ImportError
return None
get_source = get_code # same as get_code
_importer = _SixMetaPathImporter(__name__)
class _MovedItems(_LazyModule):
"""Lazy loading of moved objects"""
__path__ = [] # mark as package
_moved_attributes = [
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
MovedAttribute("intern", "__builtin__", "sys"),
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
MovedAttribute("reduce", "__builtin__", "functools"),
MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
MovedAttribute("StringIO", "StringIO", "io"),
MovedAttribute("UserDict", "UserDict", "collections"),
MovedAttribute("UserList", "UserList", "collections"),
MovedAttribute("UserString", "UserString", "collections"),
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
MovedModule("builtins", "__builtin__"),
MovedModule("configparser", "ConfigParser"),
MovedModule("copyreg", "copy_reg"),
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
MovedModule("http_cookies", "Cookie", "http.cookies"),
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
MovedModule("html_parser", "HTMLParser", "html.parser"),
MovedModule("http_client", "httplib", "http.client"),
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
MovedModule("cPickle", "cPickle", "pickle"),
MovedModule("queue", "Queue"),
MovedModule("reprlib", "repr"),
MovedModule("socketserver", "SocketServer"),
MovedModule("_thread", "thread", "_thread"),
MovedModule("tkinter", "Tkinter"),
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
MovedModule("tkinter_colorchooser", "tkColorChooser",
"tkinter.colorchooser"),
MovedModule("tkinter_commondialog", "tkCommonDialog",
"tkinter.commondialog"),
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
"tkinter.simpledialog"),
MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
MovedModule("winreg", "_winreg"),
]
for attr in _moved_attributes:
setattr(_MovedItems, attr.name, attr)
if isinstance(attr, MovedModule):
_importer._add_module(attr, "moves." + attr.name)
del attr
_MovedItems._moved_attributes = _moved_attributes
moves = _MovedItems(__name__ + ".moves")
_importer._add_module(moves, "moves")
class Module_six_moves_urllib_parse(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_parse"""
_urllib_parse_moved_attributes = [
MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
MovedAttribute("urljoin", "urlparse", "urllib.parse"),
MovedAttribute("urlparse", "urlparse", "urllib.parse"),
MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
MovedAttribute("quote", "urllib", "urllib.parse"),
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
MovedAttribute("unquote", "urllib", "urllib.parse"),
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
MovedAttribute("urlencode", "urllib", "urllib.parse"),
MovedAttribute("splitquery", "urllib", "urllib.parse"),
MovedAttribute("splittag", "urllib", "urllib.parse"),
MovedAttribute("splituser", "urllib", "urllib.parse"),
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
MovedAttribute("uses_query", "urlparse", "urllib.parse"),
MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
]
for attr in _urllib_parse_moved_attributes:
setattr(Module_six_moves_urllib_parse, attr.name, attr)
del attr
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
"moves.urllib_parse", "moves.urllib.parse")
class Module_six_moves_urllib_error(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_error"""
_urllib_error_moved_attributes = [
MovedAttribute("URLError", "urllib2", "urllib.error"),
MovedAttribute("HTTPError", "urllib2", "urllib.error"),
MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
]
for attr in _urllib_error_moved_attributes:
setattr(Module_six_moves_urllib_error, attr.name, attr)
del attr
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
"moves.urllib_error", "moves.urllib.error")
class Module_six_moves_urllib_request(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_request"""
_urllib_request_moved_attributes = [
MovedAttribute("urlopen", "urllib2", "urllib.request"),
MovedAttribute("install_opener", "urllib2", "urllib.request"),
MovedAttribute("build_opener", "urllib2", "urllib.request"),
MovedAttribute("pathname2url", "urllib", "urllib.request"),
MovedAttribute("url2pathname", "urllib", "urllib.request"),
MovedAttribute("getproxies", "urllib", "urllib.request"),
MovedAttribute("Request", "urllib2", "urllib.request"),
MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
MovedAttribute("FileHandler", "urllib2", "urllib.request"),
MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
MovedAttribute("urlretrieve", "urllib", "urllib.request"),
MovedAttribute("urlcleanup", "urllib", "urllib.request"),
MovedAttribute("URLopener", "urllib", "urllib.request"),
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
]
for attr in _urllib_request_moved_attributes:
setattr(Module_six_moves_urllib_request, attr.name, attr)
del attr
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
"moves.urllib_request", "moves.urllib.request")
class Module_six_moves_urllib_response(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_response"""
_urllib_response_moved_attributes = [
MovedAttribute("addbase", "urllib", "urllib.response"),
MovedAttribute("addclosehook", "urllib", "urllib.response"),
MovedAttribute("addinfo", "urllib", "urllib.response"),
MovedAttribute("addinfourl", "urllib", "urllib.response"),
]
for attr in _urllib_response_moved_attributes:
setattr(Module_six_moves_urllib_response, attr.name, attr)
del attr
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
"moves.urllib_response", "moves.urllib.response")
class Module_six_moves_urllib_robotparser(_LazyModule):
"""Lazy loading of moved objects in six.moves.urllib_robotparser"""
_urllib_robotparser_moved_attributes = [
MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
]
for attr in _urllib_robotparser_moved_attributes:
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
del attr
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
"moves.urllib_robotparser", "moves.urllib.robotparser")
class Module_six_moves_urllib(types.ModuleType):
"""Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
__path__ = [] # mark as package
parse = _importer._get_module("moves.urllib_parse")
error = _importer._get_module("moves.urllib_error")
request = _importer._get_module("moves.urllib_request")
response = _importer._get_module("moves.urllib_response")
robotparser = _importer._get_module("moves.urllib_robotparser")
def __dir__(self):
return ['parse', 'error', 'request', 'response', 'robotparser']
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
"moves.urllib")
def add_move(move):
"""Add an item to six.moves."""
setattr(_MovedItems, move.name, move)
def remove_move(name):
"""Remove item from six.moves."""
try:
delattr(_MovedItems, name)
except AttributeError:
try:
del moves.__dict__[name]
except KeyError:
raise AttributeError("no such move, %r" % (name,))
if PY3:
_meth_func = "__func__"
_meth_self = "__self__"
_func_closure = "__closure__"
_func_code = "__code__"
_func_defaults = "__defaults__"
_func_globals = "__globals__"
else:
_meth_func = "im_func"
_meth_self = "im_self"
_func_closure = "func_closure"
_func_code = "func_code"
_func_defaults = "func_defaults"
_func_globals = "func_globals"
try:
advance_iterator = next
except NameError:
def advance_iterator(it):
return it.next()
next = advance_iterator
try:
callable = callable
except NameError:
def callable(obj):
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
if PY3:
def get_unbound_function(unbound):
return unbound
create_bound_method = types.MethodType
Iterator = object
else:
def get_unbound_function(unbound):
return unbound.im_func
def create_bound_method(func, obj):
return types.MethodType(func, obj, obj.__class__)
class Iterator(object):
def next(self):
return type(self).__next__(self)
callable = callable
_add_doc(get_unbound_function,
"""Get the function out of a possibly unbound function""")
get_method_function = operator.attrgetter(_meth_func)
get_method_self = operator.attrgetter(_meth_self)
get_function_closure = operator.attrgetter(_func_closure)
get_function_code = operator.attrgetter(_func_code)
get_function_defaults = operator.attrgetter(_func_defaults)
get_function_globals = operator.attrgetter(_func_globals)
if PY3:
def iterkeys(d, **kw):
return iter(d.keys(**kw))
def itervalues(d, **kw):
return iter(d.values(**kw))
def iteritems(d, **kw):
return iter(d.items(**kw))
def iterlists(d, **kw):
return iter(d.lists(**kw))
else:
def iterkeys(d, **kw):
return iter(d.iterkeys(**kw))
def itervalues(d, **kw):
return iter(d.itervalues(**kw))
def iteritems(d, **kw):
return iter(d.iteritems(**kw))
def iterlists(d, **kw):
return iter(d.iterlists(**kw))
_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
_add_doc(iteritems,
"Return an iterator over the (key, value) pairs of a dictionary.")
_add_doc(iterlists,
"Return an iterator over the (key, [values]) pairs of a dictionary.")
if PY3:
def b(s):
return s.encode("latin-1")
def u(s):
return s
unichr = chr
if sys.version_info[1] <= 1:
def int2byte(i):
return bytes((i,))
else:
# This is about 2x faster than the implementation above on 3.2+
int2byte = operator.methodcaller("to_bytes", 1, "big")
byte2int = operator.itemgetter(0)
indexbytes = operator.getitem
iterbytes = iter
import io
StringIO = io.StringIO
BytesIO = io.BytesIO
else:
def b(s):
return s
# Workaround for standalone backslash
def u(s):
return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
unichr = unichr
int2byte = chr
def byte2int(bs):
return ord(bs[0])
def indexbytes(buf, i):
return ord(buf[i])
def iterbytes(buf):
return (ord(byte) for byte in buf)
import StringIO
StringIO = BytesIO = StringIO.StringIO
_add_doc(b, """Byte literal""")
_add_doc(u, """Text literal""")
if PY3:
exec_ = getattr(moves.builtins, "exec")
def reraise(tp, value, tb=None):
if value is None:
value = tp()
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
else:
def exec_(_code_, _globs_=None, _locs_=None):
"""Execute code in a namespace."""
if _globs_ is None:
frame = sys._getframe(1)
_globs_ = frame.f_globals
if _locs_ is None:
_locs_ = frame.f_locals
del frame
elif _locs_ is None:
_locs_ = _globs_
exec("""exec _code_ in _globs_, _locs_""")
exec_("""def reraise(tp, value, tb=None):
raise tp, value, tb
""")
print_ = getattr(moves.builtins, "print", None)
if print_ is None:
def print_(*args, **kwargs):
"""The new-style print function for Python 2.4 and 2.5."""
fp = kwargs.pop("file", sys.stdout)
if fp is None:
return
def write(data):
if not isinstance(data, basestring):
data = str(data)
# If the file has an encoding, encode unicode with it.
if (isinstance(fp, file) and
isinstance(data, unicode) and
fp.encoding is not None):
errors = getattr(fp, "errors", None)
if errors is None:
errors = "strict"
data = data.encode(fp.encoding, errors)
fp.write(data)
want_unicode = False
sep = kwargs.pop("sep", None)
if sep is not None:
if isinstance(sep, unicode):
want_unicode = True
elif not isinstance(sep, str):
raise TypeError("sep must be None or a string")
end = kwargs.pop("end", None)
if end is not None:
if isinstance(end, unicode):
want_unicode = True
elif not isinstance(end, str):
raise TypeError("end must be None or a string")
if kwargs:
raise TypeError("invalid keyword arguments to print()")
if not want_unicode:
for arg in args:
if isinstance(arg, unicode):
want_unicode = True
break
if want_unicode:
newline = unicode("\n")
space = unicode(" ")
else:
newline = "\n"
space = " "
if sep is None:
sep = space
if end is None:
end = newline
for i, arg in enumerate(args):
if i:
write(sep)
write(arg)
write(end)
_add_doc(reraise, """Reraise an exception.""")
if sys.version_info[0:2] < (3, 4):
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
updated=functools.WRAPPER_UPDATES):
def wrapper(f):
f = functools.wraps(wrapped)(f)
f.__wrapped__ = wrapped
return f
return wrapper
else:
wraps = functools.wraps
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
# This requires a bit of explanation: the basic idea is to make a dummy
# metaclass for one level of class instantiation that replaces itself with
# the actual metaclass.
class metaclass(meta):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
def add_metaclass(metaclass):
"""Class decorator for creating a class with a metaclass."""
def wrapper(cls):
orig_vars = cls.__dict__.copy()
slots = orig_vars.get('__slots__')
if slots is not None:
if isinstance(slots, str):
slots = [slots]
for slots_var in slots:
orig_vars.pop(slots_var)
orig_vars.pop('__dict__', None)
orig_vars.pop('__weakref__', None)
return metaclass(cls.__name__, cls.__bases__, orig_vars)
return wrapper
# Complete the moves implementation.
# This code is at the end of this module to speed up module loading.
# Turn this module into a package.
__path__ = [] # required for PEP 302 and PEP 451
__package__ = __name__ # see PEP 366 @ReservedAssignment
if globals().get("__spec__") is not None:
__spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
# Remove other six meta path importers, since they cause problems. This can
# happen if six is removed from sys.modules and then reloaded. (Setuptools does
# this for some reason.)
if sys.meta_path:
for i, importer in enumerate(sys.meta_path):
# Here's some real nastiness: Another "instance" of the six module might
# be floating around. Therefore, we can't use isinstance() to check for
# the six meta path importer, since the other six instance will have
# inserted an importer with different class.
if (type(importer).__name__ == "_SixMetaPathImporter" and
importer.name == __name__):
del sys.meta_path[i]
break
del i, importer
# Finally, add the importer to the meta path import hook.
sys.meta_path.append(_importer)
### Additional customizations for Django ###
if PY3:
_assertRaisesRegex = "assertRaisesRegex"
_assertRegex = "assertRegex"
memoryview = memoryview
else:
_assertRaisesRegex = "assertRaisesRegexp"
_assertRegex = "assertRegexpMatches"
# memoryview and buffer are not stricly equivalent, but should be fine for
# django core usage (mainly BinaryField). However, Jython doesn't support
# buffer (see http://bugs.jython.org/issue1521), so we have to be careful.
if sys.platform.startswith('java'):
memoryview = memoryview
else:
memoryview = buffer
def assertRaisesRegex(self, *args, **kwargs):
return getattr(self, _assertRaisesRegex)(*args, **kwargs)
def assertRegex(self, *args, **kwargs):
return getattr(self, _assertRegex)(*args, **kwargs)

View File

@ -286,6 +286,39 @@ def compress_string(s):
ustring_re = re.compile(u"([\u0080-\uffff])")
# Backported from django 1.5
class StreamingBuffer(object):
def __init__(self):
self.vals = []
def write(self, val):
self.vals.append(val)
def read(self):
ret = ''.join(self.vals)
self.vals = []
return ret
def flush(self):
return
def close(self):
return
# Backported from django 1.5
# Like compress_string, but for iterators of strings.
def compress_sequence(sequence):
buf = StreamingBuffer()
zfile = GzipFile(mode='wb', compresslevel=6, fileobj=buf)
# Output headers...
yield buf.read()
for item in sequence:
zfile.write(item)
zfile.flush()
yield buf.read()
zfile.close()
yield buf.read()
def javascript_quote(s, quote_double_quotes=False):
def fix(match):

View File

@ -438,8 +438,8 @@ def blankout(src, char):
return dot_re.sub(char, src)
context_re = re.compile(r"""^\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?'))\s*""")
inline_re = re.compile(r"""^\s*trans\s+((?:"[^"]*?")|(?:'[^']*?'))(\s+.*context\s+(?:"[^"]*?")|(?:'[^']*?'))?\s*""")
block_re = re.compile(r"""^\s*blocktrans(\s+.*context\s+(?:"[^"]*?")|(?:'[^']*?'))?(?:\s+|$)""")
inline_re = re.compile(r"""^\s*trans\s+((?:"[^"]*?")|(?:'[^']*?'))(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?\s*""")
block_re = re.compile(r"""^\s*blocktrans(\s+.*context\s+((?:"[^"]*?")|(?:'[^']*?')))?(?:\s+|$)""")
endblock_re = re.compile(r"""^\s*endblocktrans$""")
plural_re = re.compile(r"""^\s*plural$""")
constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""")

View File

@ -155,9 +155,20 @@ class SafeExceptionReporterFilter(ExceptionReporterFilter):
Replaces the values of variables marked as sensitive with
stars (*********).
"""
func_name = tb_frame.f_code.co_name
func = tb_frame.f_globals.get(func_name)
sensitive_variables = getattr(func, 'sensitive_variables', [])
# Loop through the frame's callers to see if the sensitive_variables
# decorator was used.
current_frame = tb_frame.f_back
sensitive_variables = None
while current_frame is not None:
if (current_frame.f_code.co_name == 'sensitive_variables_wrapper'
and 'sensitive_variables_wrapper' in current_frame.f_locals):
# The sensitive_variables decorator was used, so we take note
# of the sensitive variables' names.
wrapper = current_frame.f_locals['sensitive_variables_wrapper']
sensitive_variables = getattr(wrapper, 'sensitive_variables', None)
break
current_frame = current_frame.f_back
cleansed = []
if self.is_active(request) and sensitive_variables:
if sensitive_variables == '__ALL__':

View File

@ -1,5 +1,7 @@
import functools
from django.http import HttpRequest
def sensitive_variables(*variables):
"""
@ -26,13 +28,13 @@ def sensitive_variables(*variables):
"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
def sensitive_variables_wrapper(*args, **kwargs):
if variables:
wrapper.sensitive_variables = variables
sensitive_variables_wrapper.sensitive_variables = variables
else:
wrapper.sensitive_variables = '__ALL__'
sensitive_variables_wrapper.sensitive_variables = '__ALL__'
return func(*args, **kwargs)
return wrapper
return sensitive_variables_wrapper
return decorator
@ -61,11 +63,15 @@ def sensitive_post_parameters(*parameters):
"""
def decorator(view):
@functools.wraps(view)
def wrapper(request, *args, **kwargs):
def sensitive_post_parameters_wrapper(request, *args, **kwargs):
assert isinstance(request, HttpRequest), (
"sensitive_post_parameters didn't receive an HttpRequest. If you "
"are decorating a classmethod, be sure to use @method_decorator."
)
if parameters:
request.sensitive_post_parameters = parameters
else:
request.sensitive_post_parameters = '__ALL__'
return view(request, *args, **kwargs)
return wrapper
return sensitive_post_parameters_wrapper
return decorator

View File

@ -8,6 +8,8 @@ from django.utils.translation import check_for_language, activate, to_locale, ge
from django.utils.text import javascript_quote
from django.utils.encoding import smart_unicode
from django.utils.formats import get_format_modules, get_format
from django.utils.http import is_safe_url
def set_language(request):
"""
@ -20,11 +22,11 @@ def set_language(request):
redirect to the page in the request (the 'next' parameter) without changing
any state.
"""
next = request.REQUEST.get('next', None)
if not next:
next = request.META.get('HTTP_REFERER', None)
if not next:
next = '/'
next = request.REQUEST.get('next')
if not is_safe_url(url=next, host=request.get_host()):
next = request.META.get('HTTP_REFERER')
if not is_safe_url(url=next, host=request.get_host()):
next = '/'
response = http.HttpResponseRedirect(next)
if request.method == 'POST':
lang_code = request.POST.get('language', None)

View File

@ -16,6 +16,9 @@ from django.template import loader, Template, Context, TemplateDoesNotExist
from django.utils.http import http_date, parse_http_date
from django.utils.translation import ugettext as _, ugettext_noop
STREAM_CHUNK_SIZE = 4096
def serve(request, path, document_root=None, show_indexes=False):
"""
Serve static files below a given point in the directory structure.
@ -59,8 +62,8 @@ def serve(request, path, document_root=None, show_indexes=False):
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
statobj.st_mtime, statobj.st_size):
return HttpResponseNotModified(mimetype=mimetype)
with open(fullpath, 'rb') as f:
response = HttpResponse(f.read(), mimetype=mimetype)
f = open(fullpath, 'rb')
response = HttpResponse(iter(lambda: f.read(STREAM_CHUNK_SIZE), ''), mimetype=mimetype)
response["Last-Modified"] = http_date(statobj.st_mtime)
if stat.S_ISREG(statobj.st_mode):
response["Content-Length"] = statobj.st_size

View File

@ -105,14 +105,22 @@ class DjangoHTMLTranslator(SmartyPantsHTMLTranslator):
# Don't use border=1, which docutils does by default.
def visit_table(self, node):
self.context.append(self.compact_p)
self.compact_p = True
self._table_row_index = 0 # Needed by Sphinx
self.body.append(self.starttag(node, 'table', CLASS='docutils'))
# <big>? Really?
def depart_table(self, node):
self.compact_p = self.context.pop()
self.body.append('</table>\n')
def visit_desc_parameterlist(self, node):
self.body.append('(')
self.body.append('(') # by default sphinx puts <big> around the "("
self.first_param = 1
self.optional_param_level = 0
self.param_separator = node.child_text_separator
self.required_params_left = sum([isinstance(c, addnodes.desc_parameter)
for c in node.children])
def depart_desc_parameterlist(self, node):
self.body.append(')')

View File

@ -17,6 +17,9 @@
{%- endmacro %}
{% block extrahead %}
{# When building htmlhelp (CHM format) disable JQuery inclusion, #}
{# as it causes problems in compiled CHM files. #}
{% if builder != "htmlhelp" %}
{{ super() }}
<script type="text/javascript" src="{{ pathto('templatebuiltins.js', 1) }}"></script>
<script type="text/javascript">
@ -51,6 +54,7 @@
});
})(jQuery);
</script>
{% endif %}
{% endblock %}
{% block document %}

View File

@ -1,7 +1,7 @@
/*** setup ***/
html { background:#092e20;}
body { font:12px/1.5 Verdana,sans-serif; background:#092e20; color: white;}
#custom-doc { width:76.54em;*width:74.69em;min-width:995px; max-width:100em; margin:auto; text-align:left; padding-top:16px; margin-top:0;}
#custom-doc { width:76.54em;*width:74.69em;min-width:995px; max-width:100em; margin:auto; text-align:left; padding-top:16px; margin-top:0;}
#hd { padding: 4px 0 12px 0; }
#bd { background:#234F32; }
#ft { color:#487858; font-size:90%; padding-bottom: 2em; }
@ -54,7 +54,7 @@ hr { color:#ccc; background-color:#ccc; height:1px; border:0; }
p, ul, dl { margin-top:.6em; margin-bottom:1em; padding-bottom: 0.1em;}
#yui-main div.yui-b img { max-width: 50em; margin-left: auto; margin-right: auto; display: block; }
caption { font-size:1em; font-weight:bold; margin-top:0.5em; margin-bottom:0.5em; margin-left: 2px; text-align: center; }
blockquote { padding: 0 1em; margin: 1em 0; font:125%/1.2em "Trebuchet MS", sans-serif; color:#234f32; border-left:2px solid #94da3a; }
blockquote { padding: 0 1em; margin: 1em 0; font:125%/1.2em "Trebuchet MS", sans-serif; color:#234f32; border-left:2px solid #94da3a; }
strong { font-weight: bold; }
em { font-style: italic; }
ins { font-weight: bold; text-decoration: none; }
@ -111,10 +111,11 @@ dt .literal, table .literal { background:none; }
.note, .admonition { padding-left:65px; background:url(docicons-note.png) .8em .8em no-repeat;}
div.admonition-philosophy { padding-left:65px; background:url(docicons-philosophy.png) .8em .8em no-repeat;}
div.admonition-behind-the-scenes { padding-left:65px; background:url(docicons-behindscenes.png) .8em .8em no-repeat;}
.admonition.warning { background:url(docicons-warning.png) .8em .8em no-repeat; border:1px solid #ffc83c;}
/*** versoinadded/changes ***/
div.versionadded, div.versionchanged { }
div.versionadded span.title, div.versionchanged span.title { font-weight: bold; }
div.versionadded span.title, div.versionchanged span.title, div.deprecated span.title { font-weight: bold; }
/*** p-links ***/
a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; visibility: hidden; }

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

View File

@ -50,9 +50,9 @@ copyright = 'Django Software Foundation and contributors'
# built documents.
#
# The short X.Y version.
version = '1.4'
version = '1.4.22'
# The full version, including alpha/beta/rc tags.
release = '1.4'
release = '1.4.22'
# The next version to be released
django_next_version = '1.5'

View File

@ -42,18 +42,15 @@ Yes. See :doc:`Integrating with a legacy database </howto/legacy-databases>`.
If I make changes to a model, how do I update the database?
-----------------------------------------------------------
If you don't mind clearing data, your project's ``manage.py`` utility has an
option to reset the SQL for a particular application::
manage.py reset appname
This drops any tables associated with ``appname`` and recreates them.
If you don't mind clearing data, your project's ``manage.py`` utility has a
:djadmin:`flush` option to reset the database to the state it was in
immediately after :djadmin:`syncdb` was executed.
If you do care about deleting data, you'll have to execute the ``ALTER TABLE``
statements manually in your database.
There are `external projects which handle schema updates
<http://djangopackages.com/grids/g/database-migration/>`_, of which the current
<http://www.djangopackages.com/grids/g/database-migration/>`_, of which the current
defacto standard is `south <http://south.aeracode.org/>`_.
Do Django models support multiple-column primary keys?

View File

@ -86,5 +86,13 @@ the provided filename into account. The ``name`` argument passed to this method
will have already cleaned to a filename valid for the storage system, according
to the ``get_valid_name()`` method described above.
The code provided on ``Storage`` simply appends ``"_1"``, ``"_2"``, etc. to the
filename until it finds one that's available in the destination directory.
.. versionchanged:: 1.4.14
If a file with ``name`` already exists, an underscore plus a random 7
character alphanumeric string is appended to the filename before the
extension.
Previously, an underscore followed by a number (e.g. ``"_1"``, ``"_2"``,
etc.) was appended to the filename until an available name in the destination
directory was found. A malicious user could exploit this deterministic
algorithm to create a denial-of-service attack.

View File

@ -120,7 +120,7 @@ default options such as :djadminopt:`--verbosity` and :djadminopt:`--traceback`.
class Command(BaseCommand):
...
self.can_import_settings = True
can_import_settings = True
def handle(self, *args, **options):

View File

@ -181,10 +181,10 @@ card values plus their suits; 104 characters in total.
Many of Django's model fields accept options that they don't do anything
with. For example, you can pass both
:attr:`~django.db.models.Field.editable` and
:attr:`~django.db.models.Field.auto_now` to a
:attr:`~django.db.models.DateField.auto_now` to a
:class:`django.db.models.DateField` and it will simply ignore the
:attr:`~django.db.models.Field.editable` parameter
(:attr:`~django.db.models.Field.auto_now` being set implies
(:attr:`~django.db.models.DateField.auto_now` being set implies
``editable=False``). No error is raised in this case.
This behavior simplifies the field classes, because they don't need to
@ -334,7 +334,6 @@ Once you have ``MytypeField``, you can use it in any model, just like any other
class Person(models.Model):
name = models.CharField(max_length=80)
gender = models.CharField(max_length=1)
something_else = MytypeField()
If you aim to build a database-agnostic application, you should account for
@ -448,6 +447,13 @@ called when it is created, you should be using `The SubfieldBase metaclass`_
mentioned earlier. Otherwise :meth:`.to_python` won't be called
automatically.
.. warning::
If your custom field allows ``null=True``, any field method that takes
``value`` as an argument, like :meth:`~Field.to_python` and
:meth:`~Field.get_prep_value`, should handle the case when ``value`` is
``None``.
Converting Python objects to query values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -476,6 +482,16 @@ For example::
return ''.join([''.join(l) for l in (value.north,
value.east, value.south, value.west)])
.. warning::
If your custom field uses the ``CHAR``, ``VARCHAR`` or ``TEXT``
types for MySQL, you must make sure that :meth:`.get_prep_value`
always returns a string type. MySQL performs flexible and unexpected
matching when a query is performed on these types and the provided
value is an integer, which can cause queries to include unexpected
objects in their results. This problem cannot occur if you always
return a string type from :meth:`.get_prep_value`.
Converting query values to database values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -525,8 +541,8 @@ for the first time, the ``add`` parameter will be ``True``, otherwise it will be
You only need to override this method if you want to preprocess the value
somehow, just before saving. For example, Django's
:class:`~django.db.models.DateTimeField` uses this method to set the attribute
correctly in the case of :attr:`~django.db.models.Field.auto_now` or
:attr:`~django.db.models.Field.auto_now_add`.
correctly in the case of :attr:`~django.db.models.DateField.auto_now` or
:attr:`~django.db.models.DateField.auto_now_add`.
If you do override this method, you must return the value of the attribute at
the end. You should also update the model's attribute if you make any changes
@ -685,7 +701,7 @@ data storage anyway, we can reuse some existing conversion code::
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_db_prep_value(value)
return self.get_prep_value(value)
Some general advice
--------------------

View File

@ -18,8 +18,8 @@ The `official mod_wsgi documentation`_ is fantastic; it's your source for all
the details about how to use mod_wsgi. You'll probably want to start with the
`installation and configuration documentation`_.
.. _official mod_wsgi documentation: http://www.modwsgi.org/
.. _installation and configuration documentation: http://www.modwsgi.org/wiki/InstallationInstructions
.. _official mod_wsgi documentation: http://code.google.com/p/modwsgi/
.. _installation and configuration documentation: http://code.google.com/p/modwsgi/wiki/InstallationInstructions
Basic configuration
===================
@ -61,10 +61,10 @@ Using a virtualenv
If you install your project's Python dependencies inside a `virtualenv`_,
you'll need to add the path to this virtualenv's ``site-packages`` directory to
your Python path as well. To do this, you can add another line to your
Apache configuration::
your Python path as well. To do this, add an additional path to your
`WSGIPythonPath` directive, with multiple paths separated by a colon::
WSGIPythonPath /path/to/your/venv/lib/python2.X/site-packages
WSGIPythonPath /path/to/mysite.com:/path/to/your/venv/lib/python2.X/site-packages
Make sure you give the correct path to your virtualenv, and replace
``python2.X`` with the correct Python version (e.g. ``python2.7``).
@ -75,12 +75,20 @@ Using mod_wsgi daemon mode
==========================
"Daemon mode" is the recommended mode for running mod_wsgi (on non-Windows
platforms). See the `official mod_wsgi documentation`_ for details on setting
up daemon mode. The only change required to the above configuration if you use
daemon mode is that you can't use ``WSGIPythonPath``; instead you should use
the ``python-path`` option to ``WSGIDaemonProcess``, for example::
platforms). To create the required daemon process group and delegate the
Django instance to run in it, you will need to add appropriate
``WSGIDaemonProcess`` and ``WSGIProcessGroup`` directives. A further change
required to the above configuration if you use daemon mode is that you can't
use ``WSGIPythonPath``; instead you should use the ``python-path`` option to
``WSGIDaemonProcess``, for example::
WSGIDaemonProcess example.com python-path=/path/to/mysite.com:/path/to/venv/lib/python2.7/site-packages
WSGIProcessGroup example.com
See the official mod_wsgi documentation for `details on setting up daemon
mode`_.
.. _details on setting up daemon mode: http://code.google.com/p/modwsgi/wiki/QuickConfigurationGuide#Delegation_To_Daemon_Process
.. _serving-files:

View File

@ -47,7 +47,7 @@ uWSGI supports multiple ways to configure the process. See uWSGI's
Here's an example command to start a uWSGI server::
uwsgi --chdir=/path/to/your/project
--module='mysite.wsgi:application' \
--module=mysite.wsgi:application \
--env DJANGO_SETTINGS_MODULE=mysite.settings \
--master --pidfile=/tmp/project-master.pid \
--socket=127.0.0.1:49152 \ # can also be a file
@ -81,7 +81,7 @@ Example ini configuration file::
[uwsgi]
chdir=/path/to/your/project
module='mysite.wsgi:application'
module=mysite.wsgi:application
master=True
pidfile=/tmp/project-master.pid
vacuum=True
@ -93,6 +93,6 @@ Example ini configuration file usage::
uwsgi --ini uwsgi.ini
See the uWSGI docs on `managing the uWSGI process`_ for information on
starting, stoping and reloading the uWSGI workers.
starting, stopping and reloading the uWSGI workers.
.. _managing the uWSGI process: http://projects.unbit.it/uwsgi/wiki/Management

View File

@ -123,6 +123,8 @@ Filtering error reports
Filtering sensitive information
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. currentmodule:: django.views.decorators.debug
Error reports are really helpful for debugging errors, so it is generally
useful to record as much relevant information about those errors as possible.
For example, by default Django records the `full traceback`_ for the
@ -236,11 +238,13 @@ attribute::
request.exception_reporter_filter = CustomExceptionReporterFilter()
...
.. currentmodule:: django.views.debug
Your custom filter class needs to inherit from
:class:`django.views.debug.SafeExceptionReporterFilter` and may override the
following methods:
.. class:: django.views.debug.SafeExceptionReporterFilter
.. class:: SafeExceptionReporterFilter
.. method:: SafeExceptionReporterFilter.is_active(self, request)

View File

@ -163,4 +163,4 @@ Backend-specific SQL data is executed before non-backend-specific SQL
data. For example, if your app contains the files ``sql/person.sql``
and ``sql/person.sqlite3.sql`` and you're installing the app on
SQLite, Django will execute the contents of
``sql/person.sqlite.sql`` first, then ``sql/person.sql``.
``sql/person.sqlite3.sql`` first, then ``sql/person.sql``.

View File

@ -4,70 +4,7 @@ Running Django on Jython
.. index:: Jython, Java, JVM
Jython_ is an implementation of Python that runs on the Java platform (JVM).
Django runs cleanly on Jython version 2.5 or later, which means you can deploy
Django on any Java platform.
This document will get you up and running with Django on top of Jython.
.. _jython: http://www.jython.org/
Installing Jython
=================
Django works with Jython versions 2.5b3 and higher. Download Jython at
http://www.jython.org/.
Creating a servlet container
============================
If you just want to experiment with Django, skip ahead to the next section;
Django includes a lightweight Web server you can use for testing, so you won't
need to set up anything else until you're ready to deploy Django in production.
If you want to use Django on a production site, use a Java servlet container,
such as `Apache Tomcat`_. Full JavaEE applications servers such as `GlassFish`_
or `JBoss`_ are also OK, if you need the extra features they include.
.. _`Apache Tomcat`: http://tomcat.apache.org/
.. _GlassFish: https://glassfish.dev.java.net/
.. _JBoss: http://www.jboss.org/
Installing Django
=================
The next step is to install Django itself. This is exactly the same as
installing Django on standard Python, so see
:ref:`removing-old-versions-of-django` and :ref:`install-django-code` for
instructions.
Installing Jython platform support libraries
============================================
The `django-jython`_ project contains database backends and management commands
for Django/Jython development. Note that the builtin Django backends won't work
on top of Jython.
`django-jython`_ supports Django 1.7. Please use that version of the
documentation for details.
.. _`django-jython`: http://code.google.com/p/django-jython/
To install it, follow the `installation instructions`_ detailed on the project
Web site. Also, read the `database backends`_ documentation there.
.. _`installation instructions`: http://code.google.com/p/django-jython/wiki/Install
.. _`database backends`: http://code.google.com/p/django-jython/wiki/DatabaseBackends
Differences with Django on Jython
=================================
.. index:: JYTHONPATH
At this point, Django on Jython should behave nearly identically to Django
running on standard Python. However, are a few differences to keep in mind:
* Remember to use the ``jython`` command instead of ``python``. The
documentation uses ``python`` for consistency, but if you're using Jython
you'll want to mentally replace ``python`` with ``jython`` every time it
occurs.
* Similarly, you'll need to use the ``JYTHONPATH`` environment variable
instead of ``PYTHONPATH``.

View File

@ -21,7 +21,7 @@ Here's an example::
def some_view(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(mimetype='text/csv')
response['Content-Disposition'] = 'attachment; filename=somefilename.csv'
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
writer = csv.writer(response)
writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
@ -93,7 +93,7 @@ Here's an example, which generates the same CSV file as above::
def some_view(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(mimetype='text/csv')
response['Content-Disposition'] = 'attachment; filename=somefilename.csv'
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
# The data is hard-coded here, but you could load it from a database or
# some other source.

Some files were not shown because too many files have changed in this diff Show More