1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

[soc2009/multidb] Merged up to trunk r11885.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11888 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2009-12-17 15:15:26 +00:00
parent fc36471d1b
commit f9412b4d21
19 changed files with 420 additions and 280 deletions

View File

@ -385,6 +385,7 @@ answer newbie questions, and generally made Django that much better:
Vinay Sajip <vinay_sajip@yahoo.co.uk>
Kadesarin Sanjek
Massimo Scamarcia <massimo.scamarcia@gmail.com>
Paulo Scardine <paulo@scardine.com.br>
David Schein
Bernd Schlapsi
schwank@gmail.com

View File

@ -5,7 +5,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-11 10:11+0100\n"
"POT-Creation-Date: 2009-12-16 19:53+0100\n"
"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 +223,7 @@ msgstr "chiński tradycyjny"
msgid "Successfully deleted %(count)d %(items)s."
msgstr "Usunięto %(count)d %(items)s."
#: contrib/admin/actions.py:67 contrib/admin/options.py:1034
#: contrib/admin/actions.py:67 contrib/admin/options.py:1040
msgid "Are you sure?"
msgstr "Jesteś pewien?"
@ -371,26 +371,37 @@ msgid ""
msgstr ""
"%(name)s \"%(obj)s\" dodane pomyślnie. Możesz edytować ponownie wpis poniżej."
#: contrib/admin/options.py:778
#: contrib/admin/options.py:696
msgid ""
"Items must be selected in order to perform actions on them. No items have "
"been changed."
msgstr ""
"Wykonanie akcji wymaga wybrania obiektów. Żaden obiekt nie został zmieniony."
#: contrib/admin/options.py:710
msgid "No action selected."
msgstr "Nie wybrano akcji."
#: contrib/admin/options.py:784
#, python-format
msgid "Add %s"
msgstr "Dodaj %s"
#: contrib/admin/options.py:810 contrib/admin/options.py:1012
#: contrib/admin/options.py:816 contrib/admin/options.py:1018
#, 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."
#: contrib/admin/options.py:867
#: contrib/admin/options.py:873
#, python-format
msgid "Change %s"
msgstr "Zmień %s"
#: contrib/admin/options.py:911
#: contrib/admin/options.py:917
msgid "Database error"
msgstr "Błąd bazy danych"
#: contrib/admin/options.py:947
#: contrib/admin/options.py:953
#, python-format
msgid "%(count)s %(name)s was changed successfully."
msgid_plural "%(count)s %(name)s were changed successfully."
@ -398,12 +409,12 @@ 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."
#: contrib/admin/options.py:1027
#: contrib/admin/options.py:1033
#, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
msgstr "%(name)s \"%(obj)s\" usunięty pomyślnie."
#: contrib/admin/options.py:1064
#: contrib/admin/options.py:1070
#, python-format
msgid "Change history: %s"
msgstr "Historia zmian: %s"
@ -1055,88 +1066,7 @@ msgstr "liczba %s"
msgid "Fields on %s objects"
msgstr "Pola obiektów %s"
#: contrib/admindocs/views.py:334 contrib/admindocs/views.py:345
#: contrib/admindocs/views.py:347 contrib/admindocs/views.py:353
#: contrib/admindocs/views.py:354 contrib/admindocs/views.py:356
msgid "Integer"
msgstr "Liczba całkowita"
#: contrib/admindocs/views.py:335
msgid "Boolean (Either True or False)"
msgstr "Wartość logiczna (True, False - prawda lub fałsz)"
#: contrib/admindocs/views.py:336 contrib/admindocs/views.py:355
#, python-format
msgid "String (up to %(max_length)s)"
msgstr "Łańcuch (do %(max_length)s znaków)"
#: contrib/admindocs/views.py:337
msgid "Comma-separated integers"
msgstr "Liczby całkowite rozdzielone przecinkami"
#: contrib/admindocs/views.py:338
msgid "Date (without time)"
msgstr "Data (bez godziny)"
#: contrib/admindocs/views.py:339
msgid "Date (with time)"
msgstr "Data (z godziną)"
#: contrib/admindocs/views.py:340
msgid "Decimal number"
msgstr "Liczba dziesiętna"
#: contrib/admindocs/views.py:341
msgid "E-mail address"
msgstr "Adres e-mail"
#: contrib/admindocs/views.py:342 contrib/admindocs/views.py:343
#: contrib/admindocs/views.py:346
msgid "File path"
msgstr "Ścieżka do pliku"
#: contrib/admindocs/views.py:344
msgid "Floating point number"
msgstr "Liczba zmiennoprzecinkowa"
#: contrib/admindocs/views.py:348 contrib/comments/models.py:60
msgid "IP address"
msgstr "Adres IP"
#: contrib/admindocs/views.py:350
msgid "Boolean (Either True, False or None)"
msgstr "Wartość logiczna (True, False, None - prawda, fałsz lub nic)"
#: contrib/admindocs/views.py:351
msgid "Relation to parent model"
msgstr "Relacja do modelu rodzica"
#: contrib/admindocs/views.py:352
msgid "Phone number"
msgstr "Numer telefonu"
#: contrib/admindocs/views.py:357
msgid "Text"
msgstr "Tekst"
#: contrib/admindocs/views.py:358
msgid "Time"
msgstr "Czas"
#: contrib/admindocs/views.py:359 contrib/comments/forms.py:95
#: contrib/flatpages/admin.py:8 contrib/flatpages/models.py:7
msgid "URL"
msgstr "URL"
#: contrib/admindocs/views.py:360
msgid "U.S. state (two uppercase letters)"
msgstr "Stan USA (dwie duże litery)"
#: contrib/admindocs/views.py:361
msgid "XML text"
msgstr "Tekst XML"
#: contrib/admindocs/views.py:387
#: contrib/admindocs/views.py:356
#, python-format
msgid "%s does not appear to be a urlpattern object"
msgstr "%s nie jest obiektem urlpattern"
@ -1239,7 +1169,7 @@ msgid "Change password: %s"
msgstr "Zmień hasło: %s"
#: contrib/auth/forms.py:15 contrib/auth/forms.py:48
#: contrib/auth/models.py:129
#: contrib/auth/models.py:141
msgid ""
"Required. 30 characters or fewer. Alphanumeric characters only (letters, "
"digits and underscores)."
@ -1309,51 +1239,51 @@ msgstr "Stare hasło"
msgid "Your old password was entered incorrectly. Please enter it again."
msgstr "Podane stare hasło jest niepoprawne. Proszę podać je jeszcze raz."
#: contrib/auth/models.py:63 contrib/auth/models.py:86
#: contrib/auth/models.py:70 contrib/auth/models.py:98
msgid "name"
msgstr "nazwa"
#: contrib/auth/models.py:65
#: contrib/auth/models.py:72
msgid "codename"
msgstr "nazwa kodowa"
#: contrib/auth/models.py:68
#: contrib/auth/models.py:76
msgid "permission"
msgstr "uprawnienie"
#: contrib/auth/models.py:69 contrib/auth/models.py:87
#: contrib/auth/models.py:77 contrib/auth/models.py:99
msgid "permissions"
msgstr "uprawnienia"
#: contrib/auth/models.py:90
#: contrib/auth/models.py:102
msgid "group"
msgstr "grupa"
#: contrib/auth/models.py:91 contrib/auth/models.py:139
#: contrib/auth/models.py:103 contrib/auth/models.py:151
msgid "groups"
msgstr "grupy"
#: contrib/auth/models.py:129
#: contrib/auth/models.py:141
msgid "username"
msgstr "użytkownik"
#: contrib/auth/models.py:130
#: contrib/auth/models.py:142
msgid "first name"
msgstr "imię"
#: contrib/auth/models.py:131
#: contrib/auth/models.py:143
msgid "last name"
msgstr "nazwisko"
#: contrib/auth/models.py:132
#: contrib/auth/models.py:144
msgid "e-mail address"
msgstr "adres e-mail"
#: contrib/auth/models.py:133
#: contrib/auth/models.py:145
msgid "password"
msgstr "hasło"
#: contrib/auth/models.py:133
#: contrib/auth/models.py:145
msgid ""
"Use '[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change "
"password form</a>."
@ -1361,19 +1291,19 @@ msgstr ""
"Użyj '[algo]$[salt]$[hexdigest]' lub <a href=\"password/\">formularza zmiany "
"hasła</a>."
#: contrib/auth/models.py:134
#: contrib/auth/models.py:146
msgid "staff status"
msgstr "w zespole"
#: contrib/auth/models.py:134
#: contrib/auth/models.py:146
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:135
#: contrib/auth/models.py:147
msgid "active"
msgstr "aktywny"
#: contrib/auth/models.py:135
#: contrib/auth/models.py:147
msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
@ -1381,11 +1311,11 @@ msgstr ""
"Oznacza czy użytkownika należy uważać za aktywnego. Odznacz to, zamiast "
"usuwać konta."
#: contrib/auth/models.py:136
#: contrib/auth/models.py:148
msgid "superuser status"
msgstr "status administratora"
#: contrib/auth/models.py:136
#: contrib/auth/models.py:148
msgid ""
"Designates that this user has all permissions without explicitly assigning "
"them."
@ -1393,15 +1323,15 @@ msgstr ""
"Oznacza, że ten użytkownik ma wszystkie uprawnienia bez jawnego "
"przypisywania ich."
#: contrib/auth/models.py:137
#: contrib/auth/models.py:149
msgid "last login"
msgstr "ostatnio zalogowany"
#: contrib/auth/models.py:138
#: contrib/auth/models.py:150
msgid "date joined"
msgstr "data przyłączenia"
#: contrib/auth/models.py:140
#: contrib/auth/models.py:152
msgid ""
"In addition to the permissions manually assigned, this user will also get "
"all permissions granted to each group he/she is in."
@ -1409,20 +1339,20 @@ msgstr ""
"Oprócz uprawnień przypisanych bezpośrednio użytkownikowi otrzyma on "
"uprawnienia grup, do których należy."
#: contrib/auth/models.py:141
#: contrib/auth/models.py:153
msgid "user permissions"
msgstr "uprawnienia użytkownika"
#: contrib/auth/models.py:145 contrib/comments/models.py:50
#: contrib/auth/models.py:157 contrib/comments/models.py:50
#: contrib/comments/models.py:168
msgid "user"
msgstr "użytkownik"
#: contrib/auth/models.py:146
#: contrib/auth/models.py:158
msgid "users"
msgstr "użytkownicy"
#: contrib/auth/models.py:334
#: contrib/auth/models.py:346
msgid "message"
msgstr "wiadomość"
@ -1492,6 +1422,11 @@ msgstr "Nazwa"
msgid "Email address"
msgstr "Adres e-mail"
#: contrib/comments/forms.py:95 contrib/flatpages/admin.py:8
#: contrib/flatpages/models.py:7 db/models/fields/__init__.py:917
msgid "URL"
msgstr "URL"
#: contrib/comments/forms.py:96
msgid "Comment"
msgstr "Komentarz"
@ -1510,7 +1445,7 @@ msgid ""
msgstr ""
"Jeżeli wpiszesz cokolwiek w to pole, Twój komentarz zostanie uznany za spam"
#: contrib/comments/models.py:22 contrib/contenttypes/models.py:74
#: contrib/comments/models.py:22 contrib/contenttypes/models.py:81
msgid "content type"
msgstr "typ zawartości"
@ -1539,6 +1474,10 @@ msgstr "komentarz"
msgid "date/time submitted"
msgstr "data/czas dodania"
#: contrib/comments/models.py:60 db/models/fields/__init__.py:737
msgid "IP address"
msgstr "Adres IP"
#: contrib/comments/models.py:61
msgid "is public"
msgstr "publicznie dostępny"
@ -1706,11 +1645,11 @@ msgstr "Zapisz swój komentarz"
msgid "or make changes"
msgstr "lub wprowadź jakieś zmiany"
#: contrib/contenttypes/models.py:70
#: contrib/contenttypes/models.py:77
msgid "python model class name"
msgstr "nazwa pythonowa modelu klasy"
#: contrib/contenttypes/models.py:75
#: contrib/contenttypes/models.py:82
msgid "content types"
msgstr "typy zawartości"
@ -1782,6 +1721,39 @@ msgstr ""
"Przepraszamy, ale Twój formularz wygasł. Proszę kontynuować wypełnianie "
"formularza od tej strony."
#: contrib/gis/db/models/fields/__init__.py:42
msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type."
msgstr ""
"Podstawowe pole GIS - odpowiada typowi Geometry w specyfikacji OpenGIS."
#: contrib/gis/db/models/fields/__init__.py:264
msgid "Point"
msgstr "Punkt"
#: contrib/gis/db/models/fields/__init__.py:268
msgid "Line string"
msgstr ""
#: contrib/gis/db/models/fields/__init__.py:272
msgid "Polygon"
msgstr "Wielobok"
#: contrib/gis/db/models/fields/__init__.py:276
msgid "Multi-point"
msgstr ""
#: contrib/gis/db/models/fields/__init__.py:280
msgid "Multi-line string"
msgstr ""
#: contrib/gis/db/models/fields/__init__.py:284
msgid "Multi polygon"
msgstr ""
#: contrib/gis/db/models/fields/__init__.py:288
msgid "Geometry collection"
msgstr ""
#: contrib/gis/forms/fields.py:17
msgid "No geometry value provided."
msgstr "Brak wartości geometrii."
@ -1994,11 +1966,11 @@ msgstr "Błędny numer CNPJ."
msgid "This field requires at least 14 digits"
msgstr "To pole musi zawierać co najmniej 14 cyfr"
#: contrib/localflavor/ca/forms.py:17
#: contrib/localflavor/ca/forms.py:24
msgid "Enter a postal code in the format XXX XXX."
msgstr "Wpisz kod pocztowy w formacie XXX XXX."
#: contrib/localflavor/ca/forms.py:88
#: contrib/localflavor/ca/forms.py:95
msgid "Enter a valid Canadian Social Insurance number in XXX-XXX-XXX format."
msgstr ""
"Wpisz poprawny numer kanadyjskiego ubezpieczenia w formacie XXX-XXX-XXXX."
@ -3767,6 +3739,14 @@ msgstr "Wpisz kod pocztowy w formacie XXXXX. lub XXXXX-XXXX."
msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format."
msgstr "Wpisz poprawny numer U.S. Social Security w formacie XXX-XX-XXXX."
#: contrib/localflavor/us/models.py:8
msgid "U.S. state (two uppercase letters)"
msgstr "Stan USA (dwie duże litery)"
#: contrib/localflavor/us/models.py:17
msgid "Phone number"
msgstr "Numer telefonu"
#: contrib/localflavor/za/forms.py:20
msgid "Enter a valid South African ID number"
msgstr "Wpisz poprawny południowoafrykański numer ID"
@ -3875,62 +3855,139 @@ msgstr "wyświetlana nazwa"
msgid "sites"
msgstr "strony"
#: db/models/fields/__init__.py:356 db/models/fields/__init__.py:710
#: db/models/fields/__init__.py:64
#, python-format
msgid "Field of type: %(field_type)s"
msgstr "Pole typu: %(field_type)s"
#: db/models/fields/__init__.py:350 db/models/fields/__init__.py:712
#: db/models/fields/__init__.py:792 db/models/fields/__init__.py:802
#: db/models/fields/__init__.py:829
msgid "Integer"
msgstr "Liczba całkowita"
#: db/models/fields/__init__.py:364 db/models/fields/__init__.py:728
msgid "This value must be an integer."
msgstr "Ta wartość musi być liczbą całkowitą."
#: db/models/fields/__init__.py:388
#: db/models/fields/__init__.py:382
msgid "Boolean (Either True or False)"
msgstr "Wartość logiczna (True, False - prawda lub fałsz)"
#: db/models/fields/__init__.py:397
msgid "This value must be either True or False."
msgstr ""
"Ta wartość musi być wartością logiczną (True, False - prawda lub fałsz)."
#: db/models/fields/__init__.py:427
#: db/models/fields/__init__.py:425 db/models/fields/__init__.py:812
#, python-format
msgid "String (up to %(max_length)s)"
msgstr "Łańcuch (do %(max_length)s znaków)"
#: db/models/fields/__init__.py:437
msgid "This field cannot be null."
msgstr "To pole nie może być puste."
#: db/models/fields/__init__.py:443
#: db/models/fields/__init__.py:447
msgid "Comma-separated integers"
msgstr "Liczby całkowite rozdzielone przecinkami"
#: db/models/fields/__init__.py:454
msgid "Enter only digits separated by commas."
msgstr "Wpisz tylko cyfry oddzielone przecinkami."
#: db/models/fields/__init__.py:474
#: db/models/fields/__init__.py:463
msgid "Date (without time)"
msgstr "Data (bez godziny)"
#: db/models/fields/__init__.py:486
msgid "Enter a valid date in YYYY-MM-DD format."
msgstr "Proszę wpisać poprawną datę w formacie RRRR-MM-DD."
#: db/models/fields/__init__.py:483
#: db/models/fields/__init__.py:495
#, python-format
msgid "Invalid date: %s"
msgstr "Niepoprawna data: %s"
#: db/models/fields/__init__.py:547 db/models/fields/__init__.py:565
#: db/models/fields/__init__.py:539
msgid "Date (with time)"
msgstr "Data (z godziną)"
#: db/models/fields/__init__.py:560 db/models/fields/__init__.py:578
msgid "Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format."
msgstr ""
"Wprowadź poprawną datę i godzinę w formacie YYYY-MM-DD HH:MM[:ss[.uuuuuu]]."
#: db/models/fields/__init__.py:601
#: db/models/fields/__init__.py:600
msgid "Decimal number"
msgstr "Liczba dziesiętna"
#: db/models/fields/__init__.py:615
msgid "This value must be a decimal number."
msgstr "Ta wartość musi być liczbą dziesiętną."
#: db/models/fields/__init__.py:654
msgid "E-mail address"
msgstr "Adres e-mail"
#: db/models/fields/__init__.py:665 db/models/fields/files.py:219
#: db/models/fields/files.py:330
msgid "File path"
msgstr "Ścieżka do pliku"
#: db/models/fields/__init__.py:686
msgid "Floating point number"
msgstr "Liczba zmiennoprzecinkowa"
#: db/models/fields/__init__.py:703
msgid "This value must be a float."
msgstr "Ta wartość musi być liczbą rzeczywistą."
#: db/models/fields/__init__.py:746
#: db/models/fields/__init__.py:752
msgid "Boolean (Either True, False or None)"
msgstr "Wartość logiczna (True, False, None - prawda, fałsz lub nic)"
#: db/models/fields/__init__.py:766
msgid "This value must be either None, True or False."
msgstr ""
"Ta wartość musi być jedną z None (nic), True (prawda) lub False (fałsz)."
#: db/models/fields/__init__.py:849 db/models/fields/__init__.py:863
#: db/models/fields/__init__.py:834
msgid "Text"
msgstr "Tekst"
#: db/models/fields/__init__.py:844
msgid "Time"
msgstr "Czas"
#: db/models/fields/__init__.py:875 db/models/fields/__init__.py:889
msgid "Enter a valid time in HH:MM[:ss[.uuuuuu]] format."
msgstr "Proszę wpisać poprawną godzinę w formacie HH:MM[:ss[.uuuuuu]]."
#: db/models/fields/related.py:869
#: db/models/fields/__init__.py:929
msgid "XML text"
msgstr "Tekst XML"
#: db/models/fields/related.py:695
msgid "Foreign Key (type determined by related field)"
msgstr "Klucz obcy (typ określony przez pole powiązane)"
#: db/models/fields/related.py:798
msgid "One-to-one relationship"
msgstr "Powiązanie jeden do jednego"
#: db/models/fields/related.py:852
msgid "Many-to-many relationship"
msgstr "Powiązanie wiele do wiele"
#: db/models/fields/related.py:872
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."
#: db/models/fields/related.py:930
#: db/models/fields/related.py:933
#, python-format
msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid."
msgid_plural ""
@ -4054,7 +4111,7 @@ msgid "Select a valid choice. %(value)s is not one of the available choices."
msgstr ""
"Wybierz poprawną wartość. %(value)s nie jest jednym z dostępnych wyborów."
#: forms/fields.py:703 forms/fields.py:764 forms/models.py:999
#: forms/fields.py:703 forms/fields.py:764 forms/models.py:1004
msgid "Enter a list of values."
msgstr "Podaj listę wartości."
@ -4106,29 +4163,29 @@ msgstr ""
msgid "Please correct the duplicate values below."
msgstr "Popraw poniższe zduplikowane wartości."
#: forms/models.py:863
#: forms/models.py:865
msgid "The inline foreign key did not match the parent instance primary key."
msgstr "Osadzony klucz obcy nie pasuje do klucza głównego obiektu rodzica."
#: forms/models.py:926
#: forms/models.py:931
msgid "Select a valid choice. That choice is not one of the available choices."
msgstr "Wybierz poprawną wartość. Podana nie jest jednym z dostępnych wyborów."
#: forms/models.py:1000
#: forms/models.py:1005
#, python-format
msgid "Select a valid choice. %s is not one of the available choices."
msgstr "Wybierz poprawną wartość. %s nie jest jednym z dostępnych wyborów."
#: forms/models.py:1002
#: forms/models.py:1007
#, python-format
msgid "\"%s\" is not a valid value for a primary key."
msgstr "\"%s\" nie jest poprawną wartością klucza głównego."
#: template/defaultfilters.py:767
#: template/defaultfilters.py:768
msgid "yes,no,maybe"
msgstr "tak,nie,może"
#: template/defaultfilters.py:798
#: template/defaultfilters.py:799
#, python-format
msgid "%(size)d byte"
msgid_plural "%(size)d bytes"
@ -4136,17 +4193,17 @@ msgstr[0] "%(size)d bajt"
msgstr[1] "%(size)d bajty"
msgstr[2] "%(size)d bajtów"
#: template/defaultfilters.py:800
#: template/defaultfilters.py:801
#, python-format
msgid "%.1f KB"
msgstr "%.1f KB"
#: template/defaultfilters.py:802
#: template/defaultfilters.py:803
#, python-format
msgid "%.1f MB"
msgstr "%.1f MB"
#: template/defaultfilters.py:803
#: template/defaultfilters.py:804
#, python-format
msgid "%.1f GB"
msgstr "%.1f GB"
@ -4446,6 +4503,9 @@ msgstr "%(verbose_name)s zostało pomyślnie zmienione."
msgid "The %(verbose_name)s was deleted."
msgstr "%(verbose_name)s zostało usunięte."
#~ msgid "Relation to parent model"
#~ msgstr "Relacja do modelu rodzica"
#~ msgid "Comment moderation queue"
#~ msgstr "Kolejka moderacji komentarzy"

