1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +00:00

multi-auth: Merged to [3085]

git-svn-id: http://code.djangoproject.com/svn/django/branches/multi-auth@3086 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Joseph Kocherhans 2006-06-06 01:21:49 +00:00
parent 0c341d780e
commit e976ed1f79
78 changed files with 908 additions and 519 deletions

View File

@ -203,6 +203,16 @@ DATETIME_FORMAT = 'N j, Y, P'
# http://www.djangoproject.com/documentation/templates/#now
TIME_FORMAT = 'P'
# Default formatting for date objects when only the year and month are relevant.
# See all available format strings here:
# http://www.djangoproject.com/documentation/templates/#now
YEAR_MONTH_FORMAT = 'F Y'
# Default formatting for date objects when only the month and day are relevant.
# See all available format strings here:
# http://www.djangoproject.com/documentation/templates/#now
MONTH_DAY_FORMAT = 'F j'
# Whether to enable Psyco, which optimizes Python code. Requires Psyco.
# http://psyco.sourceforge.net/
ENABLE_PSYCO = False

View File

@ -72,7 +72,7 @@ msgstr "Dit veld mag niet leeg zijn."
#: db/models/fields/__init__.py:468 core/validators.py:132
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format."
msgstr "Geef geldige datum/tijd in JJJJ-MM-DD HH:MM formaat."
msgstr "Geef geldige datum/tijd in JJJJ-MM-DD UU:MM formaat."
#: db/models/fields/__init__.py:562
msgid "Enter a valid filename."
@ -116,7 +116,7 @@ msgstr "Frans"
#: conf/global_settings.py:46
msgid "Galician"
msgstr "Galisisch"
msgstr "Galicisch"
#: conf/global_settings.py:47
msgid "Hungarian"
@ -176,7 +176,7 @@ msgstr "Zweeds"
#: conf/global_settings.py:61
msgid "Ukrainian"
msgstr "Ukraiens"
msgstr "Oekraïens"
#: conf/global_settings.py:62
msgid "Simplified Chinese"
@ -220,11 +220,11 @@ msgstr "Geef een geldig IP adres op."
#: core/validators.py:103
msgid "Empty values are not allowed here."
msgstr "Lege waarden niet toegestaan."
msgstr "Lege waarden zijn hier niet toegestaan."
#: core/validators.py:107
msgid "Non-numeric characters aren't allowed here."
msgstr "Niet-numerieke karakters niet toegestaan."
msgstr "Niet-numerieke karakters zijn hier niet toegestaan."
#: core/validators.py:111
msgid "This value can't be comprised solely of digits."
@ -244,7 +244,7 @@ msgstr "Geef een geldige datum in JJJJ-MM-DD formaat."
#: core/validators.py:128
msgid "Enter a valid time in HH:MM format."
msgstr "Geef een geldige tijd in HH:MM formaat."
msgstr "Geef een geldige tijd in UU:MM formaat."
#: core/validators.py:136
msgid "Enter a valid e-mail address."
@ -321,7 +321,7 @@ msgstr "Dit veld moet overeenkomen met het '%s' veld."
#: core/validators.py:255
msgid "Please enter something for at least one field."
msgstr "Geef in minimaal één veld een waarde."
msgstr "Voer tenminste één veld in."
#: core/validators.py:264 core/validators.py:275
msgid "Please enter both fields or leave them both empty."
@ -355,26 +355,26 @@ msgstr "Geef een geldig decimaal getal."
msgid "Please enter a valid decimal number with at most %s total digit."
msgid_plural ""
"Please enter a valid decimal number with at most %s total digits."
msgstr[0] "Geef een geldig decimaal getal met maximaal %s cijfer."
msgstr[1] "Geef een geldig decimaal getal met maximaal %s cijfers."
msgstr[0] "Geef een geldig decimaal getal met hooguit %s cijfer."
msgstr[1] "Geef een geldig decimaal getal met hooguit %s cijfers."
#: core/validators.py:352
#, python-format
msgid "Please enter a valid decimal number with at most %s decimal place."
msgid_plural ""
"Please enter a valid decimal number with at most %s decimal places."
msgstr[0] "Geef een decimaal getal met maximaal %s cijfer achter de komma."
msgstr[1] "Geef een decimaal getal met maximaal %s cijfers achter de komma."
msgstr[0] "Geef een decimaal getal met hooguit %s cijfer achter de komma."
msgstr[1] "Geef een decimaal getal met hooguit %s cijfers achter de komma."
#: core/validators.py:362
#, python-format
msgid "Make sure your uploaded file is at least %s bytes big."
msgstr "Zorg ervoor dat het bestand minimaal %s bytes groot is."
msgstr "Zorg ervoor dat het bestand minstens %s bytes groot is."
#: core/validators.py:363
#, python-format
msgid "Make sure your uploaded file is at most %s bytes big."
msgstr "Zorg ervoor dat het bestand maximaal %s bytes groot is."
msgstr "Zorg ervoor dat het bestand hoogstens %s bytes groot is."
#: core/validators.py:376
msgid "The format for this field is wrong."
@ -456,7 +456,7 @@ msgid ""
"Your Web browser doesn't appear to have cookies enabled. Cookies are "
"required for logging in."
msgstr ""
"Het lijkt erop dat uw browser geen cookies accepteerd. Om u aan te melden "
"Het lijkt erop dat uw browser geen cookies accepteerd. Om aan te melden "
"moeten cookies worden geaccepteerd."
#: contrib/auth/forms.py:36 contrib/auth/forms.py:41
@ -465,7 +465,7 @@ msgid ""
"Please enter a correct username and password. Note that both fields are case-"
"sensitive."
msgstr ""
"Geef een correcte gebruikersnaam en wachtwoord. Let op de velden zijn "
"Voer een correcte gebruikersnaam en wachtwoord in. Let op, de velden zijn "
"hoofdletter-gevoelig."
#: contrib/auth/models.py:13 contrib/auth/models.py:26
@ -522,7 +522,7 @@ msgstr "staf status"
#: contrib/auth/models.py:60
msgid "Designates whether the user can log into this admin site."
msgstr "Bepaalt of de gebruiker kan inloggen op deze admin site"
msgstr "Bepaalt of de gebruiker kan inloggen op deze admin site."
#: contrib/auth/models.py:61
msgid "active"
@ -682,7 +682,7 @@ msgstr "Gebruikersnamen mogen geen '@' bevatten."
#: contrib/admin/views/decorators.py:84
#, python-format
msgid "Your e-mail address is not your username. Try '%s' instead."
msgstr "Uw e-mail adres is niet uw gebruikersnaam. Probeer '%s' eens."
msgstr "Uw e-mailadres is niet uw gebruikersnaam. Probeer '%s' eens."
#: contrib/admin/views/main.py:226
msgid "Site administration"
@ -734,13 +734,13 @@ msgstr "Geen velden gewijzigd."
#: contrib/admin/views/main.py:346
#, python-format
msgid "The %(name)s \"%(obj)s\" was changed successfully."
msgstr "Wijzigen %(name)s \"%(obj)s\" is geslaagd."
msgstr "Het wijzigen van %(name)s \"%(obj)s\" is geslaagd."
#: contrib/admin/views/main.py:354
#, python-format
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr "De %(name)s \"%(obj)s\" toegevoegd. U kunt het hieronder wijzigen."
msgstr "De %(name)s \"%(obj)s\" was toegevoegd. U kunt het hieronder wijzigen."
#: contrib/admin/views/main.py:392
#, python-format
@ -760,7 +760,7 @@ msgstr "Een of meer %(fieldname)s in %(name)s:"
#: contrib/admin/views/main.py:508
#, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
msgstr "Verwijdering %(name)s \"%(obj)s\" is geslaagd."
msgstr "De verwijdering van %(name)s \"%(obj)s\" is geslaagd."
#: contrib/admin/views/main.py:511
msgid "Are you sure?"
@ -769,7 +769,7 @@ msgstr "Weet u het zeker?"
#: contrib/admin/views/main.py:533
#, python-format
msgid "Change history: %s"
msgstr "Wijzigingshistorie: %s"
msgstr "Wijzigingsgeschiedenis: %s"
#: contrib/admin/views/main.py:565
#, python-format
@ -794,7 +794,7 @@ msgstr "Boolean (True of False)"
#: contrib/admin/views/doc.py:279 contrib/admin/views/doc.py:296
#, python-format
msgid "String (up to %(maxlength)s)"
msgstr "Karakterreeks (maximaal %(maxlength)s)"
msgstr "Karakterreeks (hooguit %(maxlength)s)"
#: contrib/admin/views/doc.py:280
msgid "Comma-separated integers"
@ -939,9 +939,9 @@ msgid ""
"objects, but your account doesn't have permission to delete the following "
"types of objects:"
msgstr ""
"Verwijderen van %(object_name)s '%(object)s' zal ook gerelateerde objecten "
"verwijderen. Echter u heeft geen rechten om de volgende typen objecten te "
"verwijderen:"
"Het verwijderen van %(object_name)s '%(object)s' zal ook gerelateerde "
"objecten verwijderen. Echter u heeft geen rechten om de volgende typen "
"objecten te verwijderen:"
#: contrib/admin/templates/admin/delete_confirmation.html:21
#, python-format
@ -963,7 +963,7 @@ msgstr "Pagina niet gevonden"
#: contrib/admin/templates/admin/404.html:10
msgid "We're sorry, but the requested page could not be found."
msgstr "De gevraagde pagina komt niet voor."
msgstr "Onze excuses, maar de gevraagde pagina komt niet voor."
#: contrib/admin/templates/admin/change_form.html:15
#: contrib/admin/templates/admin/index.html:28
@ -1073,8 +1073,8 @@ msgid ""
"This object doesn't have a change history. It probably wasn't added via this "
"admin site."
msgstr ""
"Dit object heeft geen geschiedenis. Mogelijk niet via de admin site "
"toegevoegd."
"Dit object heeft geen wijzigingsgeschiedenis. Het is mogelijk niet via de "
"admin site toegevoegd."
#: contrib/admin/templates/admin/500.html:4
msgid "Server error"
@ -1093,9 +1093,8 @@ msgid ""
"There's been an error. It's been reported to the site administrators via e-"
"mail and should be fixed shortly. Thanks for your patience."
msgstr ""
"Er is een fout opgetreden. Dit is inmiddels doorgegevens aan de "
"sitebeheerder via e-mail en zal binnenkort worden gerepareerd. Bedankt voor "
"uw geduld"
"Er is een fout opgetreden. Dit is inmiddels doorgegeven aan de sitebeheerder "
"via e-mail en zal spoedig worden gerepareerd. Bedankt voor uw geduld."
#: contrib/admin/templates/admin/search_form.html:8
msgid "Go"
@ -1188,7 +1187,7 @@ msgstr "Bewerk dit object (nieuwe pagina)"
#: contrib/admin/templates/admin_doc/bookmarklets.html:29
msgid "As above, but opens the admin page in a new window."
msgstr "Als boven, maar opent de beheerpagina in een nieuw venster."
msgstr "Zoals hierboven, maar opent de beheerpagina in een nieuw venster."
#: contrib/admin/templates/widget/date_time.html:3
msgid "Date:"
@ -1218,8 +1217,8 @@ msgid ""
"Forgotten your password? Enter your e-mail address below, and we'll reset "
"your password and e-mail the new one to you."
msgstr ""
"Wachtwoord vergeten? Geef u e-mail adres op en we herstellen uw wachtwoord "
"en zullen u het nieuwe wachtwoord per e-mail toesturen."
"Uw wachtwoord vergeten? Geef uw e-mailadres op en er zal een nieuw "
"wachtwoord worden toegekend en aan u worden toegezonden."
#: contrib/admin/templates/registration/password_reset_form.html:16
msgid "E-mail address:"
@ -1231,7 +1230,7 @@ msgstr "Herstel mijn wachtwoord"
#: contrib/admin/templates/registration/password_reset_email.html:2
msgid "You're receiving this e-mail because you requested a password reset"
msgstr "U krijgt een e-mail omdat u om een nieuw wachtwoord heeft gevraagd"
msgstr "U krijgt deze e-mail omdat u om een nieuw wachtwoord heeft gevraagd"
#: contrib/admin/templates/registration/password_reset_email.html:3
#, python-format
@ -1278,8 +1277,7 @@ msgid ""
"We've e-mailed a new password to the e-mail address you submitted. You "
"should be receiving it shortly."
msgstr ""
"Een nieuw wachtwoord is per e-mail verstuurd. U zult het binnenkort "
"ontvangen."
"Een nieuw wachtwoord is per e-mail verstuurd. U zult het spoedig ontvangen."
#: contrib/admin/templates/registration/password_change_form.html:4
#: contrib/admin/templates/registration/password_change_form.html:6
@ -1293,8 +1291,8 @@ msgid ""
"Please enter your old password, for security's sake, and then enter your new "
"password twice so we can verify you typed it in correctly."
msgstr ""
"Geef voor de veiligheid uw oude wachtwoord op en twee keer een nieuw "
"wachtwoord, zodat we kunnen controleren of er geen typefouten zijn gemaakt."
"Vanwege de beveiliging moet u uw oude en twee keer een nieuw wachtwoord"
"invoeren, zodat we kunnen controleren of er geen typefouten zijn gemaakt."
#: contrib/admin/templates/registration/password_change_form.html:17
msgid "Old password:"
@ -1340,7 +1338,8 @@ msgstr "sites"
#: contrib/flatpages/models.py:8
msgid ""
"Example: '/about/contact/'. Make sure to have leading and trailing slashes."
msgstr "Voorbeeld: '/about/contact/'. Zorg voor slashes aan begin en eind"
msgstr ""
"Voorbeeld: '/about/contact/'. Zorg voor slashes aan het begin en eind."
#: contrib/flatpages/models.py:9
msgid "title"
@ -1373,8 +1372,8 @@ msgstr "registratie verplicht"
#: contrib/flatpages/models.py:14
msgid "If this is checked, only logged-in users will be able to view the page."
msgstr ""
"Alleen ingelogde gebruikers kunnen deze pagina zien, indien dit is "
"aangekruist."
"Indien dit is aangekruist kunnen alleen ingelogde gebruikers deze pagina "
"bekijken."
#: contrib/flatpages/models.py:18
msgid "flat page"
@ -1405,8 +1404,8 @@ msgid ""
"This can be either an absolute path (as above) or a full URL starting with "
"'http://'."
msgstr ""
"Dit kan een absoluut pad zijn (zoals boven) of een volledige URL beginnend "
"met 'http://'."
"Dit kan een absoluut pad (zoals hierboven) zijn of een volledige URL "
"beginnend met 'http://'."
#: contrib/redirects/models.py:12
msgid "redirect"
@ -1471,7 +1470,7 @@ msgstr "datum/tijd toegevoegd"
#: contrib/comments/models.py:84 contrib/comments/models.py:170
msgid "is public"
msgstr "is publiek"
msgstr "is openbaar"
#: contrib/comments/models.py:86
msgid "is removed"
@ -1518,7 +1517,7 @@ msgstr "ip adres"
#: contrib/comments/models.py:173
msgid "approved by staff"
msgstr "goegekeurd door de staf"
msgstr "goedgekeurd door de staf"
#: contrib/comments/models.py:176
msgid "free comment"
@ -1604,14 +1603,14 @@ msgstr "Ongeldige opmerkingen ID"
#: contrib/comments/views/karma.py:25
msgid "No voting for yourself"
msgstr "Niet op jezelf stemmen"
msgstr "Niet op uzelf stemmen"
#: contrib/comments/views/comments.py:28
msgid ""
"This rating is required because you've entered at least one other rating."
msgstr ""
"Deze waardering is verplicht omdat je op zijn minst een andere waardering "
"hebt ingevoerd."
"Deze waardering is verplicht omdat u tenminste één andere waardering hebt "
"ingevoerd."
#: contrib/comments/views/comments.py:112
#, python-format
@ -1626,13 +1625,13 @@ msgid_plural ""
"\n"
"%(text)s"
msgstr[0] ""
"Deze opmerking is gepost door een gebruiker welke minder dan %(count)s opmerking "
"heeft gepost:\n"
"Deze opmerking is gepost door een gebruiker die minder dan %(count)s "
"opmerking heeft gepost:\n"
"\n"
"%(text)s"
msgstr[1] ""
"Deze opmerking is gepost door een gebruiker welke minder dan %(count)s opmerkingen "
"heeft gepost:\n"
"Deze opmerking is gepost door een gebruiker die minder dan %(count)s "
"opmerkingen heeft gepost:\n"
"\n"
"%(text)s"
@ -1655,7 +1654,7 @@ msgstr "Alleen POSTs zijn toegestaan"
#: contrib/comments/views/comments.py:193
#: contrib/comments/views/comments.py:284
msgid "One or more of the required fields wasn't submitted"
msgstr "Een of meerdere verplichte velden is niet ingevuld"
msgstr "Een of meerdere verplichte velden zijn niet ingevuld"
#: contrib/comments/views/comments.py:197
#: contrib/comments/views/comments.py:286
@ -1710,7 +1709,7 @@ msgstr "Concept opmerking"
#: contrib/comments/templates/comments/freeform.html:4
msgid "Your name:"
msgstr "Uw gebruikernaam:"
msgstr "Uw gebruikersnaam:"
#: contrib/sessions/models.py:35
msgid "session key"
@ -1738,18 +1737,18 @@ msgstr "python model-class-naam"
#: contrib/contenttypes/models.py:28
msgid "content type"
msgstr "inhoudtype"
msgstr "inhoudstype"
#: contrib/contenttypes/models.py:29
msgid "content types"
msgstr "inhoudtypen"
msgstr "inhoudstypen"
#: forms/__init__.py:380
#, python-format
msgid "Ensure your text is less than %s character."
msgid_plural "Ensure your text is less than %s characters."
msgstr[0] "Zorg ervoor dat uw tekst korter is dan %s teken."
msgstr[1] "Zorg ervoor dat uw tekst korter is dan %S tekens."
msgstr[0] "Zorg ervoor dat uw tekst korter is dan %s karakter."
msgstr[1] "Zorg ervoor dat uw tekst korter is dan %s karakters."
#: forms/__init__.py:385
msgid "Line breaks are not allowed here."
@ -1778,31 +1777,31 @@ msgstr "Geef een geheel getal op tussen 0 en 32.767."
#: utils/dates.py:6
msgid "Monday"
msgstr "Maandag"
msgstr "maandag"
#: utils/dates.py:6
msgid "Tuesday"
msgstr "Dinsdag"
msgstr "dinsdag"
#: utils/dates.py:6
msgid "Wednesday"
msgstr "Woensdag"
msgstr "woensdag"
#: utils/dates.py:6
msgid "Thursday"
msgstr "Donderdag"
msgstr "donderdag"
#: utils/dates.py:6
msgid "Friday"
msgstr "Vrijdag"
msgstr "vrijdag"
#: utils/dates.py:7
msgid "Saturday"
msgstr "Zaterdag"
msgstr "zaterdag"
#: utils/dates.py:7
msgid "Sunday"
msgstr "Zondag"
msgstr "zondag"
#: utils/dates.py:14
msgid "January"
@ -1890,7 +1889,7 @@ msgstr "sep"
#: utils/dates.py:20
msgid "oct"
msgstr "oct"
msgstr "okt"
#: utils/dates.py:20
msgid "nov"

