summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2009-12-12 02:10:28 +0000
committerAlex Gaynor <alex.gaynor@gmail.com>2009-12-12 02:10:28 +0000
commitc88113683d6538dbc2f779ff57fd1ea20211abec (patch)
tree48756d324a15b8d1d990855ad3249f607a81ebf5 /django
parent353e0797920fbf5170653c9a9aabe2572a5ad214 (diff)
downloaddjango-c88113683d6538dbc2f779ff57fd1ea20211abec.tar.gz
[soc2009/multidb] Merged up to trunk r11810. There are many conflicts in this merge, these will be resolved in a subsequent commit.
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11812 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rw-r--r--django/conf/global_settings.py3
-rw-r--r--django/conf/locale/pl/LC_MESSAGES/django.po196
-rw-r--r--django/contrib/admin/options.py2
-rw-r--r--django/contrib/auth/__init__.py7
-rw-r--r--django/contrib/auth/backends.py2
-rw-r--r--django/contrib/auth/models.py57
-rw-r--r--django/contrib/auth/tests/__init__.py1
-rw-r--r--django/contrib/auth/tests/auth_backends.py149
-rw-r--r--django/contrib/gis/tests/relatedapp/tests.py11
-rw-r--r--django/core/management/commands/dumpdata.py5
-rw-r--r--django/db/backends/oracle/base.py4
-rw-r--r--django/db/models/base.py10
-rw-r--r--django/db/models/fields/related.py23
-rw-r--r--django/db/models/manager.py3
-rw-r--r--django/db/models/query.py8
-rw-r--r--django/db/models/sql/query.py104
-rw-r--r--django/forms/models.py4
-rw-r--r--django/template/defaulttags.py101
-rw-r--r--django/template/smartif.py192
19 files changed, 762 insertions, 120 deletions
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
index 9a08348fa5..6b8c2c885c 100644
--- a/django/conf/global_settings.py
+++ b/django/conf/global_settings.py
@@ -131,9 +131,12 @@ DATABASE_HOST = '' # Set to empty string for localhost. Not used wit
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
DATABASE_OPTIONS = {} # Set to empty dictionary for default.
+<<<<<<< HEAD:django/conf/global_settings.py
DATABASES = {
}
+=======
+>>>>>>> master:django/conf/global_settings.py
# The email backend to use. For possible shortcuts see django.core.mail.
# The default is to use the SMTP backend.
# Third-party backends can be specified by providing a Python path
diff --git a/django/conf/locale/pl/LC_MESSAGES/django.po b/django/conf/locale/pl/LC_MESSAGES/django.po
index 28f12561ee..6e8e59de92 100644
--- a/django/conf/locale/pl/LC_MESSAGES/django.po
+++ b/django/conf/locale/pl/LC_MESSAGES/django.po
@@ -5,7 +5,11 @@ msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
"POT-Creation-Date: 2009-10-25 20:56+0100\n"
+=======
+"POT-Creation-Date: 2009-12-11 10:11+0100\n"
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
"PO-Revision-Date: 2008-02-25 15:53+0100\n"
"Last-Translator: Jarek Zgoda <jarek.zgoda@gmail.com>\n"
"MIME-Version: 1.0\n"
@@ -223,7 +227,11 @@ msgstr "chiński tradycyjny"
msgid "Successfully deleted %(count)d %(items)s."
msgstr "Usunięto %(count)d %(items)s."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/actions.py:67 contrib/admin/options.py:1027
+=======
+#: contrib/admin/actions.py:67 contrib/admin/options.py:1034
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
msgid "Are you sure?"
msgstr "Jesteś pewien?"
@@ -310,87 +318,132 @@ msgstr "log"
msgid "log entries"
msgstr "logi"
-#: contrib/admin/options.py:133 contrib/admin/options.py:147
+#: contrib/admin/options.py:135 contrib/admin/options.py:149
msgid "None"
msgstr "brak"
-#: contrib/admin/options.py:519
+#: contrib/admin/options.py:522
#, python-format
msgid "Changed %s."
msgstr "Zmieniono %s"
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:519 contrib/admin/options.py:529
+=======
+#: contrib/admin/options.py:522 contrib/admin/options.py:532
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/comments/templates/comments/preview.html:16 forms/models.py:384
#: forms/models.py:596
msgid "and"
msgstr "i"
-#: contrib/admin/options.py:524
+#: contrib/admin/options.py:527
#, python-format
msgid "Added %(name)s \"%(object)s\"."
msgstr "Dodano %(name)s \"%(object)s\"."
-#: contrib/admin/options.py:528
+#: contrib/admin/options.py:531
#, python-format
msgid "Changed %(list)s for %(name)s \"%(object)s\"."
msgstr "Zmieniono %(list)s w %(name)s \"%(object)s\"."
-#: contrib/admin/options.py:533
+#: contrib/admin/options.py:536
#, python-format
msgid "Deleted %(name)s \"%(object)s\"."
msgstr "Usunięto %(name)s \"%(object)s\"."
-#: contrib/admin/options.py:537
+#: contrib/admin/options.py:540
msgid "No fields changed."
msgstr "Żadne pole nie zmienione."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:599 contrib/auth/admin.py:67
+=======
+#: contrib/admin/options.py:602 contrib/auth/admin.py:68
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "The %(name)s \"%(obj)s\" was added successfully."
msgstr "%(name)s \"%(obj)s\" dodany pomyślnie."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:603 contrib/admin/options.py:636
#: contrib/auth/admin.py:75
msgid "You may edit it again below."
msgstr "Możesz ponownie edytować wpis poniżej."
#: contrib/admin/options.py:613 contrib/admin/options.py:646
+=======
+#: contrib/admin/options.py:606 contrib/admin/options.py:639
+#: contrib/auth/admin.py:77
+msgid "You may edit it again below."
+msgstr "Możesz ponownie edytować wpis poniżej."
+
+#: contrib/admin/options.py:616 contrib/admin/options.py:649
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "You may add another %s below."
msgstr "Możesz dodać nowy wpis %s poniżej."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:634
+=======
+#: contrib/admin/options.py:637
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "The %(name)s \"%(obj)s\" was changed successfully."
msgstr "%(name)s \"%(obj)s\" zostało pomyślnie zmienione."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:642
+=======
+#: contrib/admin/options.py:645
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr ""
"%(name)s \"%(obj)s\" dodane pomyślnie. Możesz edytować ponownie wpis poniżej."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:773
+=======
+#: contrib/admin/options.py:778
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "Add %s"
msgstr "Dodaj %s"
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:804 contrib/admin/options.py:1005
+=======
+#: contrib/admin/options.py:810 contrib/admin/options.py:1012
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "%(name)s object with primary key %(key)r does not exist."
msgstr "Obiekt %(name)s o kluczu głównym %(key)r nie istnieje."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:861
+=======
+#: contrib/admin/options.py:867
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "Change %s"
msgstr "Zmień %s"
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:905
msgid "Database error"
msgstr "Błąd bazy danych"
#: contrib/admin/options.py:941
+=======
+#: contrib/admin/options.py:911
+msgid "Database error"
+msgstr "Błąd bazy danych"
+
+#: contrib/admin/options.py:947
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "%(count)s %(name)s was changed successfully."
msgid_plural "%(count)s %(name)s were changed successfully."
@@ -398,17 +451,29 @@ msgstr[0] "%(count)s %(name)s został pomyślnie zmieniony."
msgstr[1] "%(count)s %(name)s zostały pomyślnie zmienione."
msgstr[2] "%(count)s %(name)s zostało pomyślnie zmienionych."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:1020
+=======
+#: contrib/admin/options.py:1027
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
msgstr "%(name)s \"%(obj)s\" usunięty pomyślnie."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/options.py:1057
+=======
+#: contrib/admin/options.py:1064
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "Change history: %s"
msgstr "Historia zmian: %s"
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/sites.py:21 contrib/admin/views/decorators.py:14
+=======
+#: contrib/admin/sites.py:22 contrib/admin/views/decorators.py:14
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/auth/forms.py:80
msgid ""
"Please enter a correct username and password. Note that both fields are case-"
@@ -417,11 +482,19 @@ msgstr ""
"Proszę wpisać poprawną nazwę użytkownika i hasło. Uwaga: wielkość liter ma "
"znaczenie."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/sites.py:288 contrib/admin/views/decorators.py:40
msgid "Please log in again, because your session has expired."
msgstr "Twoja sesja wygasła, zaloguj się ponownie."
#: contrib/admin/sites.py:295 contrib/admin/views/decorators.py:47
+=======
+#: contrib/admin/sites.py:292 contrib/admin/views/decorators.py:40
+msgid "Please log in again, because your session has expired."
+msgstr "Twoja sesja wygasła, zaloguj się ponownie."
+
+#: contrib/admin/sites.py:299 contrib/admin/views/decorators.py:47
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
msgid ""
"Looks like your browser isn't configured to accept cookies. Please enable "
"cookies, reload this page, and try again."
@@ -429,27 +502,47 @@ msgstr ""
"Twoja przeglądarka nie chce akceptować ciasteczek. Zmień jej ustawienia i "
"spróbuj ponownie."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/sites.py:311 contrib/admin/sites.py:317
+=======
+#: contrib/admin/sites.py:315 contrib/admin/sites.py:321
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/views/decorators.py:66
msgid "Usernames cannot contain the '@' character."
msgstr "Nazwy użytkowników nie mogą zawierać znaku '@'."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/sites.py:314 contrib/admin/views/decorators.py:62
+=======
+#: contrib/admin/sites.py:318 contrib/admin/views/decorators.py:62
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "Your e-mail address is not your username. Try '%s' instead."
msgstr "Podany adres e-mail nie jest Twoją nazwą użytkownika. Spróbuj '%s'."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/sites.py:370
msgid "Site administration"
msgstr "Administracja stroną"
#: contrib/admin/sites.py:384 contrib/admin/templates/admin/login.html:26
+=======
+#: contrib/admin/sites.py:374
+msgid "Site administration"
+msgstr "Administracja stroną"
+
+#: contrib/admin/sites.py:388 contrib/admin/templates/admin/login.html:26
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/templates/registration/password_reset_complete.html:14
#: contrib/admin/views/decorators.py:20
msgid "Log in"
msgstr "Zaloguj się"
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/sites.py:429
+=======
+#: contrib/admin/sites.py:433
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "%s administration"
msgstr "%s - administracja"
@@ -670,8 +763,13 @@ msgid ""
"Are you sure you want to delete the selected %(object_name)s objects? All of "
"the following objects and their related items will be deleted:"
msgstr ""
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
"Czy chcesz skasować wybrane %(object_name)s? Następujące obiekty i zależne od "
"nich zostaną skasowane:"
+=======
+"Czy chcesz skasować wybrane %(object_name)s? Następujące obiekty i zależne "
+"od nich zostaną skasowane:"
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/admin/templates/admin/filter.html:2
#, python-format
@@ -977,7 +1075,7 @@ msgstr "Adres e-mail:"
msgid "Reset my password"
msgstr "Zresetuj moje hasło"
-#: contrib/admin/templatetags/admin_list.py:299
+#: contrib/admin/templatetags/admin_list.py:304
msgid "All dates"
msgstr "Wszystkie daty"
@@ -991,11 +1089,11 @@ msgstr "Zaznacz %s"
msgid "Select %s to change"
msgstr "Zaznacz %s aby zmienić"
-#: contrib/admin/views/template.py:37 contrib/sites/models.py:38
+#: contrib/admin/views/template.py:38 contrib/sites/models.py:38
msgid "site"
msgstr "strona"
-#: contrib/admin/views/template.py:39
+#: contrib/admin/views/template.py:40
msgid "template"
msgstr "szablon"
@@ -1209,37 +1307,37 @@ msgstr "Edytuj ten obiekt (nowe okno)"
msgid "As above, but opens the admin page in a new window."
msgstr "Jak wyżej, tyle że otwiera nowe okno."
-#: contrib/auth/admin.py:21
+#: contrib/auth/admin.py:22
msgid "Personal info"
msgstr "Dane osobowe"
-#: contrib/auth/admin.py:22
+#: contrib/auth/admin.py:23
msgid "Permissions"
msgstr "Uprawnienia"
-#: contrib/auth/admin.py:23
+#: contrib/auth/admin.py:24
msgid "Important dates"
msgstr "Ważne daty"
-#: contrib/auth/admin.py:24
+#: contrib/auth/admin.py:25
msgid "Groups"
msgstr "Grupy"
-#: contrib/auth/admin.py:80
+#: contrib/auth/admin.py:82
msgid "Add user"
msgstr "Dodaj użytkownika"
-#: contrib/auth/admin.py:106
+#: contrib/auth/admin.py:108
msgid "Password changed successfully."
msgstr "Hasło zostało zmienione pomyślnie."
-#: contrib/auth/admin.py:112
+#: contrib/auth/admin.py:114
#, python-format
msgid "Change password: %s"
msgstr "Zmień hasło: %s"
#: contrib/auth/forms.py:15 contrib/auth/forms.py:48
-#: contrib/auth/models.py:128
+#: contrib/auth/models.py:129
msgid ""
"Required. 30 characters or fewer. Alphanumeric characters only (letters, "
"digits and underscores)."
@@ -1329,31 +1427,31 @@ msgstr "uprawnienia"
msgid "group"
msgstr "grupa"
-#: contrib/auth/models.py:91 contrib/auth/models.py:138
+#: contrib/auth/models.py:91 contrib/auth/models.py:139
msgid "groups"
msgstr "grupy"
-#: contrib/auth/models.py:128
+#: contrib/auth/models.py:129
msgid "username"
msgstr "użytkownik"
-#: contrib/auth/models.py:129
+#: contrib/auth/models.py:130
msgid "first name"
msgstr "imię"
-#: contrib/auth/models.py:130
+#: contrib/auth/models.py:131
msgid "last name"
msgstr "nazwisko"
-#: contrib/auth/models.py:131
+#: contrib/auth/models.py:132
msgid "e-mail address"
msgstr "adres e-mail"
-#: contrib/auth/models.py:132
+#: contrib/auth/models.py:133
msgid "password"
msgstr "hasło"
-#: contrib/auth/models.py:132
+#: contrib/auth/models.py:133
msgid ""
"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change "
"password form</a>."
@@ -1361,19 +1459,19 @@ msgstr ""
"Użyj '[algo]$[salt]$[hexdigest]' lub <a href=\"password/\">formularza zmiany "
"hasła</a>."
-#: contrib/auth/models.py:133
+#: contrib/auth/models.py:134
msgid "staff status"
msgstr "w zespole"
-#: contrib/auth/models.py:133
+#: contrib/auth/models.py:134
msgid "Designates whether the user can log into this admin site."
msgstr "Oznacza czy użytkownik może zalogować się do panelu admina."
-#: contrib/auth/models.py:134
+#: contrib/auth/models.py:135
msgid "active"
msgstr "aktywny"
-#: contrib/auth/models.py:134
+#: contrib/auth/models.py:135
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
@@ -1381,11 +1479,11 @@ msgstr ""
"Oznacza czy użytkownika należy uważać za aktywnego. Odznacz to, zamiast "
"usuwać konta."
-#: contrib/auth/models.py:135
+#: contrib/auth/models.py:136
msgid "superuser status"
msgstr "status administratora"
-#: contrib/auth/models.py:135
+#: contrib/auth/models.py:136
msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
@@ -1393,15 +1491,15 @@ msgstr ""
"Oznacza, że ten użytkownik ma wszystkie uprawnienia bez jawnego "
"przypisywania ich."
-#: contrib/auth/models.py:136
+#: contrib/auth/models.py:137
msgid "last login"
msgstr "ostatnio zalogowany"
-#: contrib/auth/models.py:137
+#: contrib/auth/models.py:138
msgid "date joined"
msgstr "data przyłączenia"
-#: contrib/auth/models.py:139
+#: contrib/auth/models.py:140
msgid ""
"In addition to the permissions manually assigned, this user will also get "
"all permissions granted to each group he/she is in."
@@ -1409,24 +1507,28 @@ msgstr ""
"Oprócz uprawnień przypisanych bezpośrednio użytkownikowi otrzyma on "
"uprawnienia grup, do których należy."
-#: contrib/auth/models.py:140
+#: contrib/auth/models.py:141
msgid "user permissions"
msgstr "uprawnienia użytkownika"
-#: contrib/auth/models.py:144 contrib/comments/models.py:50
+#: contrib/auth/models.py:145 contrib/comments/models.py:50
#: contrib/comments/models.py:168
msgid "user"
msgstr "użytkownik"
-#: contrib/auth/models.py:145
+#: contrib/auth/models.py:146
msgid "users"
msgstr "użytkownicy"
-#: contrib/auth/models.py:301
+#: contrib/auth/models.py:334
msgid "message"
msgstr "wiadomość"
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: contrib/auth/views.py:58
+=======
+#: contrib/auth/views.py:60
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
msgid "Logged out"
msgstr "Wylogowany"
@@ -1774,7 +1876,7 @@ msgstr "strona statyczna"
msgid "flat pages"
msgstr "strony statyczne"
-#: contrib/formtools/wizard.py:130
+#: contrib/formtools/wizard.py:132
msgid ""
"We apologize, but your form has expired. Please continue filling out the "
"form from this page."
@@ -3811,6 +3913,10 @@ msgstr "Prowincja Północno-Zachodnia"
msgid "Western Cape"
msgstr "Prowincja Przylądkowa Zachodnia"
+#: contrib/messages/tests/base.py:97
+msgid "lazy message"
+msgstr "testowa wiadomość z opóźnioną ewaluacją"
+
#: contrib/redirects/models.py:7
msgid "redirect from"
msgstr "przekieruj z"
@@ -3919,14 +4025,22 @@ msgstr ""
msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format."
msgstr "Proszę wpisać poprawną godzinę w formacie HH:MM[:ss[.uuuuuu]]."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: db/models/fields/related.py:816
+=======
+#: db/models/fields/related.py:869
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
msgid ""
"Hold down \"Control\", or \"Command\" on a Mac, to select more than one."
msgstr ""
"Przytrzymaj wciśnięty klawisz \"Ctrl\" lub \"Command\" na Mac'u aby "
"zaznaczyć więcej niż jeden wybór."
+<<<<<<< HEAD:django/conf/locale/pl/LC_MESSAGES/django.po
#: db/models/fields/related.py:894
+=======
+#: db/models/fields/related.py:930
+>>>>>>> master:django/conf/locale/pl/LC_MESSAGES/django.po
#, python-format
msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
msgid_plural ""
@@ -4427,17 +4541,17 @@ msgstr "Y-m"
msgid "MONTH_DAY_FORMAT"
msgstr "m-d"
-#: views/generic/create_update.py:114
+#: views/generic/create_update.py:115
#, python-format
msgid "The %(verbose_name)s was created successfully."
msgstr "%(verbose_name)s zostało pomyślnie utworzone."
-#: views/generic/create_update.py:156
+#: views/generic/create_update.py:158
#, python-format
msgid "The %(verbose_name)s was updated successfully."
msgstr "%(verbose_name)s zostało pomyślnie zmienione."
-#: views/generic/create_update.py:198
+#: views/generic/create_update.py:201
#, python-format
msgid "The %(verbose_name)s was deleted."
msgstr "%(verbose_name)s zostało usunięte."
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index aab5ddbb6c..7193beeee8 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -1059,7 +1059,7 @@ class ModelAdmin(BaseModelAdmin):
content_type__id__exact = ContentType.objects.get_for_model(model).id
).select_related().order_by('action_time')
# If no history was found, see whether this object even exists.
- obj = get_object_or_404(model, pk=object_id)
+ obj = get_object_or_404(model, pk=unquote(object_id))
context = {
'title': _('Change history: %s') % force_unicode(obj),
'action_list': action_list,
diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py
index b89aee1682..eda3e386d3 100644
--- a/django/contrib/auth/__init__.py
+++ b/django/contrib/auth/__init__.py
@@ -1,4 +1,5 @@
import datetime
+from warnings import warn
from django.core.exceptions import ImproperlyConfigured
from django.utils.importlib import import_module
@@ -19,6 +20,12 @@ def load_backend(path):
cls = getattr(mod, attr)
except AttributeError:
raise ImproperlyConfigured, 'Module "%s" does not define a "%s" authentication backend' % (module, attr)
+ try:
+ getattr(cls, 'supports_object_permissions')
+ except AttributeError:
+ warn("Authentication backends without a `supports_object_permissions` attribute are deprecated. Please define it in %s." % cls,
+ PendingDeprecationWarning)
+ cls.supports_object_permissions = False
return cls()
def get_backends():
diff --git a/django/contrib/auth/backends.py b/django/contrib/auth/backends.py
index 05f98358b7..80a6bef136 100644
--- a/django/contrib/auth/backends.py
+++ b/django/contrib/auth/backends.py
@@ -11,6 +11,8 @@ class ModelBackend(object):
"""
Authenticates against django.contrib.auth.models.User.
"""
+ supports_object_permissions = False
+
# TODO: Model, login attribute name and password attribute name should be
# configurable.
def authenticate(self, username=None, password=None):
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
index 55fbb39bec..053761cb56 100644
--- a/django/contrib/auth/models.py
+++ b/django/contrib/auth/models.py
@@ -121,7 +121,8 @@ class UserManager(models.Manager):
return ''.join([choice(allowed_chars) for i in range(length)])
class User(models.Model):
- """Users within the Django authentication system are represented by this model.
+ """
+ Users within the Django authentication system are represented by this model.
Username and password are required. Other fields are optional.
"""
@@ -151,11 +152,16 @@ class User(models.Model):
return "/users/%s/" % urllib.quote(smart_str(self.username))
def is_anonymous(self):
- "Always returns False. This is a way of comparing User objects to anonymous users."
+ """
+ Always returns False. This is a way of comparing User objects to
+ anonymous users.
+ """
return False
def is_authenticated(self):
- """Always return True. This is a way to tell if the user has been authenticated in templates.
+ """
+ Always return True. This is a way to tell if the user has been
+ authenticated in templates.
"""
return True
@@ -194,30 +200,41 @@ class User(models.Model):
def has_usable_password(self):
return self.password != UNUSABLE_PASSWORD
- def get_group_permissions(self):
+ def get_group_permissions(self, obj=None):
"""
Returns a list of permission strings that this user has through
his/her groups. This method queries all available auth backends.
+ If an object is passed in, only permissions matching this object
+ are returned.
"""
permissions = set()
for backend in auth.get_backends():
if hasattr(backend, "get_group_permissions"):
- permissions.update(backend.get_group_permissions(self))
+ if obj is not None and backend.supports_object_permissions:
+ group_permissions = backend.get_group_permissions(self, obj)
+ else:
+ group_permissions = backend.get_group_permissions(self)
+ permissions.update(group_permissions)
return permissions
- def get_all_permissions(self):
+ def get_all_permissions(self, obj=None):
permissions = set()
for backend in auth.get_backends():
if hasattr(backend, "get_all_permissions"):
- permissions.update(backend.get_all_permissions(self))
+ if obj is not None and backend.supports_object_permissions:
+ all_permissions = backend.get_all_permissions(self, obj)
+ else:
+ all_permissions = backend.get_all_permissions(self)
+ permissions.update(all_permissions)
return permissions
- def has_perm(self, perm):
+ def has_perm(self, perm, obj=None):
"""
Returns True if the user has the specified permission. This method
queries all available auth backends, but returns immediately if any
backend returns True. Thus, a user who has permission from a single
- auth backend is assumed to have permission in general.
+ auth backend is assumed to have permission in general. If an object
+ is provided, permissions for this specific object are checked.
"""
# Inactive users have no permissions.
if not self.is_active:
@@ -230,14 +247,22 @@ class User(models.Model):
# Otherwise we need to check the backends.
for backend in auth.get_backends():
if hasattr(backend, "has_perm"):
- if backend.has_perm(self, perm):
- return True
+ if obj is not None and backend.supports_object_permissions:
+ if backend.has_perm(self, perm, obj):
+ return True
+ else:
+ if backend.has_perm(self, perm):
+ return True
return False
- def has_perms(self, perm_list):
- """Returns True if the user has each of the specified permissions."""
+ def has_perms(self, perm_list, obj=None):
+ """
+ Returns True if the user has each of the specified permissions.
+ If object is passed, it checks if the user has all required perms
+ for this object.
+ """
for perm in perm_list:
- if not self.has_perm(perm):
+ if not self.has_perm(perm, obj):
return False
return True
@@ -358,10 +383,10 @@ class AnonymousUser(object):
return self._user_permissions
user_permissions = property(_get_user_permissions)
- def has_perm(self, perm):
+ def has_perm(self, perm, obj=None):
return False
- def has_perms(self, perm_list):
+ def has_perms(self, perm_list, obj=None):
return False
def has_module_perms(self, module):
diff --git a/django/contrib/auth/tests/__init__.py b/django/contrib/auth/tests/__init__.py
index 14428d0fc8..9a078cf643 100644
--- a/django/contrib/auth/tests/__init__.py
+++ b/django/contrib/auth/tests/__init__.py
@@ -4,6 +4,7 @@ from django.contrib.auth.tests.views \
from django.contrib.auth.tests.forms import FORM_TESTS
from django.contrib.auth.tests.remote_user \
import RemoteUserTest, RemoteUserNoCreateTest, RemoteUserCustomTest
+from django.contrib.auth.tests.auth_backends import BackendTest, RowlevelBackendTest
from django.contrib.auth.tests.tokens import TOKEN_GENERATOR_TESTS
# The password for the fixture data users is 'password'
diff --git a/django/contrib/auth/tests/auth_backends.py b/django/contrib/auth/tests/auth_backends.py
new file mode 100644
index 0000000000..bf5611aef0
--- /dev/null
+++ b/django/contrib/auth/tests/auth_backends.py
@@ -0,0 +1,149 @@
+from django.conf import settings
+from django.contrib.auth.models import User, Group, Permission, AnonymousUser
+from django.contrib.contenttypes.models import ContentType
+from django.test import TestCase
+
+
+class BackendTest(TestCase):
+
+ backend = 'django.contrib.auth.backends.ModelBackend'
+
+ def setUp(self):
+ self.curr_auth = settings.AUTHENTICATION_BACKENDS
+ settings.AUTHENTICATION_BACKENDS = (self.backend,)
+ User.objects.create_user('test', 'test@example.com', 'test')
+
+ def tearDown(self):
+ settings.AUTHENTICATION_BACKENDS = self.curr_auth
+
+ def test_has_perm(self):
+ user = User.objects.get(username='test')
+ self.assertEqual(user.has_perm('auth.test'), False)
+ user.is_staff = True
+ user.save()
+ self.assertEqual(user.has_perm('auth.test'), False)
+ user.is_superuser = True
+ user.save()
+ self.assertEqual(user.has_perm('auth.test'), True)
+ user.is_staff = False
+ user.is_superuser = False
+ user.save()
+ self.assertEqual(user.has_perm('auth.test'), False)
+
+ def test_custom_perms(self):
+ user = User.objects.get(username='test')
+ content_type=ContentType.objects.get_for_model(Group)
+ perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
+ user.user_permissions.add(perm)
+ user.save()
+
+ # reloading user to purge the _perm_cache
+ user = User.objects.get(username='test')
+ self.assertEqual(user.get_all_permissions() == set([u'auth.test']), True)
+ self.assertEqual(user.get_group_permissions(), set([]))
+ self.assertEqual(user.has_module_perms('Group'), False)
+ self.assertEqual(user.has_module_perms('auth'), True)
+ perm = Permission.objects.create(name='test2', content_type=content_type, codename='test2')
+ user.user_permissions.add(perm)
+ user.save()
+ perm = Permission.objects.create(name='test3', content_type=content_type, codename='test3')
+ user.user_permissions.add(perm)
+ user.save()
+ user = User.objects.get(username='test')
+ self.assertEqual(user.get_all_permissions(), set([u'auth.test2', u'auth.test', u'auth.test3']))
+ self.assertEqual(user.has_perm('test'), False)
+ self.assertEqual(user.has_perm('auth.test'), True)
+ self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), True)
+ perm = Permission.objects.create(name='test_group', content_type=content_type, codename='test_group')
+ group = Group.objects.create(name='test_group')
+ group.permissions.add(perm)
+ group.save()
+ user.groups.add(group)
+ user = User.objects.get(username='test')
+ exp = set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group'])
+ self.assertEqual(user.get_all_permissions(), exp)
+ self.assertEqual(user.get_group_permissions(), set([u'auth.test_group']))
+ self.assertEqual(user.has_perms(['auth.test3', 'auth.test_group']), True)
+
+ user = AnonymousUser()
+ self.assertEqual(user.has_perm('test'), False)
+ self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), False)
+
+
+class TestObj(object):
+ pass
+
+
+class SimpleRowlevelBackend(object):
+ supports_object_permissions = True
+
+ def has_perm(self, user, perm, obj=None):
+ if not obj:
+ return # We only support row level perms
+
+ if isinstance(obj, TestObj):
+ if user.username == 'test2':
+ return True
+ elif isinstance(user, AnonymousUser) and perm == 'anon':
+ return True
+ return False
+
+ def get_all_permissions(self, user, obj=None):
+ if not obj:
+ return [] # We only support row level perms
+
+ if not isinstance(obj, TestObj):
+ return ['none']
+
+ if user.username == 'test2':
+ return ['simple', 'advanced']
+ else:
+ return ['simple']
+
+ def get_group_permissions(self, user, obj=None):
+ if not obj:
+ return # We only support row level perms
+
+ if not isinstance(obj, TestObj):
+ return ['none']
+
+ if 'test_group' in [group.name for group in user.groups.all()]:
+ return ['group_perm']
+ else:
+ return ['none']
+
+
+class RowlevelBackendTest(TestCase):
+
+ backend = 'django.contrib.auth.tests.auth_backends.SimpleRowlevelBackend'
+
+ def setUp(self):
+ self.curr_auth = settings.AUTHENTICATION_BACKENDS
+ settings.AUTHENTICATION_BACKENDS = self.curr_auth + (self.backend,)
+ self.user1 = User.objects.create_user('test', 'test@example.com', 'test')
+ self.user2 = User.objects.create_user('test2', 'test2@example.com', 'test')
+ self.user3 = AnonymousUser()
+ self.user4 = User.objects.create_user('test4', 'test4@example.com', 'test')
+
+ def tearDown(self):
+ settings.AUTHENTICATION_BACKENDS = self.curr_auth
+
+ def test_has_perm(self):
+ self.assertEqual(self.user1.has_perm('perm', TestObj()), False)
+ self.assertEqual(self.user2.has_perm('perm', TestObj()), True)
+ self.assertEqual(self.user2.has_perm('perm'), False)
+ self.assertEqual(self.user2.has_perms(['simple', 'advanced'], TestObj()), True)
+ self.assertEqual(self.user3.has_perm('perm', TestObj()), False)
+ self.assertEqual(self.user3.has_perm('anon', TestObj()), False)
+ self.assertEqual(self.user3.has_perms(['simple', 'advanced'], TestObj()), False)
+
+ def test_get_all_permissions(self):
+ self.assertEqual(self.user1.get_all_permissions(TestObj()), set(['simple']))
+ self.assertEqual(self.user2.get_all_permissions(TestObj()), set(['simple', 'advanced']))
+ self.assertEqual(self.user2.get_all_permissions(), set([]))
+
+ def test_get_group_permissions(self):
+ content_type=ContentType.objects.get_for_model(Group)
+ group = Group.objects.create(name='test_group')
+ self.user4.groups.add(group)
+ self.assertEqual(self.user4.get_group_permissions(TestObj()), set(['group_perm']))
diff --git a/django/contrib/gis/tests/relatedapp/tests.py b/django/contrib/gis/tests/relatedapp/tests.py
index 2bdf29304d..4166784a07 100644
--- a/django/contrib/gis/tests/relatedapp/tests.py
+++ b/django/contrib/gis/tests/relatedapp/tests.py
@@ -279,11 +279,19 @@ class RelatedGeoModelTest(unittest.TestCase):
def test14_collect(self):
"Testing the `collect` GeoQuerySet method and `Collect` aggregate."
# Reference query:
+<<<<<<< HEAD:django/contrib/gis/tests/relatedapp/tests.py
# SELECT AsText(ST_Collect("relatedapp_location"."point")) FROM "relatedapp_city" LEFT OUTER JOIN
# "relatedapp_location" ON ("relatedapp_city"."location_id" = "relatedapp_location"."id")
# WHERE "relatedapp_city"."state" = 'TX';
ref_geom = fromstr('MULTIPOINT(-97.516111 33.058333,-96.801611 32.782057,-95.363151 29.763374,-96.801611 32.782057)')
+=======
+ # SELECT AsText(ST_Collect("relatedapp_location"."point")) FROM "relatedapp_city" LEFT OUTER JOIN
+ # "relatedapp_location" ON ("relatedapp_city"."location_id" = "relatedapp_location"."id")
+ # WHERE "relatedapp_city"."state" = 'TX';
+ ref_geom = fromstr('MULTIPOINT(-97.516111 33.058333,-96.801611 32.782057,-95.363151 29.763374,-96.801611 32.782057)')
+
+>>>>>>> master:django/contrib/gis/tests/relatedapp/tests.py
c1 = City.objects.filter(state='TX').collect(field_name='location__point')
c2 = City.objects.filter(state='TX').aggregate(Collect('location__point'))['location__point__collect']
@@ -292,7 +300,10 @@ class RelatedGeoModelTest(unittest.TestCase):
# consolidate -- that's why 4 points in MultiPoint.
self.assertEqual(4, len(coll))
self.assertEqual(ref_geom, coll)
+<<<<<<< HEAD:django/contrib/gis/tests/relatedapp/tests.py
+=======
+>>>>>>> master:django/contrib/gis/tests/relatedapp/tests.py
# TODO: Related tests for KML, GML, and distance lookups.
diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py
index 215e58605e..6ca6d9c699 100644
--- a/django/core/management/commands/dumpdata.py
+++ b/django/core/management/commands/dumpdata.py
@@ -82,9 +82,14 @@ class Command(BaseCommand):
model_list = get_models(app)
for model in model_list:
+<<<<<<< HEAD:django/core/management/commands/dumpdata.py
# Don't serialize proxy models, or models that haven't been synchronized
if not model._meta.proxy and model._meta.db_table in tables:
objects.extend(model._default_manager.using(using).all())
+=======
+ if not model._meta.proxy:
+ objects.extend(model._default_manager.all())
+>>>>>>> master:django/core/management/commands/dumpdata.py
try:
return serializers.serialize(format, objects, indent=indent)
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index cc85685e5b..dd085008a2 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -357,7 +357,11 @@ class DatabaseWrapper(BaseDatabaseWrapper):
cursor = None
if not self._valid_connection():
conn_string = convert_unicode(self._connect_string())
+<<<<<<< HEAD:django/db/backends/oracle/base.py
self.connection = Database.connect(conn_string, **self.settings_dict['OPTIONS'])
+=======
+ self.connection = Database.connect(conn_string, **self.settings_dict['DATABASE_OPTIONS'])
+>>>>>>> master:django/db/backends/oracle/base.py
cursor = FormatStylePlaceholderCursor(self.connection)
# Set oracle date to ansi date format. This only needs to execute
# once when we create a new connection. We also set the Territory
diff --git a/django/db/models/base.py b/django/db/models/base.py
index eb484ea3bc..8a6ffd12b3 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -230,6 +230,7 @@ class ModelBase(type):
signals.class_prepared.send(sender=cls)
+<<<<<<< HEAD:django/db/models/base.py
class ModelState(object):
"""
A class for storing instance state
@@ -237,6 +238,8 @@ class ModelState(object):
def __init__(self, db=None):
self.db = db
+=======
+>>>>>>> master:django/db/models/base.py
class Model(object):
__metaclass__ = ModelBase
_deferred = False
@@ -488,7 +491,11 @@ class Model(object):
if pk_set:
# Determine whether a record with the primary key already exists.
if (force_update or (not force_insert and
+<<<<<<< HEAD:django/db/models/base.py
manager.using(using).filter(pk=pk_val).exists())):
+=======
+ manager.filter(pk=pk_val).exists())):
+>>>>>>> master:django/db/models/base.py
# It does already exist, so do an UPDATE.
if force_update or non_pks:
values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
@@ -527,7 +534,10 @@ class Model(object):
# Store the database on which the object was saved
self._state.db = using
+<<<<<<< HEAD:django/db/models/base.py
# Signal that the save is complete
+=======
+>>>>>>> master:django/db/models/base.py
if origin and not meta.auto_created:
signals.post_save.send(sender=origin, instance=self,
created=(not record_exists), raw=raw)
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index f8ae5b1f4e..e3520965e1 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -474,7 +474,11 @@ def create_many_related_manager(superclass, rel=False):
if not rel.through._meta.auto_created:
opts = through._meta
raise AttributeError, "Cannot use create() on a ManyToManyField which specifies an intermediary model. Use %s.%s's Manager instead." % (opts.app_label, opts.object_name)
+<<<<<<< HEAD:django/db/models/fields/related.py
new_obj = super(ManyRelatedManager, self).using(self.instance._state.db).create(**kwargs)
+=======
+ new_obj = super(ManyRelatedManager, self).create(**kwargs)
+>>>>>>> master:django/db/models/fields/related.py
self.add(new_obj)
return new_obj
create.alters_data = True
@@ -501,15 +505,22 @@ def create_many_related_manager(superclass, rel=False):
new_ids = set()
for obj in objs:
if isinstance(obj, self.model):
+<<<<<<< HEAD:django/db/models/fields/related.py
if obj._state.db != self.instance._state.db:
raise ValueError('Cannot add "%r": instance is on database "%s", value is is on database "%s"' %
(obj, self.instance._state.db, obj._state.db))
+=======
+>>>>>>> master:django/db/models/fields/related.py
new_ids.add(obj.pk)
elif isinstance(obj, Model):
raise TypeError, "'%s' instance expected" % self.model._meta.object_name
else:
new_ids.add(obj)
+<<<<<<< HEAD:django/db/models/fields/related.py
vals = self.through._default_manager.using(self.instance._state.db).values_list(target_field_name, flat=True)
+=======
+ vals = self.through._default_manager.values_list(target_field_name, flat=True)
+>>>>>>> master:django/db/models/fields/related.py
vals = vals.filter(**{
source_field_name: self._pk_val,
'%s__in' % target_field_name: new_ids,
@@ -518,7 +529,11 @@ def create_many_related_manager(superclass, rel=False):
# Add the ones that aren't there already
for obj_id in (new_ids - vals):
+<<<<<<< HEAD:django/db/models/fields/related.py
self.through._default_manager.using(self.instance._state.db).create(**{
+=======
+ self.through._default_manager.create(**{
+>>>>>>> master:django/db/models/fields/related.py
'%s_id' % source_field_name: self._pk_val,
'%s_id' % target_field_name: obj_id,
})
@@ -538,14 +553,22 @@ def create_many_related_manager(superclass, rel=False):
else:
old_ids.add(obj)
# Remove the specified objects from the join table
+<<<<<<< HEAD:django/db/models/fields/related.py
self.through._default_manager.using(self.instance._state.db).filter(**{
+=======
+ self.through._default_manager.filter(**{
+>>>>>>> master:django/db/models/fields/related.py
source_field_name: self._pk_val,
'%s__in' % target_field_name: old_ids
}).delete()
def _clear_items(self, source_field_name):
# source_col_name: the PK colname in join_table for the source object
+<<<<<<< HEAD:django/db/models/fields/related.py
self.through._default_manager.using(self.instance._state.db).filter(**{
+=======
+ self.through._default_manager.filter(**{
+>>>>>>> master:django/db/models/fields/related.py
source_field_name: self._pk_val
}).delete()
diff --git a/django/db/models/manager.py b/django/db/models/manager.py
index d752519f5c..2bdc615615 100644
--- a/django/db/models/manager.py
+++ b/django/db/models/manager.py
@@ -172,9 +172,12 @@ class Manager(object):
def only(self, *args, **kwargs):
return self.get_query_set().only(*args, **kwargs)
+<<<<<<< HEAD:django/db/models/manager.py
def using(self, *args, **kwargs):
return self.get_query_set().using(*args, **kwargs)
+=======
+>>>>>>> master:django/db/models/manager.py
def exists(self, *args, **kwargs):
return self.get_query_set().exists(*args, **kwargs)
diff --git a/django/db/models/query.py b/django/db/models/query.py
index eba6f2f142..05d2514f69 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -3,8 +3,12 @@ The main QuerySet implementation. This provides the public API for the ORM.
"""
from copy import deepcopy
+<<<<<<< HEAD:django/db/models/query.py
from django.db import connections, transaction, IntegrityError, DEFAULT_DB_ALIAS
+=======
+from django.db import connection, transaction, IntegrityError
+>>>>>>> master:django/db/models/query.py
from django.db.models.aggregates import Aggregate
from django.db.models.fields import DateField
from django.db.models.query_utils import Q, select_related_descend, CollectedObjects, CyclicDependency, deferred_class_factory
@@ -476,7 +480,11 @@ class QuerySet(object):
def exists(self):
if self._result_cache is None:
+<<<<<<< HEAD:django/db/models/query.py
return self.query.has_results(using=self.db)
+=======
+ return self.query.has_results()
+>>>>>>> master:django/db/models/query.py
return bool(self._result_cache)
##################################################
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 0a53fc3bae..3176e7e9df 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -22,8 +22,12 @@ from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.where import WhereNode, Constraint, EverythingNode, AND, OR
from django.core.exceptions import FieldError
+<<<<<<< HEAD:django/db/models/sql/query.py
__all__ = ['Query']
+=======
+__all__ = ['Query', 'BaseQuery']
+>>>>>>> master:django/db/models/sql/query.py
class Query(object):
"""
@@ -337,7 +341,11 @@ class Query(object):
return number
+<<<<<<< HEAD:django/db/models/sql/query.py
def has_results(self, using):
+=======
+ def has_results(self):
+>>>>>>> master:django/db/models/sql/query.py
q = self.clone()
q.add_extra({'a': 1}, None, None, None, None, None)
q.add_fields(())
@@ -345,8 +353,104 @@ class Query(object):
q.set_aggregate_mask(())
q.clear_ordering()
q.set_limits(high=1)
+<<<<<<< HEAD:django/db/models/sql/query.py
compiler = q.get_compiler(using=using)
return bool(compiler.execute_sql(SINGLE))
+=======
+ return bool(q.execute_sql(SINGLE))
+
+ def as_sql(self, with_limits=True, with_col_aliases=False):
+ """
+ Creates the SQL for this query. Returns the SQL string and list of
+ parameters.
+
+ If 'with_limits' is False, any limit/offset information is not included
+ in the query.
+ """
+ self.pre_sql_setup()
+ out_cols = self.get_columns(with_col_aliases)
+ ordering, ordering_group_by = self.get_ordering()
+
+ # This must come after 'select' and 'ordering' -- see docstring of
+ # get_from_clause() for details.
+ from_, f_params = self.get_from_clause()
+
+ qn = self.quote_name_unless_alias
+ where, w_params = self.where.as_sql(qn=qn)
+ having, h_params = self.having.as_sql(qn=qn)
+ params = []
+ for val in self.extra_select.itervalues():
+ params.extend(val[1])
+
+ result = ['SELECT']
+ if self.distinct:
+ result.append('DISTINCT')
+ result.append(', '.join(out_cols + self.ordering_aliases))
+
+ result.append('FROM')
+ result.extend(from_)
+ params.extend(f_params)
+
+ if where:
+ result.append('WHERE %s' % where)
+ params.extend(w_params)
+ if self.extra_where:
+ if not where:
+ result.append('WHERE')
+ else:
+ result.append('AND')
+ result.append(' AND '.join(self.extra_where))
+
+ grouping, gb_params = self.get_grouping()
+ if grouping:
+ if ordering:
+ # If the backend can't group by PK (i.e., any database
+ # other than MySQL), then any fields mentioned in the
+ # ordering clause needs to be in the group by clause.
+ if not self.connection.features.allows_group_by_pk:
+ for col, col_params in ordering_group_by:
+ if col not in grouping:
+ grouping.append(str(col))
+ gb_params.extend(col_params)
+ else:
+ ordering = self.connection.ops.force_no_ordering()
+ result.append('GROUP BY %s' % ', '.join(grouping))
+ params.extend(gb_params)
+
+ if having:
+ result.append('HAVING %s' % having)
+ params.extend(h_params)
+
+ if ordering:
+ result.append('ORDER BY %s' % ', '.join(ordering))
+
+ if with_limits:
+ if self.high_mark is not None:
+ result.append('LIMIT %d' % (self.high_mark - self.low_mark))
+ if self.low_mark:
+ if self.high_mark is None:
+ val = self.connection.ops.no_limit_value()
+ if val:
+ result.append('LIMIT %d' % val)
+ result.append('OFFSET %d' % self.low_mark)
+
+ params.extend(self.extra_params)
+ return ' '.join(result), tuple(params)
+
+ def as_nested_sql(self):
+ """
+ Perform the same functionality as the as_sql() method, returning an
+ SQL string and parameters. However, the alias prefixes are bumped
+ beforehand (in a copy -- the current query isn't changed) and any
+ ordering is removed.
+
+ Used when nesting this query inside another.
+ """
+ obj = self.clone()
+ obj.clear_ordering(True)
+ obj.bump_prefix()
+ return obj.as_sql()
+>>>>>>> master:django/db/models/sql/query.py
def combine(self, rhs, connector):
"""
diff --git a/django/forms/models.py b/django/forms/models.py
index 6985448659..991c1fbdc6 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -471,8 +471,12 @@ class BaseModelFormSet(BaseFormSet):
pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
pk = self.data[pk_key]
pk_field = self.model._meta.pk
+<<<<<<< HEAD:django/forms/models.py
pk = pk_field.get_db_prep_lookup('exact', pk,
connection=connections[self.get_queryset().db])
+=======
+ pk = pk_field.get_db_prep_lookup('exact', pk)
+>>>>>>> master:django/forms/models.py
if isinstance(pk, list):
pk = pk[0]
kwargs['instance'] = self._existing_object(pk)
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 6d57cdeef8..77b9b9795c 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -11,6 +11,7 @@ except NameError:
from django.template import Node, NodeList, Template, Context, Variable
from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
from django.template import get_library, Library, InvalidTemplateLibrary
+from django.template.smartif import IfParser, Literal
from django.conf import settings
from django.utils.encoding import smart_str, smart_unicode
from django.utils.itercompat import groupby
@@ -227,10 +228,9 @@ class IfEqualNode(Node):
return self.nodelist_false.render(context)
class IfNode(Node):
- def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type):
- self.bool_exprs = bool_exprs
+ def __init__(self, var, nodelist_true, nodelist_false=None):
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
- self.link_type = link_type
+ self.var = var
def __repr__(self):
return "<If node>"
@@ -250,28 +250,10 @@ class IfNode(Node):
return nodes
def render(self, context):
- if self.link_type == IfNode.LinkTypes.or_:
- for ifnot, bool_expr in self.bool_exprs:
- try:
- value = bool_expr.resolve(context, True)
- except VariableDoesNotExist:
- value = None
- if (value and not ifnot) or (ifnot and not value):
- return self.nodelist_true.render(context)
- return self.nodelist_false.render(context)
- else:
- for ifnot, bool_expr in self.bool_exprs:
- try:
- value = bool_expr.resolve(context, True)
- except VariableDoesNotExist:
- value = None
- if not ((value and not ifnot) or (ifnot and not value)):
- return self.nodelist_false.render(context)
+ if self.var.eval(context):
return self.nodelist_true.render(context)
-
- class LinkTypes:
- and_ = 0,
- or_ = 1
+ else:
+ return self.nodelist_false.render(context)
class RegroupNode(Node):
def __init__(self, target, expression, var_name):
@@ -761,6 +743,27 @@ def ifnotequal(parser, token):
return do_ifequal(parser, token, True)
ifnotequal = register.tag(ifnotequal)
+class TemplateLiteral(Literal):
+ def __init__(self, value, text):
+ self.value = value
+ self.text = text # for better error messages
+
+ def display(self):
+ return self.text
+
+ def eval(self, context):
+ return self.value.resolve(context, ignore_failures=True)
+
+class TemplateIfParser(IfParser):
+ error_class = TemplateSyntaxError
+
+ def __init__(self, parser, *args, **kwargs):
+ self.template_parser = parser
+ return super(TemplateIfParser, self).__init__(*args, **kwargs)
+
+ def create_var(self, value):
+ return TemplateLiteral(self.template_parser.compile_filter(value), value)
+
#@register.tag(name="if")
def do_if(parser, token):
"""
@@ -805,47 +808,21 @@ def do_if(parser, token):
There are some athletes and absolutely no coaches.
{% endif %}
- ``if`` tags do not allow ``and`` and ``or`` clauses with the same tag,
- because the order of logic would be ambigous. For example, this is
- invalid::
+ Comparison operators are also available, and the use of filters is also
+ allowed, for example:
- {% if athlete_list and coach_list or cheerleader_list %}
+ {% if articles|length >= 5 %}...{% endif %}
- If you need to combine ``and`` and ``or`` to do advanced logic, just use
- nested if tags. For example::
+ Arguments and operators _must_ have a space between them, so
+ ``{% if 1>2 %}`` is not a valid if tag.
- {% if athlete_list %}
- {% if coach_list or cheerleader_list %}
- We have athletes, and either coaches or cheerleaders!
- {% endif %}
- {% endif %}
+ All supported operators are: ``or``, ``and``, ``in``, ``==`` (or ``=``),
+ ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
+
+ Operator precedence follows Python.
"""
- bits = token.contents.split()
- del bits[0]
- if not bits:
- raise TemplateSyntaxError("'if' statement requires at least one argument")
- # Bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d']
- bitstr = ' '.join(bits)
- boolpairs = bitstr.split(' and ')
- boolvars = []
- if len(boolpairs) == 1:
- link_type = IfNode.LinkTypes.or_
- boolpairs = bitstr.split(' or ')
- else:
- link_type = IfNode.LinkTypes.and_
- if ' or ' in bitstr:
- raise TemplateSyntaxError, "'if' tags can't mix 'and' and 'or'"
- for boolpair in boolpairs:
- if ' ' in boolpair:
- try:
- not_, boolvar = boolpair.split()
- except ValueError:
- raise TemplateSyntaxError, "'if' statement improperly formatted"
- if not_ != 'not':
- raise TemplateSyntaxError, "Expected 'not' in if statement"
- boolvars.append((True, parser.compile_filter(boolvar)))
- else:
- boolvars.append((False, parser.compile_filter(boolpair)))
+ bits = token.split_contents()[1:]
+ var = TemplateIfParser(parser, bits).parse()
nodelist_true = parser.parse(('else', 'endif'))
token = parser.next_token()
if token.contents == 'else':
@@ -853,7 +830,7 @@ def do_if(parser, token):
parser.delete_first_token()
else:
nodelist_false = NodeList()
- return IfNode(boolvars, nodelist_true, nodelist_false, link_type)
+ return IfNode(var, nodelist_true, nodelist_false)
do_if = register.tag("if", do_if)
#@register.tag
diff --git a/django/template/smartif.py b/django/template/smartif.py
new file mode 100644
index 0000000000..e8a08e9087
--- /dev/null
+++ b/django/template/smartif.py
@@ -0,0 +1,192 @@
+"""
+Parser and utilities for the smart 'if' tag
+"""
+import operator
+
+# Using a simple top down parser, as described here:
+# http://effbot.org/zone/simple-top-down-parsing.htm.
+# 'led' = left denotation
+# 'nud' = null denotation
+# 'bp' = binding power (left = lbp, right = rbp)
+
+class TokenBase(object):
+ """
+ Base class for operators and literals, mainly for debugging and for throwing
+ syntax errors.
+ """
+ id = None # node/token type name
+ value = None # used by literals
+ first = second = None # used by tree nodes
+
+ def nud(self, parser):
+ # Null denotation - called in prefix context
+ raise parser.error_class(
+ "Not expecting '%s' in this position in if tag." % self.id
+ )
+
+ def led(self, left, parser):
+ # Left denotation - called in infix context
+ raise parser.error_class(
+ "Not expecting '%s' as infix operator in if tag." % self.id
+ )
+
+ def display(self):
+ """
+ Returns what to display in error messages for this node
+ """
+ return self.id
+
+ def __repr__(self):
+ out = [str(x) for x in [self.id, self.first, self.second] if x is not None]
+ return "(" + " ".join(out) + ")"
+
+
+def infix(bp, func):
+ """
+ Creates an infix operator, given a binding power and a function that
+ evaluates the node
+ """
+ class Operator(TokenBase):
+ lbp = bp
+
+ def led(self, left, parser):
+ self.first = left
+ self.second = parser.expression(bp)
+ return self
+
+ def eval(self, context):
+ try:
+ return func(self.first.eval(context), self.second.eval(context))
+ except Exception:
+ # Templates shouldn't throw exceptions when rendering. We are
+ # most likely to get exceptions for things like {% if foo in bar
+ # %} where 'bar' does not support 'in', so default to False
+ return False
+
+ return Operator
+
+
+def prefix(bp, func):
+ """
+ Creates a prefix operator, given a binding power and a function that
+ evaluates the node.
+ """
+ class Operator(TokenBase):
+ lbp = bp
+
+ def nud(self, parser):
+ self.first = parser.expression(bp)
+ self.second = None
+ return self
+
+ def eval(self, context):
+ try:
+ return func(self.first.eval(context))
+ except Exception:
+ return False
+
+ return Operator
+
+
+# Operator precedence follows Python.
+# NB - we can get slightly more accurate syntax error messages by not using the
+# same object for '==' and '='.
+
+OPERATORS = {
+ 'or': infix(6, lambda x, y: x or y),
+ 'and': infix(7, lambda x, y: x and y),
+ 'not': prefix(8, operator.not_),
+ 'in': infix(9, lambda x, y: x in y),
+ '=': infix(10, operator.eq),
+ '==': infix(10, operator.eq),
+ '!=': infix(10, operator.ne),
+ '>': infix(10, operator.gt),
+ '>=': infix(10, operator.ge),
+ '<': infix(10, operator.lt),
+ '<=': infix(10, operator.le),
+}
+
+# Assign 'id' to each:
+for key, op in OPERATORS.items():
+ op.id = key
+
+
+class Literal(TokenBase):
+ """
+ A basic self-resolvable object similar to a Django template variable.
+ """
+ # IfParser uses Literal in create_var, but TemplateIfParser overrides
+ # create_var so that a proper implementation that actually resolves
+ # variables, filters etc is used.
+ id = "literal"
+ lbp = 0
+
+ def __init__(self, value):
+ self.value = value
+
+ def display(self):
+ return repr(self.value)
+
+ def nud(self, parser):
+ return self
+
+ def eval(self, context):
+ return self.value
+
+ def __repr__(self):
+ return "(%s %r)" % (self.id, self.value)
+
+
+class EndToken(TokenBase):
+ lbp = 0
+
+ def nud(self, parser):
+ raise parser.error_class("Unexpected end of expression in if tag.")
+
+EndToken = EndToken()
+
+
+class IfParser(object):
+ error_class = ValueError
+
+ def __init__(self, tokens):
+ self.tokens = map(self.translate_tokens, tokens)
+ self.pos = 0
+ self.current_token = self.next()
+
+ def translate_tokens(self, token):
+ try:
+ op = OPERATORS[token]
+ except (KeyError, TypeError):
+ return self.create_var(token)
+ else:
+ return op()
+
+ def next(self):
+ if self.pos >= len(self.tokens):
+ return EndToken
+ else:
+ retval = self.tokens[self.pos]
+ self.pos += 1
+ return retval
+
+ def parse(self):
+ retval = self.expression()
+ # Check that we have exhausted all the tokens
+ if self.current_token is not EndToken:
+ raise self.error_class("Unused '%s' at end of if expression." %
+ self.current_token.display())
+ return retval
+
+ def expression(self, rbp=0):
+ t = self.current_token
+ self.current_token = self.next()
+ left = t.nud(self)
+ while rbp < self.current_token.lbp:
+ t = self.current_token
+ self.current_token = self.next()
+ left = t.led(left, self)
+ return left
+
+ def create_var(self, value):
+ return Literal(value)