View File

@ -1,36 +1,30 @@
import unittest
from django.contrib.admindocs import views
import fields
from django.contrib.admindocs import views
from django.db.models import fields as builtin_fields
class TestFieldType(unittest.TestCase):
def setUp(self):
pass
def test_field_name(self):
self.assertRaises(AttributeError,
views.get_readable_field_data_type, "NotAField"
)
def test_builtin_fields(self):
self.assertEqual(
views.get_readable_field_data_type(builtin_fields.BooleanField()),
u'Boolean (Either True or False)'
)
def test_custom_fields(self):
self.assertEqual(
views.get_readable_field_data_type(fields.CustomField()),
u'A custom field type'
)
self.assertEqual(
views.get_readable_field_data_type(fields.DocstringLackingField()),
u'Field of type: DocstringLackingField'
)
def test_multiline_custom_field_truncation(self):
self.assertEqual(
views.get_readable_field_data_type(fields.ManyLineDocstringField()),
u'Many-line custom field'
views.get_readable_field_data_type(fields.DescriptionLackingField()),
u'Field of type: DescriptionLackingField'
)

View File

@ -1,13 +1,7 @@
from django.db import models
class CustomField(models.Field):
"""A custom field type"""
class ManyLineDocstringField(models.Field):
"""Many-line custom field
This docstring has many lines. Lorum ipsem etc. etc. Four score
and seven years ago, and so on and so forth."""
description = "A custom field type"
class DocstringLackingField(models.Field):
class DescriptionLackingField(models.Field):
pass