View File

@ -1,7 +1,7 @@
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^login/$', 'django.contrib.auth.view.login'),
(r'^login/$', 'django.contrib.auth.views.login'),
(r'^logout/$', 'django.contrib.auth.views.logout'),
(r'^login_another/$', 'django.contrib.auth.views.logout_then_login'),

View File

@ -4,7 +4,7 @@
#changelist { position:relative; width:100%; }
#changelist table { width:100%; }
.change-list .filtered table { border-right:1px solid #ddd; }
.change-list .filtered { min-height:400px; _height:400px; }
.change-list .filtered { min-height:400px; }
.change-list .filtered { background:white url(../img/admin/changelist-bg.gif) top right repeat-y !important; }
.change-list .filtered table, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull { margin-right:160px !important; width:auto !important; }
.change-list .filtered table tbody th { padding-right:1em; }
@ -27,7 +27,7 @@
#changelist-filter { position:absolute; top:0; right:0; z-index:1000; width:160px; border-left:1px solid #ddd; background:#efefef; margin:0; }
#changelist-filter h2 { font-size:11px; padding:2px 5px; border-bottom:1px solid #ddd; }
#changelist-filter h3 { font-size:12px; margin-bottom:0; }
#changelist-filter ul { padding-left:0;margin-left:10px;_margin-right:-10px; }
#changelist-filter ul { padding-left:0;margin-left:10px; }
#changelist-filter li { list-style-type:none; margin-left:0; padding-left:0; }
#changelist-filter a { color:#999; }
#changelist-filter a:hover { color:#036; }

View File

@ -86,7 +86,7 @@ textarea { vertical-align:top !important; }
input[type=text], input[type=password], textarea, select, .vTextField { border:1px solid #ccc; }
/* FORM BUTTONS */
input[type=submit], input[type=button], .submit-row input { background:white url(../img/admin/nav-bg.gif) bottom repeat-x; padding:3px; color:black; }
input[type=submit], input[type=button], .submit-row input { background:white url(../img/admin/nav-bg.gif) bottom repeat-x; padding:3px; color:black; border:1px solid #bbb; border-color:#ddd #aaa #aaa #ddd; }
input[type=submit]:active, input[type=button]:active { background-image:url(../img/admin/nav-bg-reverse.gif); background-position:top; }
input[type=submit].default, .submit-row input.default { border:2px solid #5b80b2; background:#7CA0C7 url(../img/admin/default-bg.gif) bottom repeat-x; font-weight:bold; color:white; }
input[type=submit].default:active { background-image:url(../img/admin/default-bg-reverse.gif); background-position:top; }

View File

@ -3,13 +3,13 @@
#content { margin:10px 15px; }
#header { width:100%; }
#content-main { float:left; width:100%; }
#content-related { float:right; width:220px; position:relative; margin-right:-230px; }
#content-related { float:right; width:18em; position:relative; margin-right:-19em; }
#footer { clear:both; padding:10px; }
/* COLUMN TYPES */
.colMS { margin-right:245px !important; }
.colSM { margin-left:245px !important; }
.colSM #content-related { float:left; margin-right:0; margin-left:-230px; }
.colMS { margin-right:20em !important; }
.colSM { margin-left:20em !important; }
.colSM #content-related { float:left; margin-right:0; margin-left:-19em; }
.colSM #content-main { float:right; }
.popup .colM { width:95%; }
.subcol { float:left; width:46%; margin-right:15px; }

View File

@ -3,4 +3,6 @@
* html .colSM #content-related { margin-right:10px; margin-left:-115px; position:static; } /* put the left sidebars back on the page */
* html .form-row { height:1%; }
* html .dashboard #content { width:768px; } /* proper fixed width for dashboard in IE6 */
* html .dashboard #content-main { width:535px; } /* proper fixed width for dashboard in IE6 */
* html .dashboard #content-main { width:535px; } /* proper fixed width for dashboard in IE6 */
* html #changelist-filter ul { margin-right:-10px; } /* fix right margin for changelist filters in IE6 */
* html .change-list .filtered { height:400px; } /* IE ignores min-height, but treats height as if it were min-height */

View File

@ -3,6 +3,8 @@
function showRelatedObjectLookupPopup(triggeringLink) {
var name = triggeringLink.id.replace(/^lookup_/, '');
// IE doesn't like periods in the window name, so convert temporarily.
name = name.replace(/\./g, '___');
var href;
if (triggeringLink.href.search(/\?/) >= 0) {
href = triggeringLink.href + '&pop=1';
@ -15,11 +17,12 @@ function showRelatedObjectLookupPopup(triggeringLink) {
}
function dismissRelatedLookupPopup(win, chosenId) {
var elem = document.getElementById(win.name);
var name = win.name.replace(/___/g, '.');
var elem = document.getElementById(name);
if (elem.className.indexOf('vRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + chosenId;
} else {
document.getElementById(win.name).value = chosenId;
document.getElementById(name).value = chosenId;
}
win.close();
}

View File

@ -19,9 +19,9 @@
<div id="branding">
{% block branding %}{% endblock %}
</div>
{% if not user.is_anonymous %}
{% if not user.is_anonymous %}{% if user.is_staff %}
<div id="user-tools">{% trans 'Welcome,' %} <strong>{% if user.first_name %}{{ user.first_name }}{% else %}{{ user.username }}{% endif %}</strong>. {% block userlinks %}<a href="doc/">{% trans 'Documentation' %}</a> / <a href="password_change/">{% trans 'Change password' %}</a> / <a href="logout/">{% trans 'Log out' %}</a>{% endblock %}</div>
{% endif %}
{% endif %}{% endif %}
{% block nav-global %}{% endblock %}
</div>
<!-- END Header -->

View File

@ -2,14 +2,15 @@ from django import template
from django.conf import settings
from django.contrib.admin.views.main import MAX_SHOW_ALL_ALLOWED, ALL_VAR
from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR
from django.contrib.admin.views.main import IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE, MONTHS
from django.contrib.admin.views.main import IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils import dateformat
from django.utils.html import escape
from django.utils.text import capfirst
from django.utils.translation import get_date_formats
from django.utils.translation import get_date_formats, get_partial_date_formats
from django.template import Library
import datetime
register = Library()
@ -193,18 +194,19 @@ def date_hierarchy(cl):
year_lookup = cl.params.get(year_field)
month_lookup = cl.params.get(month_field)
day_lookup = cl.params.get(day_field)
year_month_format, month_day_format = get_partial_date_formats()
link = lambda d: cl.get_query_string(d, [field_generic])
if year_lookup and month_lookup and day_lookup:
month_name = MONTHS[int(month_lookup)]
day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup))
return {
'show': True,
'back': {
'link': link({year_field: year_lookup, month_field: month_lookup}),
'title': "%s %s" % (month_name, year_lookup)
'title': dateformat.format(day, year_month_format)
},
'choices': [{'title': "%s %s" % (month_name, day_lookup)}]
'choices': [{'title': dateformat.format(day, month_day_format)}]
}
elif year_lookup and month_lookup:
days = cl.query_set.filter(**{year_field: year_lookup, month_field: month_lookup}).dates(field_name, 'day')
@ -216,7 +218,7 @@ def date_hierarchy(cl):
},
'choices': [{
'link': link({year_field: year_lookup, month_field: month_lookup, day_field: day.day}),
'title': day.strftime('%B %d')
'title': dateformat.format(day, month_day_format)
} for day in days]
}
elif year_lookup:
@ -229,7 +231,7 @@ def date_hierarchy(cl):
},
'choices': [{
'link': link({year_field: year_lookup, month_field: month.month}),
'title': "%s %s" % (month.strftime('%B'), month.year)
'title': dateformat.format(month, year_month_format)
} for month in months]
}
else:

View File

@ -81,12 +81,16 @@ ROLES = {
}
def create_reference_role(rolename, urlbase):
def _role(name, rawtext, text, lineno, inliner, options={}, content=[]):
def _role(name, rawtext, text, lineno, inliner, options=None, content=None):
if options is None: options = {}
if content is None: content = []
node = docutils.nodes.reference(rawtext, text, refuri=(urlbase % (inliner.document.settings.link_base, text.lower())), **options)
return [node], []
docutils.parsers.rst.roles.register_canonical_role(rolename, _role)
def default_reference_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
def default_reference_role(name, rawtext, text, lineno, inliner, options=None, content=None):
if options is None: options = {}
if content is None: content = []
context = inliner.document.settings.default_reference_context
node = docutils.nodes.reference(rawtext, text, refuri=(ROLES[context] % (inliner.document.settings.link_base, text.lower())), **options)
return [node], []

View File

@ -579,7 +579,9 @@ class ChangeList(object):
filter_specs.append(spec)
return filter_specs, bool(filter_specs)
def get_query_string(self, new_params={}, remove=[]):
def get_query_string(self, new_params=None, remove=None):
if new_params is None: new_params = {}
if remove is None: remove = []
p = self.params.copy()
for r in remove:
for k in p.keys():

View File

@ -35,6 +35,8 @@ class AuthenticationForm(forms.Manipulator):
self.user_cache = authenticate(username=username, password=password)
if self.user_cache is None:
raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
elif not self.user_cache.is_active:
raise validators.ValidationError, _("This account is inactive.")
def get_user_id(self):
if self.user_cache:

View File

@ -0,0 +1,64 @@
from django import template
import re
register = template.Library()
def ordinal(value):
"""
Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd',
3 is '3rd', etc. Works for any integer.
"""
try:
value = int(value)
except ValueError:
return value
t = ('th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th')
if value % 100 in (11, 12, 13): # special case
return '%dth' % value
return '%d%s' % (value, t[value % 10])
register.filter(ordinal)
def intcomma(value):
"""
Converts an integer to a string containing commas every three digits.
For example, 3000 becomes '3,000' and 45000 becomes '45,000'.
"""
orig = str(value)
new = re.sub("^(-?\d+)(\d{3})", '\g<1>,\g<2>', str(value))
if orig == new:
return new
else:
return intcomma(new)
register.filter(intcomma)
def intword(value):
"""
Converts a large integer to a friendly text representation. Works best for
numbers over 1 million. For example, 1000000 becomes '1.0 million', 1200000
becomes '1.2 million' and '1200000000' becomes '1.2 billion'.
"""
value = int(value)
if value < 1000000:
return value
if value < 1000000000:
return '%.1f million' % (value / 1000000.0)
if value < 1000000000000:
return '%.1f billion' % (value / 1000000000.0)
if value < 1000000000000000:
return '%.1f trillion' % (value / 1000000000000.0)
return value
register.filter(intword)
def apnumber(value):
"""
For numbers 1-9, returns the number spelled out. Otherwise, returns the
number. This follows Associated Press style.
"""
try:
value = int(value)
except ValueError:
return value
if not 0 < value < 10:
return value
return ('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine')[value-1]
register.filter(apnumber)

View File

@ -1055,7 +1055,9 @@ def run_shell(use_plain=False):
# Don't bother loading IPython, because the user wants plain Python.
raise ImportError
import IPython
shell = IPython.Shell.IPShell()
# Explicitly pass an empty list as arguments, because otherwise IPython
# would use sys.argv from this script.
shell = IPython.Shell.IPShell(argv=[])
shell.mainloop()
except ImportError:
import code
@ -1137,7 +1139,11 @@ def print_error(msg, cmd):
sys.stderr.write(style.ERROR('Error: %s' % msg) + '\nRun "%s --help" for help.\n' % cmd)
sys.exit(1)
def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING):
def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
# Use sys.argv if we've not passed in a custom argv
if argv is None:
argv = sys.argv
# Parse the command-line arguments. optparse handles the dirty work.
parser = DjangoOptionParser(usage=get_usage(action_mapping), version=get_version())
parser.add_option('--settings',
@ -1146,7 +1152,7 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING):
help='Lets you manually add a directory the Python path, e.g. "/home/djangoprojects/myproject".')
parser.add_option('--plain', action='store_true', dest='plain',
help='Tells Django to use plain Python, not IPython, for "shell" command.')
options, args = parser.parse_args()
options, args = parser.parse_args(argv[1:])
# Take care of options.
if options.settings:
@ -1161,7 +1167,7 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING):
except IndexError:
parser.print_usage_and_exit()
if not action_mapping.has_key(action):
print_error("Your action, %r, was invalid." % action, sys.argv[0])
print_error("Your action, %r, was invalid." % action, argv[0])
# Switch to English, because django-admin.py creates database content
# like permissions, and those shouldn't contain any translations.
@ -1220,7 +1226,7 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING):
if action not in NO_SQL_TRANSACTION:
print style.SQL_KEYWORD("COMMIT;")
def execute_manager(settings_mod):
def execute_manager(settings_mod, argv=None):
# Add this project to sys.path so that it's importable in the conventional
# way. For example, if this file (manage.py) lives in a directory
# "myproject", this code would add "/path/to/myproject" to sys.path.
@ -1247,4 +1253,4 @@ def execute_manager(settings_mod):
action_mapping['startapp'].args = startapp.args
# Run the django-admin.py command.
execute_from_command_line(action_mapping)
execute_from_command_line(action_mapping, argv)