View File

@ -327,19 +327,11 @@ def get_return_data_type(func_name):
return ''
def get_readable_field_data_type(field):
"""Returns the first line of a doc string for a given field type, if it
exists. Fields' docstrings can contain format strings, which will be
interpolated against the values of Field.__dict__ before being output.
If no docstring is given, a sensible value will be auto-generated from
the field's class name."""
"""Returns the description for a given field type, if it exists,
Fields' descriptions can contain format strings, which will be interpolated
against the values of field.__dict__ before being output."""
if field.__doc__:
doc = field.__doc__.split('\n')[0]
return _(doc) % field.__dict__
else:
return _(u'Field of type: %(field_type)s') % {
'field_type': field.__class__.__name__
}
return field.description % field.__dict__
def extract_views_from_urlpatterns(urlpatterns, base=''):
"""

View File

@ -292,15 +292,27 @@ class BaseGenericInlineFormSet(BaseModelFormSet):
ct_field_name = "content_type"
ct_fk_field_name = "object_id"
def __init__(self, data=None, files=None, instance=None, save_as_new=None, prefix=None):
def __init__(self, data=None, files=None, instance=None, save_as_new=None,
prefix=None, queryset=None):
# Avoid a circular import.
from django.contrib.contenttypes.models import ContentType
opts = self.model._meta
self.instance = instance
self.rel_name = '-'.join((
opts.app_label, opts.object_name.lower(),
self.ct_field.name, self.ct_fk_field.name,
))
if self.instance is None or self.instance.pk is None:
qs = self.model._default_manager.none()
else:
if queryset is None:
queryset = self.model._default_manager
qs = queryset.filter(**{
self.ct_field.name: ContentType.objects.get_for_model(self.instance),
self.ct_fk_field.name: self.instance.pk,
})
super(BaseGenericInlineFormSet, self).__init__(
queryset=self.get_queryset(), data=data, files=files,
queryset=qs, data=data, files=files,
prefix=prefix
)
@ -312,19 +324,6 @@ class BaseGenericInlineFormSet(BaseModelFormSet):
))
get_default_prefix = classmethod(get_default_prefix)
def get_queryset(self):
# Avoid a circular import.
from django.contrib.contenttypes.models import ContentType
if self.instance is None or self.instance.pk is None:
return self.model._default_manager.none()
qs = self.model._default_manager.filter(**{
self.ct_field.name: ContentType.objects.get_for_model(self.instance),
self.ct_fk_field.name: self.instance.pk,
})
if not qs.ordered:
qs = qs.order_by(self.model._meta.pk.name)
return qs
def save_new(self, form, commit=True):
# Avoid a circular import.
from django.contrib.contenttypes.models import ContentType

View File

@ -1,4 +1,5 @@
from django.db.models.fields import Field
from django.utils.translation import ugettext_lazy as _
from django.contrib.gis import forms
from django.contrib.gis.db.models.proxy import GeometryProxy
from django.contrib.gis.geometry.backend import Geometry, GeometryException
@ -49,6 +50,8 @@ class GeometryField(Field):
# Geodetic units.
geodetic_units = ('Decimal Degree', 'degree')
description = _("The base GIS field -- maps to the OpenGIS Specification Geometry type.")
def __init__(self, verbose_name=None, srid=4326, spatial_index=True, dim=2,
geography=False, **kwargs):
"""
@ -287,22 +290,28 @@ class GeometryField(Field):
# The OpenGIS Geometry Type Fields
class PointField(GeometryField):
geom_type = 'POINT'
description = _("Point")
class LineStringField(GeometryField):
geom_type = 'LINESTRING'
description = _("Line string")
class PolygonField(GeometryField):
geom_type = 'POLYGON'
description = _("Polygon")
class MultiPointField(GeometryField):
geom_type = 'MULTIPOINT'
description = _("Multi-point")
class MultiLineStringField(GeometryField):
geom_type = 'MULTILINESTRING'
description = _("Multi-line string")
class MultiPolygonField(GeometryField):
geom_type = 'MULTIPOLYGON'
description = _("Multi polygon")
class GeometryCollectionField(GeometryField):
geom_type = 'GEOMETRYCOLLECTION'
description = _("Geometry collection")