View File

@ -23,6 +23,25 @@ def get_mod_func(callback):
dot = callback.rindex('.')
return callback[:dot], callback[dot+1:]
def reverse_helper(regex, *args, **kwargs):
"""
Does a "reverse" lookup -- returns the URL for the given args/kwargs.
The args/kwargs are applied to the given compiled regular expression.
For example:
>>> reverse_helper(re.compile('^places/(\d+)/$'), 3)
'places/3/'
>>> reverse_helper(re.compile('^places/(?P<id>\d+)/$'), id=3)
'places/3/'
>>> reverse_helper(re.compile('^people/(?P<state>\w\w)/(\w+)/$'), 'adrian', state='il')
'people/il/adrian/'
Raises NoReverseMatch if the args/kwargs aren't valid for the regex.
"""
# TODO: Handle nested parenthesis in the following regex.
result = re.sub(r'\(([^)]+)\)', MatchChecker(args, kwargs), regex.pattern)
return result.replace('^', '').replace('$', '')
class MatchChecker(object):
"Class used in reverse RegexURLPattern lookup."
def __init__(self, args, kwargs):
@ -108,23 +127,7 @@ class RegexURLPattern:
return self.reverse_helper(*args, **kwargs)
def reverse_helper(self, *args, **kwargs):
"""
Does a "reverse" lookup -- returns the URL for the given args/kwargs.
The args/kwargs are applied to the regular expression in this
RegexURLPattern. For example:
>>> RegexURLPattern('^places/(\d+)/$').reverse_helper(3)
'places/3/'
>>> RegexURLPattern('^places/(?P<id>\d+)/$').reverse_helper(id=3)
'places/3/'
>>> RegexURLPattern('^people/(?P<state>\w\w)/(\w+)/$').reverse_helper('adrian', state='il')
'people/il/adrian/'
Raises NoReverseMatch if the args/kwargs aren't valid for the RegexURLPattern.
"""
# TODO: Handle nested parenthesis in the following regex.
result = re.sub(r'\(([^)]+)\)', MatchChecker(args, kwargs), self.regex.pattern)
return result.replace('^', '').replace('$', '')
return reverse_helper(self.regex, *args, **kwargs)
class RegexURLResolver(object):
def __init__(self, regex, urlconf_name):
@ -182,9 +185,19 @@ class RegexURLResolver(object):
def reverse(self, viewname, *args, **kwargs):
for pattern in self.urlconf_module.urlpatterns:
if pattern.callback == viewname:
if isinstance(pattern, RegexURLResolver):
try:
return pattern.reverse_helper(viewname, *args, **kwargs)
except NoReverseMatch:
continue
elif pattern.callback == viewname:
try:
return pattern.reverse_helper(*args, **kwargs)
except NoReverseMatch:
continue
raise NoReverseMatch
def reverse_helper(self, viewname, *args, **kwargs):
sub_match = self.reverse(viewname, *args, **kwargs)
result = reverse_helper(self.regex, *args, **kwargs)
return result + sub_match

View File

@ -399,7 +399,8 @@ class AnyValidator:
as a validation error. The message is rather unspecific, so it's best to
specify one on instantiation.
"""
def __init__(self, validator_list=[], error_message=gettext_lazy("This field is invalid.")):
def __init__(self, validator_list=None, error_message=gettext_lazy("This field is invalid.")):
if validator_list is None: validator_list = []
self.validator_list = validator_list
self.error_message = error_message
for v in validator_list:

View File

@ -125,6 +125,9 @@ def get_limit_offset_sql(limit, offset=None):
def get_random_function_sql():
return "RAND()"
def get_fulltext_search_sql(field_name):
raise NotImplementedError
def get_drop_foreignkey_sql():
return "DROP CONSTRAINT"

View File

@ -33,5 +33,6 @@ get_date_extract_sql = complain
get_date_trunc_sql = complain
get_limit_offset_sql = complain
get_random_function_sql = complain
get_fulltext_search_sql = complain
get_drop_foreignkey_sql = complain
OPERATOR_MAPPING = {}

View File

@ -152,6 +152,9 @@ def get_limit_offset_sql(limit, offset=None):
def get_random_function_sql():
return "RAND()"
def get_fulltext_search_sql(field_name):
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
def get_drop_foreignkey_sql():
return "DROP FOREIGN KEY"

View File

@ -65,11 +65,13 @@ class FormatStylePlaceholderCursor(Database.Cursor):
This fixes it -- but note that if you want to use a literal "%s" in a query,
you'll need to use "%%s".
"""
def execute(self, query, params=[]):
def execute(self, query, params=None):
if params is None: params = []
query = self.convert_arguments(query, len(params))
return Database.Cursor.execute(self, query, params)
def executemany(self, query, params=[]):
def executemany(self, query, params=None):
if params is None: params = []
query = self.convert_arguments(query, len(params[0]))
return Database.Cursor.executemany(self, query, params)
@ -106,6 +108,9 @@ def get_limit_offset_sql(limit, offset=None):
def get_random_function_sql():
return "DBMS_RANDOM.RANDOM"
def get_fulltext_search_sql(field_name):
raise NotImplementedError
def get_drop_foreignkey_sql():
return "DROP FOREIGN KEY"

View File

@ -102,6 +102,9 @@ def get_limit_offset_sql(limit, offset=None):
def get_random_function_sql():
return "RANDOM()"
def get_fulltext_search_sql(field_name):
raise NotImplementedError
def get_drop_foreignkey_sql():
return "DROP CONSTRAINT"

View File

@ -108,6 +108,9 @@ def get_limit_offset_sql(limit, offset=None):
def get_random_function_sql():
return "RANDOM()"
def get_fulltext_search_sql(field_name):
raise NotImplementedError
def get_drop_foreignkey_sql():
return "DROP CONSTRAINT"

View File

@ -124,6 +124,9 @@ def get_limit_offset_sql(limit, offset=None):
def get_random_function_sql():
return "RANDOM()"
def get_fulltext_search_sql(field_name):
raise NotImplementedError
def get_drop_foreignkey_sql():
return ""

View File

@ -162,7 +162,7 @@ class Field(object):
def get_db_prep_lookup(self, lookup_type, value):
"Returns field's value prepared for database lookup."
if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'year', 'month', 'day'):
if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'year', 'month', 'day', 'search'):
return [value]
elif lookup_type in ('range', 'in'):
return value

View File

@ -198,7 +198,7 @@ class ForeignRelatedObjectsDescriptor(object):
setattr(obj, rel_field.name, None)
obj.save()
else:
raise rel_field.rel.to.DoesNotExist, "'%s' is not related to '%s'." % (obj, instance)
raise rel_field.rel.to.DoesNotExist, "%r is not related to %r." % (obj, instance)
remove.alters_data = True
def clear(self):
@ -712,7 +712,7 @@ class ManyToManyRel:
self.related_name = related_name
self.filter_interface = filter_interface
if limit_choices_to is None:
limit_choices_to = {}
limit_choices_to = {}
self.limit_choices_to = limit_choices_to
self.edit_inline = False
self.raw_id_admin = raw_id_admin

View File

@ -615,6 +615,8 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
return "%s = %%s" % backend.get_date_extract_sql(lookup_type, table_prefix + field_name)
elif lookup_type == 'isnull':
return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or ''))
elif lookup_type == 'search':
return backend.get_fulltext_search_sql(table_prefix + field_name)
raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type)
def get_cached_row(klass, row, index_start):

View File

@ -367,7 +367,8 @@ class FormField:
class TextField(FormField):
input_type = "text"
def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=[], member_name=None):
def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=None, member_name=None):
if validator_list is None: validator_list = []
self.field_name = field_name
self.length, self.maxlength = length, maxlength
self.is_required = is_required
@ -404,7 +405,8 @@ class PasswordField(TextField):
input_type = "password"
class LargeTextField(TextField):
def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=[], maxlength=None):
def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=None, maxlength=None):
if validator_list is None: validator_list = []
self.field_name = field_name
self.rows, self.cols, self.is_required = rows, cols, is_required
self.validator_list = validator_list[:]
@ -422,7 +424,8 @@ class LargeTextField(TextField):
self.field_name, self.rows, self.cols, escape(data))
class HiddenField(FormField):
def __init__(self, field_name, is_required=False, validator_list=[]):
def __init__(self, field_name, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
self.field_name, self.is_required = field_name, is_required
self.validator_list = validator_list[:]
@ -452,7 +455,9 @@ class CheckboxField(FormField):
html2python = staticmethod(html2python)
class SelectField(FormField):
def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[], member_name=None):
def __init__(self, field_name, choices=None, size=1, is_required=False, validator_list=None, member_name=None):
if validator_list is None: validator_list = []
if choices is None: choices = []
self.field_name = field_name
# choices is a list of (value, human-readable key) tuples because order matters
self.choices, self.size, self.is_required = choices, size, is_required
@ -488,7 +493,9 @@ class NullSelectField(SelectField):
html2python = staticmethod(html2python)
class RadioSelectField(FormField):
def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[], member_name=None):
def __init__(self, field_name, choices=None, ul_class='', is_required=False, validator_list=None, member_name=None):
if validator_list is None: validator_list = []
if choices is None: choices = []
self.field_name = field_name
# choices is a list of (value, human-readable key) tuples because order matters
self.choices, self.is_required = choices, is_required
@ -552,7 +559,8 @@ class RadioSelectField(FormField):
class NullBooleanField(SelectField):
"This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None"
def __init__(self, field_name, is_required=False, validator_list=[]):
def __init__(self, field_name, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
SelectField.__init__(self, field_name, choices=[('1', 'Unknown'), ('2', 'Yes'), ('3', 'No')],
is_required=is_required, validator_list=validator_list)
@ -605,7 +613,9 @@ class CheckboxSelectMultipleField(SelectMultipleField):
back into the single list that validators, renderers and save() expect.
"""
requires_data_list = True
def __init__(self, field_name, choices=[], validator_list=[]):
def __init__(self, field_name, choices=None, validator_list=None):
if validator_list is None: validator_list = []
if choices is None: choices = []
SelectMultipleField.__init__(self, field_name, choices, size=1, is_required=False, validator_list=validator_list)
def prepare(self, new_data):
@ -636,7 +646,8 @@ class CheckboxSelectMultipleField(SelectMultipleField):
####################
class FileUploadField(FormField):
def __init__(self, field_name, is_required=False, validator_list=[]):
def __init__(self, field_name, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
self.field_name, self.is_required = field_name, is_required
self.validator_list = [self.isNonEmptyFile] + validator_list
@ -675,7 +686,8 @@ class ImageUploadField(FileUploadField):
####################
class IntegerField(TextField):
def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=[], member_name=None):
def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=None, member_name=None):
if validator_list is None: validator_list = []
validator_list = [self.isInteger] + validator_list
if member_name is not None:
self.member_name = member_name
@ -694,7 +706,8 @@ class IntegerField(TextField):
html2python = staticmethod(html2python)
class SmallIntegerField(IntegerField):
def __init__(self, field_name, length=5, maxlength=5, is_required=False, validator_list=[]):
def __init__(self, field_name, length=5, maxlength=5, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isSmallInteger] + validator_list
IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
@ -703,7 +716,8 @@ class SmallIntegerField(IntegerField):
raise validators.CriticalValidationError, gettext("Enter a whole number between -32,768 and 32,767.")
class PositiveIntegerField(IntegerField):
def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=[]):
def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isPositive] + validator_list
IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
@ -712,7 +726,8 @@ class PositiveIntegerField(IntegerField):
raise validators.CriticalValidationError, gettext("Enter a positive number.")
class PositiveSmallIntegerField(IntegerField):
def __init__(self, field_name, length=5, maxlength=None, is_required=False, validator_list=[]):
def __init__(self, field_name, length=5, maxlength=None, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isPositiveSmall] + validator_list
IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
@ -721,7 +736,8 @@ class PositiveSmallIntegerField(IntegerField):
raise validators.CriticalValidationError, gettext("Enter a whole number between 0 and 32,767.")
class FloatField(TextField):
def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=[]):
def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
self.max_digits, self.decimal_places = max_digits, decimal_places
validator_list = [self.isValidFloat] + validator_list
TextField.__init__(self, field_name, max_digits+1, max_digits+1, is_required, validator_list)
@ -746,7 +762,8 @@ class FloatField(TextField):
class DatetimeField(TextField):
"""A FormField that automatically converts its data to a datetime.datetime object.
The data should be in the format YYYY-MM-DD HH:MM:SS."""
def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=[]):
def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
self.field_name = field_name
self.length, self.maxlength = length, maxlength
self.is_required = is_required
@ -769,7 +786,8 @@ class DatetimeField(TextField):
class DateField(TextField):
"""A FormField that automatically converts its data to a datetime.date object.
The data should be in the format YYYY-MM-DD."""
def __init__(self, field_name, is_required=False, validator_list=[]):
def __init__(self, field_name, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isValidDate] + validator_list
TextField.__init__(self, field_name, length=10, maxlength=10,
is_required=is_required, validator_list=validator_list)
@ -793,7 +811,8 @@ class DateField(TextField):
class TimeField(TextField):
"""A FormField that automatically converts its data to a datetime.time object.
The data should be in the format HH:MM:SS or HH:MM:SS.mmmmmm."""
def __init__(self, field_name, is_required=False, validator_list=[]):
def __init__(self, field_name, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isValidTime] + validator_list
TextField.__init__(self, field_name, length=8, maxlength=8,
is_required=is_required, validator_list=validator_list)
@ -827,7 +846,8 @@ class TimeField(TextField):
class EmailField(TextField):
"A convenience FormField for validating e-mail addresses"
def __init__(self, field_name, length=50, maxlength=75, is_required=False, validator_list=[]):
def __init__(self, field_name, length=50, maxlength=75, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isValidEmail] + validator_list
TextField.__init__(self, field_name, length, maxlength=maxlength,
is_required=is_required, validator_list=validator_list)
@ -840,7 +860,8 @@ class EmailField(TextField):
class URLField(TextField):
"A convenience FormField for validating URLs"
def __init__(self, field_name, length=50, maxlength=200, is_required=False, validator_list=[]):
def __init__(self, field_name, length=50, maxlength=200, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isValidURL] + validator_list
TextField.__init__(self, field_name, length=length, maxlength=maxlength,
is_required=is_required, validator_list=validator_list)
@ -852,7 +873,8 @@ class URLField(TextField):
raise validators.CriticalValidationError, e.messages
class IPAddressField(TextField):
def __init__(self, field_name, length=15, maxlength=15, is_required=False, validator_list=[]):
def __init__(self, field_name, length=15, maxlength=15, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isValidIPAddress] + validator_list
TextField.__init__(self, field_name, length=length, maxlength=maxlength,
is_required=is_required, validator_list=validator_list)
@ -873,7 +895,7 @@ class IPAddressField(TextField):
class FilePathField(SelectField):
"A SelectField whose choices are the files in a given directory."
def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=[]):
def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=None):
import os
if match is not None:
import re
@ -896,7 +918,8 @@ class FilePathField(SelectField):
class PhoneNumberField(TextField):
"A convenience FormField for validating phone numbers (e.g. '630-555-1234')"
def __init__(self, field_name, is_required=False, validator_list=[]):
def __init__(self, field_name, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isValidPhone] + validator_list
TextField.__init__(self, field_name, length=12, maxlength=12,
is_required=is_required, validator_list=validator_list)
@ -909,7 +932,8 @@ class PhoneNumberField(TextField):
class USStateField(TextField):
"A convenience FormField for validating U.S. states (e.g. 'IL')"
def __init__(self, field_name, is_required=False, validator_list=[]):
def __init__(self, field_name, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isValidUSState] + validator_list
TextField.__init__(self, field_name, length=2, maxlength=2,
is_required=is_required, validator_list=validator_list)
@ -929,7 +953,8 @@ class USStateField(TextField):
class CommaSeparatedIntegerField(TextField):
"A convenience FormField for validating comma-separated integer fields"
def __init__(self, field_name, maxlength=None, is_required=False, validator_list=[]):
def __init__(self, field_name, maxlength=None, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
validator_list = [self.isCommaSeparatedIntegerList] + validator_list
TextField.__init__(self, field_name, length=20, maxlength=maxlength,
is_required=is_required, validator_list=validator_list)

View File

@ -227,7 +227,8 @@ class Parser(object):
for lib in builtins:
self.add_library(lib)
def parse(self, parse_until=[]):
def parse(self, parse_until=None):
if parse_until is None: parse_until = []
nodelist = self.create_nodelist()
while self.tokens:
token = self.next_token()

View File

@ -42,7 +42,8 @@ class MergeDict:
class SortedDict(dict):
"A dictionary that keeps its keys in the order in which they're inserted."
def __init__(self, data={}):
def __init__(self, data=None):
if data is None: data = {}
dict.__init__(self, data)
self.keyOrder = data.keys()
@ -123,8 +124,9 @@ class MultiValueDict(dict):
def __copy__(self):
return self.__class__(dict.items(self))
def __deepcopy__(self, memo={}):
def __deepcopy__(self, memo=None):
import copy
if memo is None: memo = {}
result = self.__class__()
memo[id(self)] = result
for key, value in dict.items(self):

View File

@ -221,10 +221,10 @@ def get_language_bidi():
False = left-to-right layout
True = right-to-left layout
"""
from django.conf import settings
return get_language() in settings.LANGUAGES_BIDI
def catalog():
"""
This function returns the current active catalog for further processing.
@ -369,7 +369,22 @@ def get_date_formats():
datetime_format = settings.DATETIME_FORMAT
if time_format == 'TIME_FORMAT':
time_format = settings.TIME_FORMAT
return (date_format, datetime_format, time_format)
return date_format, datetime_format, time_format
def get_partial_date_formats():
"""
This function checks whether translation files provide a translation for some
technical message ID to store partial date formats. If it doesn't contain
one, the formats provided in the settings will be used.
"""
from django.conf import settings
year_month_format = _('YEAR_MONTH_FORMAT')
month_day_format = _('MONTH_DAY_FORMAT')
if year_month_format == 'YEAR_MONTH_FORMAT':
year_month_format = settings.YEAR_MONTH_FORMAT
if month_day_format == 'MONTH_DAY_FORMAT':
month_day_format = settings.MONTH_DAY_FORMAT
return year_month_format, month_day_format
def install():
"""

View File

@ -5,8 +5,9 @@ Utilities for XML generation/parsing.
from xml.sax.saxutils import XMLGenerator
class SimplerXMLGenerator(XMLGenerator):
def addQuickElement(self, name, contents=None, attrs={}):
def addQuickElement(self, name, contents=None, attrs=None):
"Convenience method for adding an element with no children"
if attrs is None: attrs = {}
self.startElement(name, attrs)
if contents is not None:
self.characters(contents)

View File

@ -9,7 +9,7 @@ from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
def create_object(request, model, template_name=None,
template_loader=loader, extra_context={}, post_save_redirect=None,
template_loader=loader, extra_context=None, post_save_redirect=None,
login_required=False, follow=None, context_processors=None):
"""
Generic object-creation function.
@ -19,6 +19,7 @@ def create_object(request, model, template_name=None,
form
the form wrapper for the object
"""
if extra_context is None: extra_context = {}
if login_required and request.user.is_anonymous():
return redirect_to_login(request.path)
@ -71,7 +72,7 @@ def create_object(request, model, template_name=None,
def update_object(request, model, object_id=None, slug=None,
slug_field=None, template_name=None, template_loader=loader,
extra_context={}, post_save_redirect=None,
extra_context=None, post_save_redirect=None,
login_required=False, follow=None, context_processors=None,
template_object_name='object'):
"""
@ -84,6 +85,7 @@ def update_object(request, model, object_id=None, slug=None,
object
the original object being edited
"""
if extra_context is None: extra_context = {}
if login_required and request.user.is_anonymous():
return redirect_to_login(request.path)
@ -143,7 +145,7 @@ def update_object(request, model, object_id=None, slug=None,
def delete_object(request, model, post_delete_redirect,
object_id=None, slug=None, slug_field=None, template_name=None,
template_loader=loader, extra_context={},
template_loader=loader, extra_context=None,
login_required=False, context_processors=None, template_object_name='object'):
"""
Generic object-delete function.
@ -157,6 +159,7 @@ def delete_object(request, model, post_delete_redirect,
object
the original object being deleted
"""
if extra_context is None: extra_context = {}
if login_required and request.user.is_anonymous():
return redirect_to_login(request.path)

View File

@ -6,7 +6,7 @@ import datetime, time
def archive_index(request, queryset, date_field, num_latest=15,
template_name=None, template_loader=loader,
extra_context={}, allow_empty=False, context_processors=None,
extra_context=None, allow_empty=False, context_processors=None,
mimetype=None):
"""
Generic top-level archive of date-based objects.
@ -18,6 +18,7 @@ def archive_index(request, queryset, date_field, num_latest=15,
latest
Latest N (defaults to 15) objects by date
"""
if extra_context is None: extra_context = {}
model = queryset.model
queryset = queryset.filter(**{'%s__lte' % date_field: datetime.datetime.now()})
date_list = queryset.dates(date_field, 'year')[::-1]
@ -44,7 +45,7 @@ def archive_index(request, queryset, date_field, num_latest=15,
return HttpResponse(t.render(c), mimetype=mimetype)
def archive_year(request, year, queryset, date_field, template_name=None,
template_loader=loader, extra_context={}, allow_empty=False,
template_loader=loader, extra_context=None, allow_empty=False,
context_processors=None, template_object_name='object', mimetype=None,
make_object_list=False):
"""
@ -60,6 +61,7 @@ def archive_year(request, year, queryset, date_field, template_name=None,
List of objects published in the given month
(Only available if make_object_list argument is True)
"""
if extra_context is None: extra_context = {}
model = queryset.model
now = datetime.datetime.now()
@ -92,7 +94,7 @@ def archive_year(request, year, queryset, date_field, template_name=None,
def archive_month(request, year, month, queryset, date_field,
month_format='%b', template_name=None, template_loader=loader,
extra_context={}, allow_empty=False, context_processors=None,
extra_context=None, allow_empty=False, context_processors=None,
template_object_name='object', mimetype=None):
"""
Generic monthly archive view.
@ -108,6 +110,7 @@ def archive_month(request, year, month, queryset, date_field,
object_list:
list of objects published in the given month
"""
if extra_context is None: extra_context = {}
try:
date = datetime.date(*time.strptime(year+month, '%Y'+month_format)[:3])
except ValueError:
@ -148,7 +151,7 @@ def archive_month(request, year, month, queryset, date_field,
def archive_week(request, year, week, queryset, date_field,
template_name=None, template_loader=loader,
extra_context={}, allow_empty=True, context_processors=None,
extra_context=None, allow_empty=True, context_processors=None,
template_object_name='object', mimetype=None):
"""
Generic weekly archive view.
@ -160,6 +163,7 @@ def archive_week(request, year, week, queryset, date_field,
object_list:
list of objects published in the given week
"""
if extra_context is None: extra_context = {}
try:
date = datetime.date(*time.strptime(year+'-0-'+week, '%Y-%w-%U')[:3])
except ValueError:
@ -195,7 +199,7 @@ def archive_week(request, year, week, queryset, date_field,
def archive_day(request, year, month, day, queryset, date_field,
month_format='%b', day_format='%d', template_name=None,
template_loader=loader, extra_context={}, allow_empty=False,
template_loader=loader, extra_context=None, allow_empty=False,
context_processors=None, template_object_name='object',
mimetype=None):
"""
@ -212,6 +216,7 @@ def archive_day(request, year, month, day, queryset, date_field,
next_day
(datetime) the next day, or None if the current day is today
"""
if extra_context is None: extra_context = {}
try:
date = datetime.date(*time.strptime(year+month+day, '%Y'+month_format+day_format)[:3])
except ValueError:
@ -261,7 +266,7 @@ def archive_today(request, **kwargs):
def object_detail(request, year, month, day, queryset, date_field,
month_format='%b', day_format='%d', object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_context={}, context_processors=None,
template_loader=loader, extra_context=None, context_processors=None,
template_object_name='object', mimetype=None):
"""
Generic detail view from year/month/day/slug or year/month/day/id structure.
@ -271,6 +276,7 @@ def object_detail(request, year, month, day, queryset, date_field,
object:
the object to be detailed
"""
if extra_context is None: extra_context = {}
try:
date = datetime.date(*time.strptime(year+month+day, '%Y'+month_format+day_format)[:3])
except ValueError:

View File

@ -4,9 +4,9 @@ from django.core.xheaders import populate_xheaders
from django.core.paginator import ObjectPaginator, InvalidPage
from django.core.exceptions import ObjectDoesNotExist
def object_list(request, queryset, paginate_by=None, allow_empty=False,
template_name=None, template_loader=loader,
extra_context={}, context_processors=None, template_object_name='object',
def object_list(request, queryset, paginate_by=None, page=None,
allow_empty=False, template_name=None, template_loader=loader,
extra_context=None, context_processors=None, template_object_name='object',
mimetype=None):
"""
Generic list of objects.
@ -34,10 +34,12 @@ def object_list(request, queryset, paginate_by=None, allow_empty=False,
hits
number of objects, total
"""
if extra_context is None: extra_context = {}
queryset = queryset._clone()
if paginate_by:
paginator = ObjectPaginator(queryset, paginate_by)
page = request.GET.get('page', 1)
if not page:
page = request.GET.get('page', 1)
try:
page = int(page)
object_list = paginator.get_page(page - 1)
@ -78,7 +80,7 @@ def object_list(request, queryset, paginate_by=None, allow_empty=False,
def object_detail(request, queryset, object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_context={},
template_loader=loader, extra_context=None,
context_processors=None, template_object_name='object',
mimetype=None):
"""
@ -89,6 +91,7 @@ def object_detail(request, queryset, object_id=None, slug=None,
object
the object
"""
if extra_context is None: extra_context = {}
model = queryset.model
if object_id:
queryset = queryset.filter(pk=object_id)

View File

@ -45,6 +45,71 @@ See the `csrf documentation`_.
.. _csrf documentation: http://www.djangoproject.com/documentation/csrf/
humanize
========
A set of Django template filters useful for adding a "human touch" to data.
To activate these filters, add ``'django.contrib.english'`` to your
``INSTALLED_APPS`` setting. Once you've done that, use ``{% load english %}``
in a template, and you'll have access to these filters:
apnumber
--------
For numbers 1-9, returns the number spelled out. Otherwise, returns the
number. This follows Associated Press style.
Examples:
* ``1`` becomes ``'one'``.
* ``2`` becomes ``'two'``.
* ``10`` becomes ``10``.
You can pass in either an integer or a string representation of an integer.
intcomma
--------
Converts an integer to a string containing commas every three digits.
Examples:
* ``4500`` becomes ``'4,500'``.
* ``45000`` becomes ``'45,000'``.
* ``450000`` becomes ``'450,000'``.
* ``4500000`` becomes ``'4,500,000'``.
You can pass in either an integer or a string representation of an integer.
intword
-------
Converts a large integer to a friendly text representation. Works best for
numbers over 1 million.
Examples:
* ``1000000`` becomes ``'1.0 million'``.
* ``1200000`` becomes ``'1.2 million'``.
* ``1200000000`` becomes ``'1.2 billion'``.
Values up to 1000000000000000 (one quadrillion) are supported.
You can pass in either an integer or a string representation of an integer.
ordinal
-------
Converts an integer to its ordinal as a string.
Examples:
* ``1`` becomes ``'1st'``.
* ``2`` becomes ``'2nd'``.
* ``3`` becomes ``'3rd'``.
You can pass in either an integer or a string representation of an integer.
flatpages
=========

View File

@ -82,14 +82,14 @@ Methods
``user_permissions``. ``User`` objects can access their related
objects in the same way as any other `Django model`_::
``myuser.objects.groups = [group_list]``
``myuser.objects.groups.add(group, group,...)``
``myuser.objects.groups.remove(group, group,...)``
``myuser.objects.groups.clear()``
``myuser.objects.permissions = [permission_list]``
``myuser.objects.permissions.add(permission, permission, ...)``
``myuser.objects.permissions.remove(permission, permission, ...]``
``myuser.objects.permissions.clear()``
myuser.objects.groups = [group_list]
myuser.objects.groups.add(group, group,...)
myuser.objects.groups.remove(group, group,...)
myuser.objects.groups.clear()
myuser.objects.permissions = [permission_list]
myuser.objects.permissions.add(permission, permission, ...)
myuser.objects.permissions.remove(permission, permission, ...]
myuser.objects.permissions.clear()
In addition to those automatic API methods, ``User`` objects have the following
custom methods:

View File

@ -1035,6 +1035,15 @@ SQL equivalent::
SELECT ... WHERE pub_date IS NULL;
search
~~~~~~
A boolean full-text search, taking advantage of full-text indexing. This is
like ``contains`` but is significantly faster due to full-text indexing.
Note this is only available in MySQL and requires direct manipulation of the
database to add the full-text index.
Default lookups are exact
~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1578,3 +1587,23 @@ get_FOO_height() and get_FOO_width()
For every ``ImageField``, the object will have ``get_FOO_height()`` and
``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This
returns the height (or width) of the image, as an integer, in pixels.
Falling back to raw SQL
=======================
If you find yourself needing to write an SQL query that is too complex for
Django's database-mapper to handle, you can fall back into raw-SQL statement
mode.
The preferred way to do this is by giving your model custom methods or custom
manager methods that execute queries. Although there's nothing in Django that
*requires* database queries to live in the model layer, this approach keeps all
your data-access logic in one place, which is smart from an code-organization
standpoint. For instructions, see `Executing custom SQL`_.
Finally, it's important to note that the Django database layer is merely an
interface to your database. You can access your database via other tools,
programming languages or database frameworks; there's nothing Django-specific
about your database.
.. _Executing custom SQL: http://www.djangoproject.com/documentation/model_api/#executing-custom-sql

View File

@ -148,7 +148,11 @@ If you run this script as a user with normal privileges (recommended), you
might not have access to start a port on a low port number. Low port numbers
are reserved for the superuser (root).
DO NOT USE THIS SERVER IN A PRODUCTION SETTING.
DO NOT USE THIS SERVER IN A PRODUCTION SETTING. It has not gone through
security audits or performance tests. (And that's how it's gonna stay. We're in
the business of making Web frameworks, not Web servers, so improving this
server to be able to handle a production environment is outside the scope of
Django.)
The development server automatically reloads Python code for each request, as
needed. You don't need to restart the server for code changes to take effect.

View File

@ -112,7 +112,7 @@ Lawrence, Kansas, USA.
.. _`Simon Willison`: http://simon.incutio.com/
.. _`simon.incutio.com`: http://simon.incutio.com/
.. _`Jacob Kaplan-Moss`: http://www.jacobian.org/
.. _`Wilson Miner`: http://www.wilsonminer.com/live/
.. _`Wilson Miner`: http://www.wilsonminer.com/
Which sites use Django?
-----------------------

View File

@ -641,8 +641,10 @@ A page representing a list of objects.
* ``paginate_by``: An integer specifying how many objects should be
displayed per page. If this is given, the view will paginate objects with
``paginate_by`` objects per page. The view will expect a ``page`` query
string (GET) parameter containing a zero-indexed page number.
``paginate_by`` objects per page. The view will expect either a ``page``
query string parameter (via ``GET``) containing a zero-indexed page
number, or a ``page`` variable specified in the URLconf. See
"Notes on pagination" below.
* ``template_name``: The full name of a template to use in rendering the
page. This lets you override the default template name (see below).
@ -711,6 +713,25 @@ If the results are paginated, the context will contain these extra variables:
* ``hits``: The total number of objects across *all* pages, not just this
page.
Notes on pagination
~~~~~~~~~~~~~~~~~~~
If ``paginate_by`` is specified, Django will paginate the results. You can
specify the page number in the URL in one of two ways:
* Use the ``page`` parameter in the URLconf. For example, this is what
your URLconf might look like::
(r'^objects/page(?P<page>[0-9]+)/$', 'object_list', dict(info_dict))
* Pass the page number via the ``page`` query-string parameter. For
example, a URL would look like this:
/objects/?page=3
In both cases, ``page`` is 1-based, not 0-based, so the first page would be
represented as page ``1``.
``django.views.generic.list_detail.object_detail``
--------------------------------------------------

View File

@ -1239,6 +1239,12 @@ The above code results in an admin change list page that looks like this:
(This example also has ``search_fields`` defined. See below.)
``list_per_page``
-----------------
Set ``list_per_page`` to control how many items appear on each paginated admin
change list page. By default, this is set to ``100``.
``list_select_related``
-----------------------

View File

@ -291,7 +291,7 @@ The default formatting to use for date fields on Django admin change-list
pages -- and, possibly, by other parts of the system. See
`allowed date format strings`_.
See also DATETIME_FORMAT and TIME_FORMAT.
See also DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
@ -304,7 +304,7 @@ The default formatting to use for datetime fields on Django admin change-list
pages -- and, possibly, by other parts of the system. See
`allowed date format strings`_.
See also DATE_FORMAT and TIME_FORMAT.
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and MONTH_DAY_FORMAT.
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
@ -532,6 +532,23 @@ Default::
A tuple of middleware classes to use. See the `middleware docs`_.
MONTH_DAY_FORMAT
----------------
Default: ``'F j'``
The default formatting to use for date fields on Django admin change-list
pages -- and, possibly, by other parts of the system -- in cases when only the
month and day are displayed.
For example, when a Django admin change-list page is being filtered by a date
drilldown, the header for a given day displays the day and month. Different
locales have different formats. For example, U.S. English would say
"January 1," whereas Spanish might say "1 Enero."
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
TIME_FORMAT and YEAR_MONTH_FORMAT.
PREPEND_WWW
-----------
@ -696,7 +713,8 @@ The default formatting to use for time fields on Django admin change-list
pages -- and, possibly, by other parts of the system. See
`allowed date format strings`_.
See also DATE_FORMAT and DATETIME_FORMAT.
See also DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT and
MONTH_DAY_FORMAT.
.. _allowed date format strings: http://www.djangoproject.com/documentation/templates/#now
@ -720,6 +738,23 @@ A boolean that specifies whether to output the "Etag" header. This saves
bandwidth but slows down performance. This is only used if ``CommonMiddleware``
is installed (see the `middleware docs`_).
YEAR_MONTH_FORMAT
-----------------
Default: ``'F Y'``
The default formatting to use for date fields on Django admin change-list
pages -- and, possibly, by other parts of the system -- in cases when only the
year and month are displayed.
For example, when a Django admin change-list page is being filtered by a date
drilldown, the header for a given month displays the month and the year.
Different locales have different formats. For example, U.S. English would say
"January 2006," whereas another locale might say "2006/January."
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
TIME_FORMAT and MONTH_DAY_FORMAT.
.. _cache docs: http://www.djangoproject.com/documentation/cache/
.. _middleware docs: http://www.djangoproject.com/documentation/middleware/
.. _session docs: http://www.djangoproject.com/documentation/sessions/

View File

@ -1091,3 +1091,27 @@ Value Argument Outputs
``None`` ``"yeah,no"`` ``"no"`` (converts None to False
if no mapping for None is given)
========== ====================== ==================================
Other tags and filter libraries
===============================
Django comes with a couple of other template-tag libraries that you have to
enable explicitly in your ``INSTALLED_APPS`` setting and enable in your
template with the ``{% load %}`` tag.
django.contrib.humanize
-----------------------
A set of Django template filters useful for adding a "human touch" to data. See
the `humanize documentation`_.
.. _humanize documentation: http://www.djangoproject.com/documentation/add_ons/#humanize
django.contrib.markup
---------------------
A collection of template filters that implement these common markup languages:
* Textile
* Markdown
* ReST (ReStructured Text)

View File

@ -368,6 +368,11 @@ Generally, you'll store templates in files on your filesystem rather than using
the low-level ``Template`` API yourself. Save templates in a directory
specified as a **template directory**.
Django searches for template directories in a number of places, depending on
your template-loader settings (see "Loader types" below), but the most basic
way of specifying template directories is by using the ``TEMPLATE_DIRS``
setting.
The TEMPLATE_DIRS setting
~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -5,7 +5,7 @@ from setuptools import setup, find_packages
setup(
name = "Django",
version = "0.91",
version = "0.95",
url = 'http://www.djangoproject.com/',
author = 'Lawrence Journal-World',
author_email = 'holovaty@gmail.com',
@ -52,6 +52,7 @@ setup(
'media/img/admin/*.png',
'media/js/*.js',
'media/js/admin/*js'],
'django.contrib.comments': ['templates/comments/*.html'],
},
scripts = ['django/bin/django-admin.py'],
zip_safe = False,

View File

@ -9,9 +9,10 @@ from django.db import models
class Article(models.Model):
headline = models.CharField(maxlength=100, default='Default headline')
pub_date = models.DateTimeField()
def __repr__(self):
def __str__(self):
return self.headline
API_TESTS = """
# No articles are in the system yet.
@ -39,34 +40,34 @@ datetime.datetime(2005, 7, 28, 0, 0)
>>> a.headline = 'Area woman programs in Python'
>>> a.save()
# Article.objects.all() returns all the articles in the database.
# Article.objects.all() returns all the articles in the database.
>>> Article.objects.all()
[Area woman programs in Python]
[<Article: Area woman programs in Python>]
# Django provides a rich database lookup API.
>>> Article.objects.get(id__exact=1)
Area woman programs in Python
<Article: Area woman programs in Python>
>>> Article.objects.get(headline__startswith='Area woman')
Area woman programs in Python
<Article: Area woman programs in Python>
>>> Article.objects.get(pub_date__year=2005)
Area woman programs in Python
<Article: Area woman programs in Python>
>>> Article.objects.get(pub_date__year=2005, pub_date__month=7)
Area woman programs in Python
<Article: Area woman programs in Python>
>>> Article.objects.get(pub_date__year=2005, pub_date__month=7, pub_date__day=28)
Area woman programs in Python
<Article: Area woman programs in Python>
# The "__exact" lookup type can be omitted, as a shortcut.
>>> Article.objects.get(id=1)
Area woman programs in Python
<Article: Area woman programs in Python>
>>> Article.objects.get(headline='Area woman programs in Python')
Area woman programs in Python
<Article: Area woman programs in Python>
>>> Article.objects.filter(pub_date__year=2005)
[Area woman programs in Python]
[<Article: Area woman programs in Python>]
>>> Article.objects.filter(pub_date__year=2004)
[]
>>> Article.objects.filter(pub_date__year=2005, pub_date__month=7)
[Area woman programs in Python]
[<Article: Area woman programs in Python>]
# Django raises an Article.DoesNotExist exception for get() if the parameters
# don't match any object.
@ -84,7 +85,7 @@ DoesNotExist: Article matching query does not exist.
# shortcut for primary-key exact lookups.
# The following is identical to articles.get(id=1).
>>> Article.objects.get(pk=1)
Area woman programs in Python
<Article: Area woman programs in Python>
# Model instances of the same type and same ID are considered equal.
>>> a = Article.objects.get(pk=1)
@ -222,7 +223,7 @@ datetime.datetime(2005, 7, 28, 0, 0)
>>> s1 = Article.objects.filter(id__exact=1)
>>> s2 = Article.objects.filter(id__exact=2)
>>> s1 | s2
[Area woman programs in Python, Second article]
[<Article: Area woman programs in Python>, <Article: Second article>]
>>> s1 & s2
[]
@ -232,34 +233,34 @@ datetime.datetime(2005, 7, 28, 0, 0)
# You can get items using index and slice notation.
>>> Article.objects.all()[0]
Area woman programs in Python
<Article: Area woman programs in Python>
>>> Article.objects.all()[1:3]
[Second article, Third article]
[<Article: Second article>, <Article: Third article>]
>>> s3 = Article.objects.filter(id__exact=3)
>>> (s1 | s2 | s3)[::2]
[Area woman programs in Python, Third article]
[<Article: Area woman programs in Python>, <Article: Third article>]
# Slices (without step) are lazy:
>>> Article.objects.all()[0:5].filter()
[Area woman programs in Python, Second article, Third article, Fourth article, Article 6]
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Fourth article>, <Article: Article 6>]
# Slicing again works:
>>> Article.objects.all()[0:5][0:2]
[Area woman programs in Python, Second article]
[<Article: Area woman programs in Python>, <Article: Second article>]
>>> Article.objects.all()[0:5][:2]
[Area woman programs in Python, Second article]
[<Article: Area woman programs in Python>, <Article: Second article>]
>>> Article.objects.all()[0:5][4:]
[Article 6]
[<Article: Article 6>]
>>> Article.objects.all()[0:5][5:]
[]
# Some more tests!
>>> Article.objects.all()[2:][0:2]
[Third article, Fourth article]
[<Article: Third article>, <Article: Fourth article>]
>>> Article.objects.all()[2:][:2]
[Third article, Fourth article]
[<Article: Third article>, <Article: Fourth article>]
>>> Article.objects.all()[2:][2:3]
[Article 6]
[<Article: Article 6>]
# Note that you can't use 'offset' without 'limit' (on some dbs), so this doesn't work:
>>> Article.objects.all()[2:]
@ -308,10 +309,10 @@ AttributeError: Manager isn't accessible via Article instances
# Bulk delete test: How many objects before and after the delete?
>>> Article.objects.all()
[Area woman programs in Python, Second article, Third article, Fourth article, Article 6, Default headline, Article 7, Updated article 8]
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Fourth article>, <Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
>>> Article.objects.filter(id__lte=4).delete()
>>> Article.objects.all()
[Article 6, Default headline, Article 7, Updated article 8]
[<Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
"""

View File

@ -20,7 +20,7 @@ class Person(models.Model):
name = models.CharField(maxlength=20)
gender = models.CharField(maxlength=1, choices=GENDER_CHOICES)
def __repr__(self):
def __str__(self):
return self.name
API_TESTS = """

View File

@ -12,7 +12,7 @@ class Person(models.Model):
first_name = models.CharField(maxlength=30, db_column='firstname')
last_name = models.CharField(maxlength=30, db_column='last')
def __repr__(self):
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
API_TESTS = """
@ -24,13 +24,13 @@ API_TESTS = """
1
>>> Person.objects.all()
[John Smith]
[<Person: John Smith>]
>>> Person.objects.filter(first_name__exact='John')
[John Smith]
[<Person: John Smith>]
>>> Person.objects.get(first_name__exact='John')
John Smith
<Person: John Smith>
>>> Person.objects.filter(firstname__exact='John')
Traceback (most recent call last):

View File

@ -23,7 +23,7 @@ class Person(models.Model):
fun = models.BooleanField()
objects = PersonManager()
def __repr__(self):
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
# An example of a custom manager that sets get_query_set().
@ -39,7 +39,7 @@ class Book(models.Model):
published_objects = PublishedBookManager()
authors = models.ManyToManyField(Person, related_name='books')
def __repr__(self):
def __str__(self):
return self.title
# An example of providing multiple custom managers.
@ -55,7 +55,7 @@ class Car(models.Model):
cars = models.Manager()
fast_cars = FastCarManager()
def __repr__(self):
def __str__(self):
return self.name
API_TESTS = """
@ -64,7 +64,7 @@ API_TESTS = """
>>> p2 = Person(first_name='Droopy', last_name='Dog', fun=False)
>>> p2.save()
>>> Person.objects.get_fun_people()
[Bugs Bunny]
[<Person: Bugs Bunny>]
# The RelatedManager used on the 'books' descriptor extends the default manager
>>> from modeltests.custom_managers.models import PublishedBookManager
@ -89,19 +89,19 @@ AttributeError: type object 'Book' has no attribute 'objects'
True
>>> Book.published_objects.all()
[How to program]
[<Book: How to program>]
>>> c1 = Car(name='Corvette', mileage=21, top_speed=180)
>>> c1.save()
>>> c2 = Car(name='Neon', mileage=31, top_speed=100)
>>> c2.save()
>>> Car.cars.order_by('name')
[Corvette, Neon]
[<Car: Corvette>, <Car: Neon>]
>>> Car.fast_cars.all()
[Corvette]
[<Car: Corvette>]
# Each model class gets a "_default_manager" attribute, which is a reference
# to the first manager defined in the class. In this case, it's "cars".
>>> Car._default_manager.order_by('name')
[Corvette, Neon]
[<Car: Corvette>, <Car: Neon>]
"""

View File

@ -11,16 +11,16 @@ class Article(models.Model):
headline = models.CharField(maxlength=100)
pub_date = models.DateField()
def __repr__(self):
def __str__(self):
return self.headline
def was_published_today(self):
return self.pub_date == datetime.date.today()
def get_articles_from_same_day_1(self):
def articles_from_same_day_1(self):
return Article.objects.filter(pub_date=self.pub_date).exclude(id=self.id)
def get_articles_from_same_day_2(self):
def articles_from_same_day_2(self):
"""
Verbose version of get_articles_from_same_day_1, which does a custom
database query for the sake of demonstration.
@ -47,12 +47,12 @@ API_TESTS = """
# Test the custom methods.
>>> a.was_published_today()
False
>>> a.get_articles_from_same_day_1()
[Beatles reunite]
>>> a.get_articles_from_same_day_2()
[Beatles reunite]
>>> b.get_articles_from_same_day_1()
[Area man programs in Python]
>>> b.get_articles_from_same_day_2()
[Area man programs in Python]
>>> a.articles_from_same_day_1()
[<Article: Beatles reunite>]
>>> a.articles_from_same_day_2()
[<Article: Beatles reunite>]
>>> b.articles_from_same_day_1()
[<Article: Area man programs in Python>]
>>> b.articles_from_same_day_2()
[<Article: Area man programs in Python>]
"""

View File

@ -14,7 +14,7 @@ class Employee(models.Model):
class Meta:
ordering = ('last_name', 'first_name')
def __repr__(self):
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Business(models.Model):
@ -23,24 +23,24 @@ class Business(models.Model):
class Meta:
verbose_name_plural = 'businesses'
def __repr__(self):
def __str__(self):
return self.name
API_TESTS = """
>>> dan = Employee(employee_code='ABC123', first_name='Dan', last_name='Jones')
>>> dan.save()
>>> Employee.objects.all()
[Dan Jones]
[<Employee: Dan Jones>]
>>> fran = Employee(employee_code='XYZ456', first_name='Fran', last_name='Bones')
>>> fran.save()
>>> Employee.objects.all()
[Fran Bones, Dan Jones]
[<Employee: Fran Bones>, <Employee: Dan Jones>]
>>> Employee.objects.get(pk='ABC123')
Dan Jones
<Employee: Dan Jones>
>>> Employee.objects.get(pk='XYZ456')
Fran Bones
<Employee: Fran Bones>
>>> Employee.objects.get(pk='foo')
Traceback (most recent call last):
...
@ -48,43 +48,43 @@ DoesNotExist: Employee matching query does not exist.
# Use the name of the primary key, rather than pk.
>>> Employee.objects.get(employee_code__exact='ABC123')
Dan Jones
<Employee: Dan Jones>
# Fran got married and changed her last name.
>>> fran = Employee.objects.get(pk='XYZ456')
>>> fran.last_name = 'Jones'
>>> fran.save()
>>> Employee.objects.filter(last_name__exact='Jones')
[Dan Jones, Fran Jones]
[<Employee: Dan Jones>, <Employee: Fran Jones>]
>>> Employee.objects.in_bulk(['ABC123', 'XYZ456'])
{'XYZ456': Fran Jones, 'ABC123': Dan Jones}
{'XYZ456': <Employee: Fran Jones>, 'ABC123': <Employee: Dan Jones>}
>>> b = Business(name='Sears')
>>> b.save()
>>> b.employees.add(dan, fran)
>>> b.employees.all()
[Dan Jones, Fran Jones]
[<Employee: Dan Jones>, <Employee: Fran Jones>]
>>> fran.business_set.all()
[Sears]
[<Business: Sears>]
>>> Business.objects.in_bulk(['Sears'])
{'Sears': Sears}
{'Sears': <Business: Sears>}
>>> Business.objects.filter(name__exact='Sears')
[Sears]
[<Business: Sears>]
>>> Business.objects.filter(pk='Sears')
[Sears]
[<Business: Sears>]
# Queries across tables, involving primary key
>>> Employee.objects.filter(business__name__exact='Sears')
[Dan Jones, Fran Jones]
[<Employee: Dan Jones>, <Employee: Fran Jones>]
>>> Employee.objects.filter(business__pk='Sears')
[Dan Jones, Fran Jones]
[<Employee: Dan Jones>, <Employee: Fran Jones>]
>>> Business.objects.filter(employees__employee_code__exact='ABC123')
[Sears]
[<Business: Sears>]
>>> Business.objects.filter(employees__pk='ABC123')
[Sears]
[<Business: Sears>]
>>> Business.objects.filter(employees__first_name__startswith='Fran')
[Sears]
[<Business: Sears>]
"""

View File

@ -17,7 +17,7 @@ class Article(models.Model):
class Meta:
get_latest_by = 'pub_date'
def __repr__(self):
def __str__(self):
return self.headline
class Person(models.Model):
@ -26,7 +26,7 @@ class Person(models.Model):
# Note that this model doesn't have "get_latest_by" set.
def __repr__(self):
def __str__(self):
return self.name
API_TESTS = """
@ -49,19 +49,19 @@ DoesNotExist: Article matching query does not exist.
# Get the latest Article.
>>> Article.objects.latest()
Article 4
<Article: Article 4>
# Get the latest Article that matches certain filters.
>>> Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest()
Article 1
<Article: Article 1>
# Pass a custom field name to latest() to change the field that's used to
# determine the latest object.
>>> Article.objects.latest('expire_date')
Article 1
<Article: Article 1>
>>> Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest('expire_date')
Article 3
<Article: Article 3>
# You can still use latest() with a model that doesn't have "get_latest_by"
# set -- just pass in the field name manually.
@ -75,5 +75,5 @@ Traceback (most recent call last):
AssertionError: latest() requires either a field_name parameter or 'get_latest_by' in the model
>>> Person.objects.latest('birthday')
Stephanie
<Person: Stephanie>
"""

View File

@ -12,7 +12,7 @@ class Article(models.Model):
class Meta:
ordering = ('-pub_date', 'headline')
def __repr__(self):
def __str__(self):
return self.headline
API_TESTS = """
@ -61,9 +61,9 @@ Article 4
# in_bulk() takes a list of IDs and returns a dictionary mapping IDs
# to objects.
>>> Article.objects.in_bulk([1, 2])
{1: Article 1, 2: Article 2}
{1: <Article: Article 1>, 2: <Article: Article 2>}
>>> Article.objects.in_bulk([3])
{3: Article 3}
{3: <Article: Article 3>}
>>> Article.objects.in_bulk([1000])
{}
>>> Article.objects.in_bulk([])
@ -125,55 +125,55 @@ True
# In the case of identical date values, these methods will use the ID as a
# fallback check. This guarantees that no records are skipped or duplicated.
>>> a1.get_next_by_pub_date()
Article 2
<Article: Article 2>
>>> a2.get_next_by_pub_date()
Article 3
<Article: Article 3>
>>> a3.get_next_by_pub_date()
Article 7
<Article: Article 7>
>>> a4.get_next_by_pub_date()
Article 6
<Article: Article 6>
>>> a5.get_next_by_pub_date()
Traceback (most recent call last):
...
DoesNotExist: Article matching query does not exist.
>>> a6.get_next_by_pub_date()
Article 5
<Article: Article 5>
>>> a7.get_next_by_pub_date()
Article 4
<Article: Article 4>
>>> a7.get_previous_by_pub_date()
Article 3
<Article: Article 3>
>>> a6.get_previous_by_pub_date()
Article 4
<Article: Article 4>
>>> a5.get_previous_by_pub_date()
Article 6
<Article: Article 6>
>>> a4.get_previous_by_pub_date()
Article 7
<Article: Article 7>
>>> a3.get_previous_by_pub_date()
Article 2
<Article: Article 2>
>>> a2.get_previous_by_pub_date()
Article 1
<Article: Article 1>
# Underscores and percent signs have special meaning in the underlying
# database library, but Django handles the quoting of them automatically.
>>> a8 = Article(headline='Article_ with underscore', pub_date=datetime(2005, 11, 20))
>>> a8.save()
>>> Article.objects.filter(headline__startswith='Article')
[Article_ with underscore, Article 5, Article 6, Article 4, Article 2, Article 3, Article 7, Article 1]
[<Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
>>> Article.objects.filter(headline__startswith='Article_')
[Article_ with underscore]
[<Article: Article_ with underscore>]
>>> a9 = Article(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21))
>>> a9.save()
>>> Article.objects.filter(headline__startswith='Article')
[Article% with percent sign, Article_ with underscore, Article 5, Article 6, Article 4, Article 2, Article 3, Article 7, Article 1]
[<Article: Article% with percent sign>, <Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
>>> Article.objects.filter(headline__startswith='Article%')
[Article% with percent sign]
[<Article: Article% with percent sign>]
# exclude() is the opposite of filter() when doing lookups:
>>> Article.objects.filter(headline__contains='Article').exclude(headline__contains='with')
[Article 5, Article 6, Article 4, Article 2, Article 3, Article 7, Article 1]
[<Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
>>> Article.objects.exclude(headline__startswith="Article_")
[Article% with percent sign, Article 5, Article 6, Article 4, Article 2, Article 3, Article 7, Article 1]
[<Article: Article% with percent sign>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
>>> Article.objects.exclude(headline="Article 7")
[Article% with percent sign, Article_ with underscore, Article 5, Article 6, Article 4, Article 2, Article 3, Article 1]
[<Article: Article% with percent sign>, <Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 1>]
"""

View File

@ -16,14 +16,14 @@ class Reporter(models.Model):
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
def __repr__(self):
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Article(models.Model):
headline = models.CharField(maxlength=100)
pub_date = models.DateField()
def __repr__(self):
def __str__(self):
return self.headline
class Writer(models.Model):
@ -31,8 +31,8 @@ class Writer(models.Model):
article = models.ForeignKey(Article)
position = models.CharField(maxlength=100)
def __repr__(self):
return '%r (%s)' % (self.reporter, self.position)
def __str__(self):
return '%s (%s)' % (self.reporter, self.position)
API_TESTS = """
# Create a few Reporters.
@ -54,15 +54,15 @@ API_TESTS = """
# Play around with the API.
>>> a.writer_set.select_related().order_by('-position')
[John Smith (Main writer), Jane Doe (Contributor)]
[<Writer: John Smith (Main writer)>, <Writer: Jane Doe (Contributor)>]
>>> w1.reporter
John Smith
<Reporter: John Smith>
>>> w2.reporter
Jane Doe
<Reporter: Jane Doe>
>>> w1.article
This is a test
<Article: This is a test>
>>> w2.article
This is a test
<Article: This is a test>
>>> r1.writer_set.all()
[John Smith (Main writer)]
[<Writer: John Smith (Main writer)>]
"""

View File

@ -14,7 +14,7 @@ class Category(models.Model):
class Meta:
ordering = ('name',)
def __repr__(self):
def __str__(self):
return self.name
class Article(models.Model):
@ -25,7 +25,7 @@ class Article(models.Model):
class Meta:
ordering = ('pub_date',)
def __repr__(self):
def __str__(self):
return self.headline
API_TESTS = """
@ -51,29 +51,29 @@ API_TESTS = """
>>> a2.secondary_categories.add(c4)
>>> a1.primary_categories.all()
[Crime, News]
[<Category: Crime>, <Category: News>]
>>> a2.primary_categories.all()
[News, Sports]
[<Category: News>, <Category: Sports>]
>>> a1.secondary_categories.all()
[Life]
[<Category: Life>]
>>> c1.primary_article_set.all()
[Area man runs]
[<Article: Area man runs>]
>>> c1.secondary_article_set.all()
[]
>>> c2.primary_article_set.all()
[Area man steals, Area man runs]
[<Article: Area man steals>, <Article: Area man runs>]
>>> c2.secondary_article_set.all()
[]
>>> c3.primary_article_set.all()
[Area man steals]
[<Article: Area man steals>]
>>> c3.secondary_article_set.all()
[]
>>> c4.primary_article_set.all()
[]
>>> c4.secondary_article_set.all()
[Area man steals, Area man runs]
[<Article: Area man steals>, <Article: Area man runs>]
"""

View File

@ -19,7 +19,7 @@ class Person(models.Model):
friends = models.ManyToManyField('self')
idols = models.ManyToManyField('self', symmetrical=False, related_name='stalkers')
def __repr__(self):
def __str__(self):
return self.name
API_TESTS = """
@ -41,37 +41,37 @@ API_TESTS = """
# Who is friends with Anne?
>>> a.friends.all()
[Bill, Chuck, David]
[<Person: Bill>, <Person: Chuck>, <Person: David>]
# Who is friends with Bill?
>>> b.friends.all()
[Anne]
[<Person: Anne>]
# Who is friends with Chuck?
>>> c.friends.all()
[Anne, David]
[<Person: Anne>, <Person: David>]
# Who is friends with David?
>>> d.friends.all()
[Anne, Chuck]
[<Person: Anne>, <Person: Chuck>]
# Bill is already friends with Anne - add Anne again, but in the reverse direction
>>> b.friends.add(a)
# Who is friends with Anne?
>>> a.friends.all()
[Bill, Chuck, David]
[<Person: Bill>, <Person: Chuck>, <Person: David>]
# Who is friends with Bill?
>>> b.friends.all()
[Anne]
[<Person: Anne>]
# Remove Anne from Bill's friends
>>> b.friends.remove(a)
# Who is friends with Anne?
>>> a.friends.all()
[Chuck, David]
[<Person: Chuck>, <Person: David>]
# Who is friends with Bill?
>>> b.friends.all()
@ -87,11 +87,11 @@ API_TESTS = """
# Reverse relationships should also be gone
# Who is friends with Chuck?
>>> c.friends.all()
[David]
[<Person: David>]
# Who is friends with David?
>>> d.friends.all()
[Chuck]
[<Person: Chuck>]
# Add some idols in the direction of field definition
@ -106,27 +106,27 @@ API_TESTS = """
# Who are Anne's idols?
>>> a.idols.all()
[Bill, Chuck, David]
[<Person: Bill>, <Person: Chuck>, <Person: David>]
# Who is stalking Anne?
>>> a.stalkers.all()
[Bill]
[<Person: Bill>]
# Who are Bill's idols?
>>> b.idols.all()
[Anne]
[<Person: Anne>]
# Who is stalking Bill?
>>> b.stalkers.all()
[Anne]
[<Person: Anne>]
# Who are Chuck's idols?
>>> c.idols.all()
[David]
[<Person: David>]
# Who is stalking Chuck?
>>> c.stalkers.all()
[Anne]
[<Person: Anne>]
# Who are David's idols?
>>> d.idols.all()
@ -134,40 +134,40 @@ API_TESTS = """
# Who is stalking David
>>> d.stalkers.all()
[Anne, Chuck]
[<Person: Anne>, <Person: Chuck>]
# Bill is already being stalked by Anne - add Anne again, but in the reverse direction
>>> b.stalkers.add(a)
# Who are Anne's idols?
>>> a.idols.all()
[Bill, Chuck, David]
[<Person: Bill>, <Person: Chuck>, <Person: David>]
# Who is stalking Anne?
[Bill]
[<Person: Bill>]
# Who are Bill's idols
>>> b.idols.all()
[Anne]
[<Person: Anne>]
# Who is stalking Bill?
>>> b.stalkers.all()
[Anne]
[<Person: Anne>]
# Remove Anne from Bill's list of stalkers
>>> b.stalkers.remove(a)
# Who are Anne's idols?
>>> a.idols.all()
[Chuck, David]
[<Person: Chuck>, <Person: David>]
# Who is stalking Anne?
>>> a.stalkers.all()
[Bill]
[<Person: Bill>]
# Who are Bill's idols?
>>> b.idols.all()
[Anne]
[<Person: Anne>]
# Who is stalking Bill?
>>> b.stalkers.all()
@ -187,6 +187,6 @@ API_TESTS = """
# Who is friends with David?
>>> d.stalkers.all()
[Chuck]
[<Person: Chuck>]
"""

View File

@ -16,7 +16,7 @@ class Category(models.Model):
name = models.CharField(maxlength=20)
parent = models.ForeignKey('self', null=True, related_name='child_set')
def __repr__(self):
def __str__(self):
return self.name
API_TESTS = """
@ -27,14 +27,14 @@ API_TESTS = """
>>> c.save()
>>> r.child_set.all()
[Child category]
[<Category: Child category>]
>>> r.child_set.get(name__startswith='Child')
Child category
<Category: Child category>
>>> print r.parent
None
>>> c.child_set.all()
[]
>>> c.parent
Root category
<Category: Root category>
"""

View File

@ -14,7 +14,7 @@ class Person(models.Model):
mother = models.ForeignKey('self', null=True, related_name='mothers_child_set')
father = models.ForeignKey('self', null=True, related_name='fathers_child_set')
def __repr__(self):
def __str__(self):
return self.full_name
API_TESTS = """
@ -29,13 +29,13 @@ API_TESTS = """
>>> kid.save()
>>> kid.mother
Jane Smith
<Person: Jane Smith>
>>> kid.father
John Smith Senior
<Person: John Smith Senior>
>>> dad.fathers_child_set.all()
[John Smith Junior]
[<Person: John Smith Junior>]
>>> mom.mothers_child_set.all()
[John Smith Junior]
[<Person: John Smith Junior>]
>>> kid.mothers_child_set.all()
[]
>>> kid.fathers_child_set.all()

View File

@ -10,7 +10,7 @@ class Musician(models.Model):
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
def __repr__(self):
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Album(models.Model):
@ -18,7 +18,7 @@ class Album(models.Model):
musician = models.ForeignKey(Musician)
release_date = models.DateField(blank=True, null=True)
def __repr__(self):
def __str__(self):
return self.name
API_TESTS = """
@ -35,7 +35,7 @@ API_TESTS = """
# Verify it worked.
>>> Musician.objects.all()
[Ella Fitzgerald]
[<Musician: Ella Fitzgerald>]
>>> [m1] == list(Musician.objects.all())
True
@ -69,9 +69,9 @@ True
# Verify it worked.
>>> Album.objects.all()
[Ella and Basie]
[<Album: Ella and Basie>]
>>> Album.objects.get().musician
Ella Fitzgerald
<Musician: Ella Fitzgerald>
# Create an Album with a release_date.
>>> data = MultiValueDict({'name': ['Ultimate Ella'], 'musician': ['1'], 'release_date': ['2005-02-13']})
@ -82,10 +82,10 @@ Ella Fitzgerald
# Verify it worked.
>>> Album.objects.order_by('name')
[Ella and Basie, Ultimate Ella]
[<Album: Ella and Basie>, <Album: Ultimate Ella>]
>>> a2 = Album.objects.get(pk=2)
>>> a2
Ultimate Ella
<Album: Ultimate Ella>
>>> a2.release_date
datetime.date(2005, 2, 13)
"""

View File

@ -12,7 +12,7 @@ from django.db import models
class Publication(models.Model):
title = models.CharField(maxlength=30)
def __repr__(self):
def __str__(self):
return self.title
class Meta:
@ -22,7 +22,7 @@ class Article(models.Model):
headline = models.CharField(maxlength=100)
publications = models.ManyToManyField(Publication)
def __repr__(self):
def __str__(self):
return self.headline
class Meta:
@ -58,29 +58,29 @@ API_TESTS = """
# Article objects have access to their related Publication objects.
>>> a1.publications.all()
[The Python Journal]
[<Publication: The Python Journal>]
>>> a2.publications.all()
[Highlights for Children, Science News, Science Weekly, The Python Journal]
[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]
# Publication objects have access to their related Article objects.
>>> p2.article_set.all()
[NASA uses Python]
[<Article: NASA uses Python>]
>>> p1.article_set.all()
[Django lets you build Web apps easily, NASA uses Python]
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
>>> Publication.objects.get(id=4).article_set.all()
[NASA uses Python]
[<Article: NASA uses Python>]
# We can perform kwarg queries across m2m relationships
>>> Article.objects.filter(publications__id__exact=1)
[Django lets you build Web apps easily, NASA uses Python]
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
>>> Article.objects.filter(publications__pk=1)
[Django lets you build Web apps easily, NASA uses Python]
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
>>> Article.objects.filter(publications__title__startswith="Science")
[NASA uses Python, NASA uses Python]
[<Article: NASA uses Python>, <Article: NASA uses Python>]
>>> Article.objects.filter(publications__title__startswith="Science").distinct()
[NASA uses Python]
[<Article: NASA uses Python>]
# The count() function respects distinct() as well.
>>> Article.objects.filter(publications__title__startswith="Science").count()
@ -92,23 +92,23 @@ API_TESTS = """
# Reverse m2m queries are supported (i.e., starting at the table that doesn't
# have a ManyToManyField).
>>> Publication.objects.filter(id__exact=1)
[The Python Journal]
[<Publication: The Python Journal>]
>>> Publication.objects.filter(pk=1)
[The Python Journal]
[<Publication: The Python Journal>]
>>> Publication.objects.filter(article__headline__startswith="NASA")
[Highlights for Children, Science News, Science Weekly, The Python Journal]
[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]
>>> Publication.objects.filter(article__id__exact=1)
[The Python Journal]
[<Publication: The Python Journal>]
>>> Publication.objects.filter(article__pk=1)
[The Python Journal]
[<Publication: The Python Journal>]
# If we delete a Publication, its Articles won't be able to access it.
>>> p1.delete()
>>> Publication.objects.all()
[Highlights for Children, Science News, Science Weekly]
[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>]
>>> a1 = Article.objects.get(pk=1)
>>> a1.publications.all()
[]
@ -116,7 +116,7 @@ API_TESTS = """
# If we delete an Article, its Publications won't be able to access it.
>>> a2.delete()
>>> Article.objects.all()
[Django lets you build Web apps easily]
[<Article: Django lets you build Web apps easily>]
>>> p2.article_set.all()
[]
@ -125,22 +125,22 @@ API_TESTS = """
>>> a4.save()
>>> p2.article_set.add(a4)
>>> p2.article_set.all()
[NASA finds intelligent life on Earth]
[<Article: NASA finds intelligent life on Earth>]
>>> a4.publications.all()
[Science News]
[<Publication: Science News>]
# Adding via the other end using keywords
>>> new_article = p2.article_set.create(headline='Oxygen-free diet works wonders')
>>> p2.article_set.all()
[NASA finds intelligent life on Earth, Oxygen-free diet works wonders]
[<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]
>>> a5 = p2.article_set.all()[1]
>>> a5.publications.all()
[Science News]
[<Publication: Science News>]
# Removing publication from an article:
>>> a4.publications.remove(p2)
>>> p2.article_set.all()
[Oxygen-free diet works wonders]
[<Article: Oxygen-free diet works wonders>]
>>> a4.publications.all()
[]
@ -154,33 +154,33 @@ API_TESTS = """
# Relation sets can be assigned. Assignment clears any existing set members
>>> p2.article_set = [a4, a5]
>>> p2.article_set.all()
[NASA finds intelligent life on Earth, Oxygen-free diet works wonders]
[<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]
>>> a4.publications.all()
[Science News]
[<Publication: Science News>]
>>> a4.publications = [p3]
>>> p2.article_set.all()
[Oxygen-free diet works wonders]
[<Article: Oxygen-free diet works wonders>]
>>> a4.publications.all()
[Science Weekly]
[<Publication: Science Weekly>]
# Relation sets can be cleared:
>>> p2.article_set.clear()
>>> p2.article_set.all()
[]
>>> a4.publications.all()
[Science Weekly]
[<Publication: Science Weekly>]
# And you can clear from the other end
>>> p2.article_set.add(a4, a5)
>>> p2.article_set.all()
[NASA finds intelligent life on Earth, Oxygen-free diet works wonders]
[<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]
>>> a4.publications.all()
[Science News, Science Weekly]
[<Publication: Science News>, <Publication: Science Weekly>]
>>> a4.publications.clear()
>>> a4.publications.all()
[]
>>> p2.article_set.all()
[Oxygen-free diet works wonders]
[<Article: Oxygen-free diet works wonders>]
# Recreate the article and Publication we just deleted.
>>> p1 = Publication(id=None, title='The Python Journal')
@ -192,22 +192,22 @@ API_TESTS = """
# Bulk delete some Publications - references to deleted publications should go
>>> Publication.objects.filter(title__startswith='Science').delete()
>>> Publication.objects.all()
[Highlights for Children, The Python Journal]
[<Publication: Highlights for Children>, <Publication: The Python Journal>]
>>> Article.objects.all()
[Django lets you build Web apps easily, NASA finds intelligent life on Earth, NASA uses Python, Oxygen-free diet works wonders]
[<Article: Django lets you build Web apps easily>, <Article: NASA finds intelligent life on Earth>, <Article: NASA uses Python>, <Article: Oxygen-free diet works wonders>]
>>> a2.publications.all()
[The Python Journal]
[<Publication: The Python Journal>]
# Bulk delete some articles - references to deleted objects should go
>>> q = Article.objects.filter(headline__startswith='Django')
>>> print q
[Django lets you build Web apps easily]
[<Article: Django lets you build Web apps easily>]
>>> q.delete()
# After the delete, the QuerySet cache needs to be cleared, and the referenced objects should be gone
>>> print q
[]
>>> p1.article_set.all()
[NASA uses Python]
[<Article: NASA uses Python>]
"""

View File

@ -11,7 +11,7 @@ class Reporter(models.Model):
last_name = models.CharField(maxlength=30)
email = models.EmailField()
def __repr__(self):
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Article(models.Model):
@ -19,7 +19,7 @@ class Article(models.Model):
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter)
def __repr__(self):
def __str__(self):
return self.headline
class Meta:
@ -42,7 +42,7 @@ API_TESTS = """
1
>>> a.reporter
John Smith
<Reporter: John Smith>
# Article objects have access to their related Reporter objects.
>>> r = a.reporter
@ -52,7 +52,7 @@ John Smith
# Create an Article via the Reporter object.
>>> new_article = r.article_set.create(headline="John's second story", pub_date=datetime(2005, 7, 29))
>>> new_article
John's second story
<Article: John's second story>
>>> new_article.reporter.id
1
@ -62,43 +62,43 @@ John's second story
>>> new_article2.reporter.id
1
>>> r.article_set.all()
[John's second story, Paul's story, This is a test]
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]
# Add the same article to a different article set - check that it moves.
>>> r2.article_set.add(new_article2)
>>> new_article2.reporter.id
2
>>> r.article_set.all()
[John's second story, This is a test]
[<Article: John's second story>, <Article: This is a test>]
>>> r2.article_set.all()
[Paul's story]
[<Article: Paul's story>]
# Assign the article to the reporter directly using the descriptor
>>> new_article2.reporter = r
>>> new_article2.save()
>>> new_article2.reporter
John Smith
<Reporter: John Smith>
>>> new_article2.reporter.id
1
>>> r.article_set.all()
[John's second story, Paul's story, This is a test]
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]
>>> r2.article_set.all()
[]
# Set the article back again using set descriptor.
>>> r2.article_set = [new_article, new_article2]
>>> r.article_set.all()
[This is a test]
[<Article: This is a test>]
>>> r2.article_set.all()
[John's second story, Paul's story]
[<Article: John's second story>, <Article: Paul's story>]
# Funny case - assignment notation can only go so far; because the
# Funny case - assignment notation can only go so far; because the
# ForeignKey cannot be null, existing members of the set must remain
>>> r.article_set = [new_article]
>>> r.article_set.all()
[John's second story, This is a test]
[<Article: John's second story>, <Article: This is a test>]
>>> r2.article_set.all()
[Paul's story]
[<Article: Paul's story>]
# Reporter cannot be null - there should not be a clear or remove method
>>> hasattr(r2.article_set, 'remove')
@ -108,10 +108,10 @@ False
# Reporter objects have access to their related Article objects.
>>> r.article_set.all()
[John's second story, This is a test]
[<Article: John's second story>, <Article: This is a test>]
>>> r.article_set.filter(headline__startswith='This')
[This is a test]
[<Article: This is a test>]
>>> r.article_set.count()
2
@ -121,24 +121,24 @@ False
# Get articles by id
>>> Article.objects.filter(id__exact=1)
[This is a test]
[<Article: This is a test>]
>>> Article.objects.filter(pk=1)
[This is a test]
[<Article: This is a test>]
# Query on an article property
>>> Article.objects.filter(headline__startswith='This')
[This is a test]
[<Article: This is a test>]
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want. There's no limit.
# Find all Articles for any Reporter whose first name is "John".
>>> Article.objects.filter(reporter__first_name__exact='John')
[John's second story, This is a test]
[<Article: John's second story>, <Article: This is a test>]
# Query twice over the related field.
>>> Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith')
[John's second story, This is a test]
[<Article: John's second story>, <Article: This is a test>]
# The underlying query only makes one join when a related table is referenced twice.
>>> query = Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith')
@ -148,13 +148,13 @@ False
# The automatically joined table has a predictable name.
>>> Article.objects.filter(reporter__first_name__exact='John').extra(where=["many_to_one_article__reporter.last_name='Smith'"])
[John's second story, This is a test]
[<Article: John's second story>, <Article: This is a test>]
# Find all Articles for the Reporter whose ID is 1.
>>> Article.objects.filter(reporter__id__exact=1)
[John's second story, This is a test]
[<Article: John's second story>, <Article: This is a test>]
>>> Article.objects.filter(reporter__pk=1)
[John's second story, This is a test]
[<Article: John's second story>, <Article: This is a test>]
# You need two underscores between "reporter" and "id" -- not one.
>>> Article.objects.filter(reporter_id__exact=1)
@ -170,7 +170,7 @@ TypeError: Cannot resolve keyword 'reporter_id' into field
# "pk" shortcut syntax works in a related context, too.
>>> Article.objects.filter(reporter__pk=1)
[John's second story, This is a test]
[<Article: John's second story>, <Article: This is a test>]
# You can also instantiate an Article by passing
# the Reporter's ID instead of a Reporter object.
@ -179,31 +179,31 @@ TypeError: Cannot resolve keyword 'reporter_id' into field
>>> a3.reporter.id
1
>>> a3.reporter
John Smith
<Reporter: John Smith>
# Similarly, the reporter ID can be a string.
>>> a4 = Article(id=None, headline="This is a test", pub_date=datetime(2005, 7, 27), reporter_id="1")
>>> a4.save()
>>> a4.reporter
John Smith
<Reporter: John Smith>
# Reporters can be queried
>>> Reporter.objects.filter(id__exact=1)
[John Smith]
[<Reporter: John Smith>]
>>> Reporter.objects.filter(pk=1)
[John Smith]
[<Reporter: John Smith>]
>>> Reporter.objects.filter(first_name__startswith='John')
[John Smith]
[<Reporter: John Smith>]
# Reporters can query in opposite direction of ForeignKey definition
>>> Reporter.objects.filter(article__id__exact=1)
[John Smith]
[<Reporter: John Smith>]
>>> Reporter.objects.filter(article__pk=1)
[John Smith]
[<Reporter: John Smith>]
>>> Reporter.objects.filter(article__headline__startswith='This')
[John Smith, John Smith, John Smith]
[<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>]
>>> Reporter.objects.filter(article__headline__startswith='This').distinct()
[John Smith]
[<Reporter: John Smith>]
# Counting in the opposite direction works in conjunction with distinct()
>>> Reporter.objects.filter(article__headline__startswith='This').count()
@ -213,20 +213,20 @@ John Smith
# Queries can go round in circles.
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John')
[John Smith, John Smith, John Smith, John Smith]
[<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>]
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John').distinct()
[John Smith]
[<Reporter: John Smith>]
# If you delete a reporter, his articles will be deleted.
>>> Article.objects.all()
[John's second story, Paul's story, This is a test, This is a test, This is a test]
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>, <Article: This is a test>, <Article: This is a test>]
>>> Reporter.objects.order_by('first_name')
[John Smith, Paul Jones]
[<Reporter: John Smith>, <Reporter: Paul Jones>]
>>> r2.delete()
>>> Article.objects.all()
[John's second story, This is a test, This is a test, This is a test]
[<Article: John's second story>, <Article: This is a test>, <Article: This is a test>, <Article: This is a test>]
>>> Reporter.objects.order_by('first_name')
[John Smith]
[<Reporter: John Smith>]
# Deletes using a join in the query
>>> Reporter.objects.filter(article__headline__startswith='This').delete()

View File

@ -10,19 +10,19 @@ from django.db import models
class Reporter(models.Model):
name = models.CharField(maxlength=30)
def __repr__(self):
def __str__(self):
return self.name
class Article(models.Model):
headline = models.CharField(maxlength=100)
reporter = models.ForeignKey(Reporter, null=True)
def __repr__(self):
return self.headline
class Meta:
ordering = ('headline',)
def __str__(self):
return self.headline
API_TESTS = """
# Create a Reporter.
>>> r = Reporter(name='John Smith')
@ -36,7 +36,7 @@ API_TESTS = """
1
>>> a.reporter
John Smith
<Reporter: John Smith>
# Article objects have access to their related Reporter objects.
>>> r = a.reporter
@ -44,15 +44,15 @@ John Smith
# Create an Article via the Reporter object.
>>> a2 = r.article_set.create(headline="Second")
>>> a2
Second
<Article: Second>
>>> a2.reporter.id
1
# Reporter objects have access to their related Article objects.
>>> r.article_set.all()
[First, Second]
[<Article: First>, <Article: Second>]
>>> r.article_set.filter(headline__startswith='Fir')
[First]
[<Article: First>]
>>> r.article_set.count()
2
@ -78,47 +78,47 @@ None
# To retrieve the articles with no reporters set, use "reporter__isnull=True".
>>> Article.objects.filter(reporter__isnull=True)
[Third]
[<Article: Third>]
# Set the reporter for the Third article
>>> r.article_set.add(a3)
>>> r.article_set.all()
[First, Second, Third]
[<Article: First>, <Article: Second>, <Article: Third>]
# Remove an article from the set, and check that it was removed.
>>> r.article_set.remove(a3)
>>> r.article_set.all()
[First, Second]
[<Article: First>, <Article: Second>]
>>> Article.objects.filter(reporter__isnull=True)
[Third]
[<Article: Third>]
# Create another article and reporter
>>> r2 = Reporter(name='Paul Jones')
>>> r2.save()
>>> a4 = r2.article_set.create(headline='Fourth')
>>> r2.article_set.all()
[Fourth]
[<Article: Fourth>]
# Try to remove a4 from a set it does not belong to
>>> r.article_set.remove(a4)
Traceback (most recent call last):
...
DoesNotExist: 'Article object' is not related to 'Reporter object'.
DoesNotExist: <Article: Fourth> is not related to <Reporter: John Smith>.
>>> r2.article_set.all()
[Fourth]
[<Article: Fourth>]
# Use descriptor assignment to allocate ForeignKey. Null is legal, so
# existing members of set that are not in the assignment set are set null
>>> r2.article_set = [a2, a3]
>>> r2.article_set.all()
[Second, Third]
[<Article: Second>, <Article: Third>]
# Clear the rest of the set
>>> r.article_set.clear()
>>> r.article_set.all()
[]
>>> Article.objects.filter(reporter__isnull=True)
[First, Fourth]
[<Article: First>, <Article: Fourth>]
"""

View File

@ -10,20 +10,20 @@ class Place(models.Model):
name = models.CharField(maxlength=50)
address = models.CharField(maxlength=80)
def __repr__(self):
def __str__(self):
return "%s the place" % self.name
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
def __repr__(self):
def __str__(self):
return "%s the restaurant" % self.name
class ItalianRestaurant(Restaurant):
serves_gnocchi = models.BooleanField()
def __repr__(self):
def __str__(self):
return "%s the italian restaurant" % self.name
API_TESTS = """

View File

@ -12,7 +12,7 @@ class Place(models.Model):
name = models.CharField(maxlength=50)
address = models.CharField(maxlength=80)
def __repr__(self):
def __str__(self):
return "%s the place" % self.name
class Restaurant(models.Model):
@ -20,15 +20,15 @@ class Restaurant(models.Model):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
def __repr__(self):
def __str__(self):
return "%s the restaurant" % self.place.name
class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant)
name = models.CharField(maxlength=50)
def __repr__(self):
return "%s the waiter at %r" % (self.name, self.restaurant)
def __str__(self):
return "%s the waiter at %s" % (self.name, self.restaurant)
API_TESTS = """
# Create a couple of Places.
@ -43,11 +43,11 @@ API_TESTS = """
# A Restaurant can access its place.
>>> r.place
Demon Dogs the place
<Place: Demon Dogs the place>
# A Place can access its restaurant, if available.
>>> p1.restaurant
Demon Dogs the restaurant
<Restaurant: Demon Dogs the restaurant>
# p2 doesn't have an associated restaurant.
>>> p2.restaurant
@ -60,69 +60,69 @@ DoesNotExist: Restaurant matching query does not exist.
>>> r.place = p2
>>> r.save()
>>> p2.restaurant
Ace Hardware the restaurant
<Restaurant: Ace Hardware the restaurant>
>>> r.place
Ace Hardware the place
<Place: Ace Hardware the place>
# Set the place back again, using assignment in the reverse direction
# Need to reget restaurant object first, because the reverse set
# Need to reget restaurant object first, because the reverse set
# can't update the existing restaurant instance
>>> p1.restaurant = r
>>> r.save()
>>> p1.restaurant
Demon Dogs the restaurant
<Restaurant: Demon Dogs the restaurant>
>>> r = Restaurant.objects.get(pk=1)
>>> r.place
Demon Dogs the place
<Place: Demon Dogs the place>
# Restaurant.objects.all() just returns the Restaurants, not the Places.
# Note that there are two restaurants - Ace Hardware the Restaurant was created
# in the call to r.place = p2. This means there are multiple restaurants referencing
# in the call to r.place = p2. This means there are multiple restaurants referencing
# a single place...
>>> Restaurant.objects.all()
[Demon Dogs the restaurant, Ace Hardware the restaurant]
[<Restaurant: Demon Dogs the restaurant>, <Restaurant: Ace Hardware the restaurant>]
# Place.objects.all() returns all Places, regardless of whether they have
# Restaurants.
>>> Place.objects.order_by('name')
[Ace Hardware the place, Demon Dogs the place]
[<Place: Ace Hardware the place>, <Place: Demon Dogs the place>]
>>> Restaurant.objects.get(place__id__exact=1)
Demon Dogs the restaurant
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.get(pk=1)
Demon Dogs the restaurant
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.get(place__exact=1)
Demon Dogs the restaurant
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.get(place__pk=1)
Demon Dogs the restaurant
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.get(place__name__startswith="Demon")
Demon Dogs the restaurant
<Restaurant: Demon Dogs the restaurant>
>>> Place.objects.get(id__exact=1)
Demon Dogs the place
<Place: Demon Dogs the place>
>>> Place.objects.get(pk=1)
Demon Dogs the place
<Place: Demon Dogs the place>
>>> Place.objects.get(restaurant__place__exact=1)
Demon Dogs the place
<Place: Demon Dogs the place>
>>> Place.objects.get(restaurant__pk=1)
Demon Dogs the place
<Place: Demon Dogs the place>
# Add a Waiter to the Restaurant.
>>> w = r.waiter_set.create(name='Joe')
>>> w.save()
>>> w
Joe the waiter at Demon Dogs the restaurant
<Waiter: Joe the waiter at Demon Dogs the restaurant>
# Query the waiters
>>> Waiter.objects.filter(restaurant__place__exact=1)
[Joe the waiter at Demon Dogs the restaurant]
[<Waiter: Joe the waiter at Demon Dogs the restaurant>]
>>> Waiter.objects.filter(restaurant__pk=1)
[Joe the waiter at Demon Dogs the restaurant]
[<Waiter: Joe the waiter at Demon Dogs the restaurant>]
>>> Waiter.objects.filter(id__exact=1)
[Joe the waiter at Demon Dogs the restaurant]
[<Waiter: Joe the waiter at Demon Dogs the restaurant>]
>>> Waiter.objects.filter(pk=1)
[Joe the waiter at Demon Dogs the restaurant]
[<Waiter: Joe the waiter at Demon Dogs the restaurant>]
# Delete the restaurant; the waiter should also be removed
>>> r = Restaurant.objects.get(pk=1)

View File

@ -1,10 +1,10 @@
"""
19. OR lookups
To perform an OR lookup, or a lookup that combines ANDs and ORs,
To perform an OR lookup, or a lookup that combines ANDs and ORs,
combine QuerySet objects using & and | operators.
Alternatively, use positional arguments, and pass one or more expressions
Alternatively, use positional arguments, and pass one or more expressions
of clauses using the variable ``django.db.models.Q`` (or any object with
a get_sql method).
@ -16,10 +16,11 @@ from django.db import models
class Article(models.Model):
headline = models.CharField(maxlength=50)
pub_date = models.DateTimeField()
class Meta:
ordering = ('pub_date',)
def __repr__(self):
def __str__(self):
return self.headline
API_TESTS = """
@ -36,10 +37,10 @@ API_TESTS = """
>>> a3.save()
>>> Article.objects.filter(headline__startswith='Hello') | Article.objects.filter(headline__startswith='Goodbye')
[Hello, Goodbye, Hello and goodbye]
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
>>> Article.objects.filter(Q(headline__startswith='Hello') | Q(headline__startswith='Goodbye'))
[Hello, Goodbye, Hello and goodbye]
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
>>> Article.objects.filter(Q(headline__startswith='Hello') & Q(headline__startswith='Goodbye'))
[]
@ -51,34 +52,34 @@ API_TESTS = """
[]
>>> articles.filter(headline__startswith='Hello') & articles.filter(headline__contains='bye')
[Hello and goodbye]
[<Article: Hello and goodbye>]
>>> Article.objects.filter(Q(headline__contains='bye'), headline__startswith='Hello')
[Hello and goodbye]
[<Article: Hello and goodbye>]
>>> Article.objects.filter(headline__contains='Hello') | Article.objects.filter(headline__contains='bye')
[Hello, Goodbye, Hello and goodbye]
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
>>> Article.objects.filter(headline__iexact='Hello') | Article.objects.filter(headline__contains='ood')
[Hello, Goodbye, Hello and goodbye]
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
>>> Article.objects.filter(Q(pk=1) | Q(pk=2))
[Hello, Goodbye]
[<Article: Hello>, <Article: Goodbye>]
>>> Article.objects.filter(Q(pk=1) | Q(pk=2) | Q(pk=3))
[Hello, Goodbye, Hello and goodbye]
[<Article: Hello>, <Article: Goodbye>, <Article: Hello and goodbye>]
# Q arg objects are ANDed
# Q arg objects are ANDed
>>> Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye'))
[Hello and goodbye]
[<Article: Hello and goodbye>]
# Q arg AND order is irrelevant
>>> Article.objects.filter(Q(headline__contains='bye'), headline__startswith='Hello')
[Hello and goodbye]
[<Article: Hello and goodbye>]
# Try some arg queries with operations other than get_list
>>> Article.objects.get(Q(headline__startswith='Hello'), Q(headline__contains='bye'))
Hello and goodbye
<Article: Hello and goodbye>
>>> Article.objects.filter(Q(headline__startswith='Hello') | Q(headline__contains='bye')).count()
3
@ -87,17 +88,17 @@ Hello and goodbye
[{'headline': 'Hello and goodbye', 'pub_date': datetime.datetime(2005, 11, 29, 0, 0), 'id': 3}]
>>> Article.objects.filter(Q(headline__startswith='Hello')).in_bulk([1,2])
{1: Hello}
{1: <Article: Hello>}
# Demonstrating exclude with a Q object
>>> Article.objects.exclude(Q(headline__startswith='Hello'))
[Goodbye]
[<Article: Goodbye>]
# The 'complex_filter' method supports framework features such as
# The 'complex_filter' method supports framework features such as
# 'limit_choices_to' which normally take a single dictionary of lookup arguments
# but need to support arbitrary queries via Q objects too.
>>> Article.objects.complex_filter({'pk': 1})
[Hello]
[<Article: Hello>]
>>> Article.objects.complex_filter(Q(pk=1) | Q(pk=2))
[Hello, Goodbye]
[<Article: Hello>, <Article: Goodbye>]
"""

View File

@ -21,7 +21,7 @@ class Article(models.Model):
class Meta:
ordering = ('-pub_date', 'headline')
def __repr__(self):
def __str__(self):
return self.headline
API_TESTS = """
@ -39,26 +39,26 @@ API_TESTS = """
# By default, Article.objects.all() orders by pub_date descending, then
# headline ascending.
>>> Article.objects.all()
[Article 4, Article 2, Article 3, Article 1]
[<Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 1>]
# Override ordering with order_by, which is in the same format as the ordering
# attribute in models.
>>> Article.objects.order_by('headline')
[Article 1, Article 2, Article 3, Article 4]
[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>]
>>> Article.objects.order_by('pub_date', '-headline')
[Article 1, Article 3, Article 2, Article 4]
[<Article: Article 1>, <Article: Article 3>, <Article: Article 2>, <Article: Article 4>]
# Use the 'stop' part of slicing notation to limit the results.
>>> Article.objects.order_by('headline')[:2]
[Article 1, Article 2]
[<Article: Article 1>, <Article: Article 2>]
# Use the 'stop' and 'start' parts of slicing notation to offset the result list.
>>> Article.objects.order_by('headline')[1:3]
[Article 2, Article 3]
[<Article: Article 2>, <Article: Article 3>]
# Getting a single item should work too:
>>> Article.objects.all()[0]
Article 4
<Article: Article 4>
# Use '?' to order randomly. (We're using [...] in the output to indicate we
# don't know what order the output will be in.

View File

@ -12,7 +12,7 @@ class Article(models.Model):
headline = models.CharField(maxlength=100, default='Default headline')
pub_date = models.DateTimeField()
def __repr__(self):
def __str__(self):
return self.headline
API_TESTS = """
@ -35,11 +35,11 @@ API_TESTS = """
# get the first page (zero-based)
>>> paginator.get_page(0)
[Article 1, Article 2, Article 3, Article 4, Article 5]
[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>, <Article: Article 5>]
# get the second page
>>> paginator.get_page(1)
[Article 6, Article 7, Article 8, Article 9]
[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>]
# does the first page have a next or previous page?
>>> paginator.has_next_page(0)

View File

@ -21,7 +21,7 @@ class Thing(models.Model):
class Meta:
db_table = 'select'
def __repr__(self):
def __str__(self):
return self.when
API_TESTS = """
@ -39,18 +39,18 @@ a
h
>>> Thing.objects.order_by('when')
[a, h]
[<Thing: a>, <Thing: h>]
>>> v = Thing.objects.get(pk='a')
>>> print v.join
b
>>> print v.where
2005-01-01
>>> Thing.objects.order_by('select.when')
[a, h]
[<Thing: a>, <Thing: h>]
>>> Thing.objects.dates('where', 'year')
[datetime.datetime(2005, 1, 1, 0, 0), datetime.datetime(2006, 1, 1, 0, 0)]
>>> Thing.objects.filter(where__month=1)
[a]
[<Thing: a>]
"""

View File

@ -8,20 +8,23 @@ from django.db import models
class User(models.Model):
name = models.CharField(maxlength=200)
def __repr__(self):
def __str__(self):
return self.name
class Poll(models.Model):
question = models.CharField(maxlength=200)
creator = models.ForeignKey(User)
def __repr__(self):
def __str__(self):
return self.question
class Choice(models.Model):
name = models.CharField(maxlength=100)
poll = models.ForeignKey(Poll, related_name="poll_choice")
related_poll = models.ForeignKey(Poll, related_name="related_choice")
def __repr(self):
def __str(self):
return self.name
API_TESTS = """
@ -38,15 +41,15 @@ API_TESTS = """
>>> # Reverse lookups by field name:
>>> User.objects.get(poll__question__exact="What's the first question?")
John Doe
<User: John Doe>
>>> User.objects.get(poll__question__exact="What's the second question?")
Jim Bo
<User: Jim Bo>
>>> # Reverse lookups by related_name:
>>> Poll.objects.get(poll_choice__name__exact="This is the answer.")
What's the first question?
<Poll: What's the first question?>
>>> Poll.objects.get(related_choice__name__exact="This is the answer.")
What's the second question?
<Poll: What's the second question?>
>>> # If a related_name is given you can't use the field name instead:
>>> Poll.objects.get(choice__name__exact="This is the answer")

View File

@ -11,7 +11,7 @@ class Person(models.Model):
first_name = models.CharField(maxlength=20)
last_name = models.CharField(maxlength=20)
def __repr__(self):
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
def save(self):
@ -31,7 +31,7 @@ Before save
After save
>>> Person.objects.all()
[John Smith]
[<Person: John Smith>]
>>> p1.delete()
Before deletion

View File

View File

@ -1,7 +1,7 @@
"""
2. Adding __repr__() to models
2. Adding __str__() to models
Although it's not a strict requirement, each model should have a ``__repr__()``
Although it's not a strict requirement, each model should have a ``__str__()``
method to return a "human-readable" representation of the object. Do this not
only for your own sanity when dealing with the interactive prompt, but also
because objects' representations are used throughout Django's
@ -14,7 +14,7 @@ class Article(models.Model):
headline = models.CharField(maxlength=100)
pub_date = models.DateTimeField()
def __repr__(self):
def __str__(self):
return self.headline
API_TESTS = """
@ -23,9 +23,9 @@ API_TESTS = """
>>> a = Article(headline='Area man programs in Python', pub_date=datetime(2005, 7, 28))
>>> a.save()
>>> repr(a)
>>> str(a)
'Area man programs in Python'
>>> a
Area man programs in Python
<Article: Area man programs in Python>
"""

View File

@ -14,12 +14,19 @@ class Reporter(models.Model):
last_name = models.CharField(maxlength=30)
email = models.EmailField()
def __repr__(self):
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
API_TESTS = """
>>> from django.db import connection, transaction
"""
from django.conf import settings
building_docs = getattr(settings, 'BUILDING_DOCS', False)
if building_docs or settings.DATABASE_ENGINE != 'mysql':
API_TESTS += """
# the default behavior is to autocommit after each save() action
>>> def create_a_reporter_then_fail(first, last):
... a = Reporter(first_name=first, last_name=last)
@ -33,7 +40,7 @@ Exception: I meant to do that
# The object created before the exception still exists
>>> Reporter.objects.all()
[Alice Smith]
[<Reporter: Alice Smith>]
# the autocommit decorator works exactly the same as the default behavior
>>> autocomitted_create_then_fail = transaction.autocommit(create_a_reporter_then_fail)
@ -44,7 +51,7 @@ Exception: I meant to do that
# Same behavior as before
>>> Reporter.objects.all()
[Alice Smith, Ben Jones]
[<Reporter: Alice Smith>, <Reporter: Ben Jones>]
# With the commit_on_success decorator, the transaction is only comitted if the
# function doesn't throw an exception
@ -56,7 +63,7 @@ Exception: I meant to do that
# This time the object never got saved
>>> Reporter.objects.all()
[Alice Smith, Ben Jones]
[<Reporter: Alice Smith>, <Reporter: Ben Jones>]
# If there aren't any exceptions, the data will get saved
>>> def remove_a_reporter():
@ -66,7 +73,7 @@ Exception: I meant to do that
>>> remove_comitted_on_success = transaction.commit_on_success(remove_a_reporter)
>>> remove_comitted_on_success()
>>> Reporter.objects.all()
[Ben Jones]
[<Reporter: Ben Jones>]
# You can manually manage transactions if you really want to, but you
# have to remember to commit/rollback
@ -77,7 +84,7 @@ Exception: I meant to do that
>>> manually_managed = transaction.commit_manually(manually_managed)
>>> manually_managed()
>>> Reporter.objects.all()
[Ben Jones, Carol Doe]
[<Reporter: Ben Jones>, <Reporter: Carol Doe>]
# If you forget, you'll get bad errors
>>> def manually_managed_mistake():

View File

@ -17,7 +17,7 @@ class Person(models.Model):
favorite_moment = models.DateTimeField()
email = models.EmailField()
def __repr__(self):
def __str__(self):
return self.name
API_TESTS = """