View File

@ -1,16 +1,21 @@
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.db.models.fields import Field, CharField
from django.contrib.localflavor.us.us_states import STATE_CHOICES
class USStateField(CharField):
"""U.S. state (two uppercase letters)"""
description = _("U.S. state (two uppercase letters)")
def __init__(self, *args, **kwargs):
kwargs['choices'] = STATE_CHOICES
kwargs['max_length'] = 2
super(USStateField, self).__init__(*args, **kwargs)
class PhoneNumberField(Field):
"""Phone number"""
description = _("Phone number")
def get_internal_type(self):
return "PhoneNumberField"

View File

@ -143,10 +143,15 @@ def sort_dependencies(app_list):
changed = False
while model_dependencies:
model, deps = model_dependencies.pop()
if all((d not in models or d in model_list) for d in deps):
# If all of the models in the dependency list are either already
# on the final model list, or not on the original serialization list,
# then we've found another model with all it's dependencies satisfied.
# If all of the models in the dependency list are either already
# on the final model list, or not on the original serialization list,
# then we've found another model with all it's dependencies satisfied.
found = True
for candidate in ((d not in models or d in model_list) for d in deps):
if not candidate:
found = False
if found:
model_list.append(model)
changed = True
else:
@ -158,4 +163,4 @@ def sort_dependencies(app_list):
)
model_dependencies = skipped
return model_list
return model_list

View File

@ -60,6 +60,13 @@ class Field(object):
creation_counter = 0
auto_creation_counter = -1
# Generic field type description, usually overriden by subclasses
def _description(self):
return _(u'Field of type: %(field_type)s') % {
'field_type': self.__class__.__name__
}
description = property(_description)
def __init__(self, verbose_name=None, name=None, primary_key=False,
max_length=None, unique=False, blank=False, null=False,
db_index=False, rel=None, default=NOT_PROVIDED, editable=True,
@ -369,10 +376,9 @@ class Field(object):
return getattr(obj, self.attname)
class AutoField(Field):
"""Integer"""
description = ugettext_lazy("Integer")
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__
kwargs['blank'] = True
@ -402,10 +408,8 @@ class AutoField(Field):
return None
class BooleanField(Field):
"""Boolean (Either True or False)"""
empty_strings_allowed = False
description = ugettext_lazy("Boolean (Either True or False)")
def __init__(self, *args, **kwargs):
kwargs['blank'] = True
if 'default' not in kwargs and not kwargs.get('null'):
@ -448,7 +452,7 @@ class BooleanField(Field):
return super(BooleanField, self).formfield(**defaults)
class CharField(Field):
"""String (up to %(max_length)s)"""
description = ugettext_lazy("String (up to %(max_length)s)")
def get_internal_type(self):
return "CharField"
@ -471,7 +475,7 @@ class CharField(Field):
# TODO: Maybe move this into contrib, because it's specialized.
class CommaSeparatedIntegerField(CharField):
"""Comma-separated integers"""
description = ugettext_lazy("Comma-separated integers")
def formfield(self, **kwargs):
defaults = {
@ -488,10 +492,9 @@ class CommaSeparatedIntegerField(CharField):
ansi_date_re = re.compile(r'^\d{4}-\d{1,2}-\d{1,2}$')
class DateField(Field):
"""Date (without time)"""
description = ugettext_lazy("Date (without time)")
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
#HACKs : auto_now_add/auto_now should be done as a default or a pre_save.
@ -571,7 +574,7 @@ class DateField(Field):
return super(DateField, self).formfield(**defaults)
class DateTimeField(DateField):
"""Date (with time)"""
description = ugettext_lazy("Date (with time)")
def get_internal_type(self):
return "DateTimeField"
@ -637,10 +640,8 @@ class DateTimeField(DateField):
return super(DateTimeField, self).formfield(**defaults)
class DecimalField(Field):
"""Decimal number"""
empty_strings_allowed = False
description = ugettext_lazy("Decimal number")
def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs):
self.max_digits, self.decimal_places = max_digits, decimal_places
Field.__init__(self, verbose_name, name, **kwargs)
@ -694,8 +695,7 @@ class DecimalField(Field):
return super(DecimalField, self).formfield(**defaults)
class EmailField(CharField):
"""E-mail address"""
description = ugettext_lazy("E-mail address")
def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 75)
CharField.__init__(self, *args, **kwargs)
@ -706,7 +706,7 @@ class EmailField(CharField):
return super(EmailField, self).formfield(**defaults)
class FilePathField(Field):
"""File path"""
description = ugettext_lazy("File path")
def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
self.path, self.match, self.recursive = path, match, recursive
@ -727,9 +727,8 @@ class FilePathField(Field):
return "FilePathField"
class FloatField(Field):
"""Floating point number"""
empty_strings_allowed = False
description = ugettext_lazy("Floating point number")
def get_prep_value(self, value):
if value is None:
@ -754,9 +753,8 @@ class FloatField(Field):
return super(FloatField, self).formfield(**defaults)
class IntegerField(Field):
"""Integer"""
empty_strings_allowed = False
description = ugettext_lazy("Integer")
def get_prep_value(self, value):
if value is None:
@ -781,10 +779,8 @@ class IntegerField(Field):
return super(IntegerField, self).formfield(**defaults)
class IPAddressField(Field):
"""IP address"""
empty_strings_allowed = False
description = ugettext_lazy("IP address")
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 15
Field.__init__(self, *args, **kwargs)
@ -798,10 +794,8 @@ class IPAddressField(Field):
return super(IPAddressField, self).formfield(**defaults)
class NullBooleanField(Field):
"""Boolean (Either True, False or None)"""
empty_strings_allowed = False
description = ugettext_lazy("Boolean (Either True, False or None)")
def __init__(self, *args, **kwargs):
kwargs['null'] = True
Field.__init__(self, *args, **kwargs)
@ -841,7 +835,7 @@ class NullBooleanField(Field):
return super(NullBooleanField, self).formfield(**defaults)
class PositiveIntegerField(IntegerField):
"""Integer"""
description = ugettext_lazy("Integer")
def get_internal_type(self):
return "PositiveIntegerField"
@ -852,8 +846,7 @@ class PositiveIntegerField(IntegerField):
return super(PositiveIntegerField, self).formfield(**defaults)
class PositiveSmallIntegerField(IntegerField):
"""Integer"""
description = ugettext_lazy("Integer")
def get_internal_type(self):
return "PositiveSmallIntegerField"
@ -863,8 +856,7 @@ class PositiveSmallIntegerField(IntegerField):
return super(PositiveSmallIntegerField, self).formfield(**defaults)
class SlugField(CharField):
"""String (up to %(max_length)s)"""
description = ugettext_lazy("String (up to %(max_length)s)")
def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 50)
# Set db_index=True unless it's been set manually.
@ -881,13 +873,13 @@ class SlugField(CharField):
return super(SlugField, self).formfield(**defaults)
class SmallIntegerField(IntegerField):
"""Integer"""
description = ugettext_lazy("Integer")
def get_internal_type(self):
return "SmallIntegerField"
class TextField(Field):
"""Text"""
description = ugettext_lazy("Text")
def get_internal_type(self):
return "TextField"
@ -898,10 +890,9 @@ class TextField(Field):
return super(TextField, self).formfield(**defaults)
class TimeField(Field):
"""Time"""
description = ugettext_lazy("Time")
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
if auto_now or auto_now_add:
@ -978,7 +969,7 @@ class TimeField(Field):
return super(TimeField, self).formfield(**defaults)
class URLField(CharField):
"""URL"""
description = ugettext_lazy("URL")
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 200)
@ -991,7 +982,7 @@ class URLField(CharField):
return super(URLField, self).formfield(**defaults)
class XMLField(TextField):
"""XML text"""
description = ugettext_lazy("XML text")
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
self.schema_path = schema_path

View File

@ -209,8 +209,6 @@ class FileDescriptor(object):
instance.__dict__[self.field.name] = value
class FileField(Field):
"""File path"""
# The class to wrap instance attributes in. Accessing the file object off
# the instance will always return an instance of attr_class.
attr_class = FieldFile
@ -218,6 +216,8 @@ class FileField(Field):
# The descriptor to use for accessing the attribute off of the class.
descriptor_class = FileDescriptor
description = ugettext_lazy("File path")
def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
for arg in ('primary_key', 'unique'):
if arg in kwargs:
@ -325,10 +325,9 @@ class ImageFieldFile(ImageFile, FieldFile):
super(ImageFieldFile, self).delete(save)
class ImageField(FileField):
"""File path"""
attr_class = ImageFieldFile
descriptor_class = ImageFileDescriptor
description = ugettext_lazy("File path")
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
self.width_field, self.height_field = width_field, height_field

View File

@ -708,9 +708,8 @@ class ManyToManyRel(object):
return self.to._meta.pk
class ForeignKey(RelatedField, Field):
"""Foreign Key (type determined by related field)"""
empty_strings_allowed = False
description = ugettext_lazy("Foreign Key (type determined by related field)")
def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs):
try:
to_name = to._meta.object_name.lower()
@ -808,13 +807,13 @@ class ForeignKey(RelatedField, Field):
return rel_field.db_type(connection=connection)
class OneToOneField(ForeignKey):
"""One-to-one relationship
"""
A OneToOneField is essentially the same as a ForeignKey, with the exception
that always carries a "unique" constraint with it and the reverse relation
always returns the object pointed to (since there will only ever be one),
rather than returning a list."""
rather than returning a list.
"""
description = ugettext_lazy("One-to-one relationship")
def __init__(self, to, to_field=None, **kwargs):
kwargs['unique'] = True
super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
@ -868,8 +867,7 @@ def create_many_to_many_intermediary_model(field, klass):
})
class ManyToManyField(RelatedField, Field):
"""Many-to-many relationship"""
description = ugettext_lazy("Many-to-many relationship")
def __init__(self, to, **kwargs):
try:
assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name)

View File

@ -35,6 +35,7 @@ class AppCache(object):
postponed = [],
nesting_level = 0,
write_lock = threading.RLock(),
_get_models_cache = {},
)
def __init__(self):
@ -140,6 +141,11 @@ class AppCache(object):
explicit intermediate table) are not included. However, if you
specify include_auto_created=True, they will be.
"""
cache_key = (app_mod, include_auto_created)
try:
return self._get_models_cache[cache_key]
except KeyError:
pass
self._populate()
if app_mod:
model_list = self.app_models.get(app_mod.__name__.split('.')[-2], SortedDict()).values()
@ -148,7 +154,8 @@ class AppCache(object):
for app_entry in self.app_models.itervalues():
model_list.extend(app_entry.values())
if not include_auto_created:
return filter(lambda o: not o._meta.auto_created, model_list)
model_list = filter(lambda o: not o._meta.auto_created, model_list)
self._get_models_cache[cache_key] = model_list
return model_list
def get_model(self, app_label, model_name, seed_cache=True):
@ -183,6 +190,7 @@ class AppCache(object):
if os.path.splitext(fname1)[0] == os.path.splitext(fname2)[0]:
continue
model_dict[model_name] = model
self._get_models_cache.clear()
cache = AppCache()

View File

@ -704,7 +704,7 @@ def modelformset_factory(model, form=ModelForm, formfield_callback=lambda f: f.f
class BaseInlineFormSet(BaseModelFormSet):
"""A formset for child objects related to a parent."""
def __init__(self, data=None, files=None, instance=None,
save_as_new=False, prefix=None):
save_as_new=False, prefix=None, queryset=None):
from django.db.models.fields.related import RelatedObject
if instance is None:
self.instance = self.fk.rel.to()
@ -717,7 +717,9 @@ class BaseInlineFormSet(BaseModelFormSet):
backlink_value = self.instance
else:
backlink_value = getattr(self.instance, self.fk.rel.field_name)
qs = self.model._default_manager.filter(**{self.fk.name: backlink_value})
if queryset is None:
queryset = self.model._default_manager
qs = queryset.filter(**{self.fk.name: backlink_value})
super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix,
queryset=qs)

View File

@ -5,6 +5,7 @@ Writing custom model fields
===========================
.. versionadded:: 1.0
.. currentmodule:: django.db.models
Introduction
============
@ -165,7 +166,8 @@ behave like any existing field, so we'll subclass directly from
from django.db import models
class HandField(models.Field):
"""A hand of cards (bridge style)"""
description = "A hand of cards (bridge style)"
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 104
@ -248,7 +250,8 @@ simple: make sure your field subclass uses a special metaclass:
For example::
class HandField(models.Field):
"""A hand of cards (bridge style)"""
description = "A hand of cards (bridge style)"
__metaclass__ = models.SubfieldBase
@ -262,16 +265,17 @@ called when the attribute is initialized.
Documenting your Custom Field
-----------------------------
.. class:: django.db.models.Field
.. attribute:: description
As always, you should document your field type, so users will know what it is.
The best way to do this is to simply provide a docstring for it. This will
automatically be picked up by ``django.contrib.admindocs``, if you have it
installed, and the first line of it will show up as the field type in the
documentation for any model that uses your field. In the above examples, it
will show up as 'A hand of cards (bridge style)'. Note that if you provide a
more verbose docstring, only the first line will show up in
``django.contrib.admindocs``. The full docstring will, of course, still be
available through ``pydoc`` or the interactive interpreter's ``help()``
function.
In addition to providing a docstring for it, which is useful for developers,
you can also allow users of the admin app to see a short description of the
field type via the ``django.contrib.admindocs`` application. To do this simply
provide descriptive text in a ``description`` class attribute of your custom field.
In the above example, the type description displayed by the ``admindocs`` application
for a ``HandField`` will be 'A hand of cards (bridge style)'.
Useful methods
--------------

View File

@ -644,6 +644,53 @@ True
>>> formset.save()
[<Poem: Brooklyn Bridge>, <Poem: Brooklyn Bridge>]
We can provide a custom queryset to our InlineFormSet:
>>> custom_qs = Book.objects.order_by('-title')
>>> formset = AuthorBooksFormSet(instance=author, queryset=custom_qs)
>>> for form in formset.forms:
... print form.as_p()
<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Les Fleurs du Mal" maxlength="100" /><input type="hidden" name="book_set-0-author" value="1" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" value="1" id="id_book_set-0-id" /></p>
<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" value="Le Spleen de Paris" maxlength="100" /><input type="hidden" name="book_set-1-author" value="1" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" value="2" id="id_book_set-1-id" /></p>
<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" value="Flowers of Evil" maxlength="100" /><input type="hidden" name="book_set-2-author" value="1" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" value="5" id="id_book_set-2-id" /></p>
<p><label for="id_book_set-3-title">Title:</label> <input id="id_book_set-3-title" type="text" name="book_set-3-title" maxlength="100" /><input type="hidden" name="book_set-3-author" value="1" id="id_book_set-3-author" /><input type="hidden" name="book_set-3-id" id="id_book_set-3-id" /></p>
<p><label for="id_book_set-4-title">Title:</label> <input id="id_book_set-4-title" type="text" name="book_set-4-title" maxlength="100" /><input type="hidden" name="book_set-4-author" value="1" id="id_book_set-4-author" /><input type="hidden" name="book_set-4-id" id="id_book_set-4-id" /></p>
>>> data = {
... 'book_set-TOTAL_FORMS': '5', # the number of forms rendered
... 'book_set-INITIAL_FORMS': '3', # the number of forms with initial data
... 'book_set-0-id': '1',
... 'book_set-0-title': 'Les Fleurs du Mal',
... 'book_set-1-id': '2',
... 'book_set-1-title': 'Le Spleen de Paris',
... 'book_set-2-id': '5',
... 'book_set-2-title': 'Flowers of Evil',
... 'book_set-3-title': 'Revue des deux mondes',
... 'book_set-4-title': '',
... }
>>> formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs)
>>> formset.is_valid()
True
>>> custom_qs = Book.objects.filter(title__startswith='F')
>>> formset = AuthorBooksFormSet(instance=author, queryset=custom_qs)
>>> for form in formset.forms:
... print form.as_p()
<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Flowers of Evil" maxlength="100" /><input type="hidden" name="book_set-0-author" value="1" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" value="5" id="id_book_set-0-id" /></p>
<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1-author" value="1" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p>
<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2-author" value="1" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p>
>>> data = {
... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered
... 'book_set-INITIAL_FORMS': '1', # the number of forms with initial data
... 'book_set-0-id': '5',
... 'book_set-0-title': 'Flowers of Evil',
... 'book_set-1-title': 'Revue des deux mondes',
... 'book_set-2-title': '',
... }
>>> formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs)
>>> formset.is_valid()
True
# Test a custom primary key ###################################################

View File

@ -20,7 +20,7 @@ class Media(models.Model):
class MediaInline(generic.GenericTabularInline):
model = Media
class EpisodeAdmin(admin.ModelAdmin):
inlines = [
MediaInline,
@ -56,7 +56,7 @@ class MediaMaxNumInline(generic.GenericTabularInline):
model = Media
extra = 5
max_num = 2
admin.site.register(EpisodeMaxNum, inlines=[MediaMaxNumInline])
#

View File

@ -17,7 +17,7 @@ class GenericAdminViewTest(TestCase):
self.original_template_debug = settings.TEMPLATE_DEBUG
settings.TEMPLATE_DEBUG = True
self.client.login(username='super', password='secret')
# Can't load content via a fixture (since the GenericForeignKey
# relies on content type IDs, which will vary depending on what
# other tests have been run), thus we do it here.
@ -25,26 +25,30 @@ class GenericAdminViewTest(TestCase):
self.episode_pk = e.pk
m = Media(content_object=e, url='http://example.com/podcast.mp3')
m.save()
self.media_pk = m.pk
self.mp3_media_pk = m.pk
m = Media(content_object=e, url='http://example.com/logo.png')
m.save()
self.png_media_pk = m.pk
def tearDown(self):
self.client.logout()
settings.TEMPLATE_DEBUG = self.original_template_debug
def testBasicAddGet(self):
"""
A smoke test to ensure GET on the add_view works.
"""
response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/add/')
self.failUnlessEqual(response.status_code, 200)
def testBasicEditGet(self):
"""
A smoke test to ensure GET on the change_view works.
"""
response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk)
self.failUnlessEqual(response.status_code, 200)
def testBasicAddPost(self):
"""
A smoke test to ensure POST on add_view works.
@ -57,7 +61,7 @@ class GenericAdminViewTest(TestCase):
}
response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/episode/add/', post_data)
self.failUnlessEqual(response.status_code, 302) # redirect somewhere
def testBasicEditPost(self):
"""
A smoke test to ensure POST on edit_view works.
@ -65,17 +69,45 @@ class GenericAdminViewTest(TestCase):
post_data = {
"name": u"This Week in Django",
# inline data
"generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"2",
"generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"1",
"generic_inline_admin-media-content_type-object_id-0-id": u"%d" % self.media_pk,
"generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"3",
"generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"2",
"generic_inline_admin-media-content_type-object_id-0-id": u"%d" % self.mp3_media_pk,
"generic_inline_admin-media-content_type-object_id-0-url": u"http://example.com/podcast.mp3",
"generic_inline_admin-media-content_type-object_id-1-id": u"",
"generic_inline_admin-media-content_type-object_id-1-url": u"",
"generic_inline_admin-media-content_type-object_id-1-id": u"%d" % self.png_media_pk,
"generic_inline_admin-media-content_type-object_id-1-url": u"http://example.com/logo.png",
"generic_inline_admin-media-content_type-object_id-2-id": u"",
"generic_inline_admin-media-content_type-object_id-2-url": u"",
}
url = '/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk
response = self.client.post(url, post_data)
self.failUnlessEqual(response.status_code, 302) # redirect somewhere
def testGenericInlineFormset(self):
EpisodeMediaFormSet = generic_inlineformset_factory(Media, can_delete=False, extra=3)
e = Episode.objects.get(name='This Week in Django')
# Works with no queryset
formset = EpisodeMediaFormSet(instance=e)
self.assertEquals(len(formset.forms), 5)
self.assertEquals(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="text" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/podcast.mp3" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="1" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>')
self.assertEquals(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" value="2" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>')
self.assertEquals(formset.forms[2].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-2-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-2-url" type="text" name="generic_inline_admin-media-content_type-object_id-2-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-2-id" id="id_generic_inline_admin-media-content_type-object_id-2-id" /></p>')
# A queryset can be used to alter display ordering
formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.order_by('url'))
self.assertEquals(len(formset.forms), 5)
self.assertEquals(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="text" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="2" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>')
self.assertEquals(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" value="http://example.com/podcast.mp3" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" value="1" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>')
self.assertEquals(formset.forms[2].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-2-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-2-url" type="text" name="generic_inline_admin-media-content_type-object_id-2-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-2-id" id="id_generic_inline_admin-media-content_type-object_id-2-id" /></p>')
# Works with a queryset that omits items
formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.filter(url__endswith=".png"))
self.assertEquals(len(formset.forms), 4)
self.assertEquals(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="text" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="2" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>')
self.assertEquals(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>')
def testGenericInlineFormsetFactory(self):
# Regression test for #10522.
inline_formset = generic_inlineformset_factory(Media,