To install bookmarklets, drag the link to your bookmarks\n"
+"toolbar, or right-click the link and add it to your bookmarks. Now you can\n"
+"select the bookmarklet from any page in the site. Note that some of these\n"
+"bookmarklets require you to be viewing the site from a computer designated\n"
+"as \"internal\" (talk to your system administrator if you aren't sure if\n"
+"your computer is \"internal\").
\n"
+msgstr ""
+"\n"
+"
За да инсталирате bookmarklet-и, задърпайте линка в bookmarks\n"
+"toolbar-а, или щракнете с десния бутон и добавете линка в отметките. Сега можете да\n"
+"изберете bookmarklet-а от която и да е страница. Някой от тези\n"
+"bookmarklet-и могат да се разглеждат само от компютър, който е маркиран \n"
+"като \"вътрешен\" (приказвайте с администратора ако не сте сигурни дали\n"
+"компютърът ви е \"вътрешен\").
\n"
+
+#: contrib/admin/templates/admin_doc/bookmarklets.html:19
+msgid "Documentation for this page"
+msgstr "Документация за тази страница"
+
+#: contrib/admin/templates/admin_doc/bookmarklets.html:20
+msgid ""
+"Jumps you from any page to the documentation for the view that generates "
+"that page."
+msgstr "Препраща от която и да е страница към документацията за изгледа, който я е генерирал. "
+
+#: contrib/admin/templates/admin_doc/bookmarklets.html:22
+msgid "Show object ID"
+msgstr "ID на обекта"
+
+#: contrib/admin/templates/admin_doc/bookmarklets.html:23
+msgid ""
+"Shows the content-type and unique ID for pages that represent a single "
+"object."
+msgstr "Показва типът на съдържанието и ID-то на страници, които представляват единичен обект. "
+
+#: contrib/admin/templates/admin_doc/bookmarklets.html:25
+msgid "Edit this object (current window)"
+msgstr "Редактирай този обект (в този прозорец)"
+
+#: contrib/admin/templates/admin_doc/bookmarklets.html:26
+msgid "Jumps to the admin page for pages that represent a single object."
+msgstr "Отива в админската страница за страници, които представляват единичен обект. "
+
+#: contrib/admin/templates/admin_doc/bookmarklets.html:28
+msgid "Edit this object (new window)"
+msgstr "Редактирай този обект (в нов прозорец)"
+
+#: contrib/admin/templates/admin_doc/bookmarklets.html:29
+msgid "As above, but opens the admin page in a new window."
+msgstr "Като горното, но отваря администраторската страница в нов прозорец."
+
+#: contrib/admin/templates/admin/submit_line.html:3
+#: contrib/admin/templates/admin/delete_confirmation.html:9
+msgid "Delete"
+msgstr "Изтрий"
+
+#: contrib/admin/templates/admin/submit_line.html:4
+msgid "Save as new"
+msgstr "Запис като нов"
+
+#: contrib/admin/templates/admin/submit_line.html:5
+msgid "Save and add another"
+msgstr "Запис и нов"
+
+#: contrib/admin/templates/admin/submit_line.html:6
+msgid "Save and continue editing"
+msgstr "Запис и продължение"
+
+#: contrib/admin/templates/admin/submit_line.html:7
+msgid "Save"
+msgstr "Запис"
+
+#: contrib/admin/templates/admin/500.html:4
+msgid "Server error"
+msgstr ""
+
+#: contrib/admin/templates/admin/500.html:6
+msgid "Server error (500)"
+msgstr ""
+
+#: contrib/admin/templates/admin/500.html:9
+msgid "Server Error (500)"
+msgstr "Server Error (500)"
+
+#: contrib/admin/templates/admin/500.html:10
+msgid ""
+"There's been an error. It's been reported to the site administrators via e-"
+"mail and should be fixed shortly. Thanks for your patience."
+msgstr "Малък проблем. Администраторът на сайта е уведомен за случилото се. Благодарим за проявеното разбиране. "
+
+#: contrib/admin/templates/admin/filter.html:2
+#, python-format
+msgid " By %(filter_title)s "
+msgstr " По %(filter_title)s "
+
+#: contrib/admin/templates/admin/filters.html:4
+msgid "Filter"
+msgstr "Филтър"
+
+#: contrib/admin/templates/admin/invalid_setup.html:8
+msgid ""
+"Something's wrong with your database installation. Make sure the appropriate "
+"database tables have been created, and make sure the database is readable by "
+"the appropriate user."
+msgstr "Има някакъв проблем с базата данни. Проверете дали необходимите таблици са създадени и дали съответния потребител има необходимите права за достъп. "
+
+#: contrib/admin/templates/admin/search_form.html:8
+msgid "Go"
+msgstr "Давай"
+
+#: contrib/admin/templates/admin/search_form.html:10
+#, python-format
+msgid "1 result"
+msgid_plural "%(counter)s results"
+msgstr[0] "1 резултат"
+msgstr[1] "%(counter)s резултата"
+
+#: contrib/admin/templates/admin/search_form.html:10
+#, python-format
+msgid "%(full_result_count)s total"
+msgstr "%(full_result_count)s общо"
+
+#: contrib/admin/templates/admin/object_history.html:5
+#: contrib/admin/templates/admin/change_form.html:21
+msgid "History"
+msgstr "История"
+
+#: contrib/admin/templates/admin/object_history.html:18
+msgid "Date/time"
+msgstr "Дата/час"
+
+#: contrib/admin/templates/admin/object_history.html:19
+msgid "User"
+msgstr "Потребител"
+
+#: contrib/admin/templates/admin/object_history.html:20
+msgid "Action"
+msgstr "Действие"
+
+#: contrib/admin/templates/admin/object_history.html:26
+msgid "DATE_WITH_TIME_FULL"
+msgstr "j N, Y, P"
+
+#: contrib/admin/templates/admin/object_history.html:36
+msgid ""
+"This object doesn't have a change history. It probably wasn't added via this "
+"admin site."
+msgstr "Този обект няма исторя на промените. Вероятно не е добавен чрез административния панел. "
+
+#: contrib/admin/templates/admin/delete_confirmation.html:14
+#, python-format
+msgid ""
+"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting "
+"related objects, but your account doesn't have permission to delete the "
+"following types of objects:"
+msgstr "Изтриването на обекта %(object_name)s '%(escaped_object)s' не може да бъде извършено без да се изтрият и някой свързани обекти, върху които обаче нямате права: "
+
+#: contrib/admin/templates/admin/delete_confirmation.html:21
+#, python-format
+msgid ""
+"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? "
+"All of the following related items will be deleted:"
+msgstr ""
+"Наистина ли искате да затрием обектите %(object_name)s \"%(escaped_object)s\"? "
+"Следните свързани елементи също ще бъдат изтрити:"
+
+#: contrib/admin/templates/admin/delete_confirmation.html:26
+msgid "Yes, I'm sure"
+msgstr "Абсолютно"
+
+#: contrib/admin/templates/admin/pagination.html:10
+msgid "Show all"
+msgstr "Покажи всички"
+
+#: contrib/admin/templates/admin/change_list.html:12
+#, python-format
+msgid "Add %(name)s"
+msgstr "Добави %(name)s"
+
+#: contrib/admin/templates/admin/change_form.html:15
+#: contrib/admin/templates/admin/index.html:28
+msgid "Add"
+msgstr "Добави"
+
+#: contrib/admin/templates/admin/change_form.html:22
+msgid "View on site"
+msgstr "Разгледай в сайта"
+
+#: contrib/admin/templates/admin/change_form.html:32
+#: contrib/admin/templates/admin/auth/user/change_password.html:24
+msgid "Please correct the error below."
+msgid_plural "Please correct the errors below."
+msgstr[0] "Коригирайте долу допуснатата грешка. "
+msgstr[1] "Коригирайте долу допуснатите грешки."
+
+#: contrib/admin/templates/admin/change_form.html:50
+msgid "Ordering"
+msgstr "Подреждане"
+
+#: contrib/admin/templates/admin/change_form.html:53
+msgid "Order:"
+msgstr "Подредба:"
+
+#: contrib/admin/templates/admin/base.html:25
+msgid "Welcome,"
+msgstr "Добре дошли,"
+
+#: contrib/admin/templates/admin/404.html:4
+#: contrib/admin/templates/admin/404.html:8
+msgid "Page not found"
+msgstr "Няма такава страница"
+
+#: contrib/admin/templates/admin/404.html:10
+msgid "We're sorry, but the requested page could not be found."
+msgstr "Сори, ама тая страничка липсва. "
+
+#: contrib/admin/templates/admin/login.html:25
+#: contrib/admin/views/decorators.py:24
+msgid "Log in"
+msgstr "Вход"
+
+#: contrib/admin/templates/admin/index.html:17
+#, python-format
+msgid "Models available in the %(name)s application."
+msgstr "Модели в приложението %(name)s "
+
+#: contrib/admin/templates/admin/index.html:18
+#, python-format
+msgid "%(name)s"
+msgstr "%(name)s"
+
+#: contrib/admin/templates/admin/index.html:34
+msgid "Change"
+msgstr "Промени"
+
+#: contrib/admin/templates/admin/index.html:44
+msgid "You don't have permission to edit anything."
+msgstr "Нямате права да редактирате каквото и да е."
+
+#: contrib/admin/templates/admin/index.html:52
+msgid "Recent Actions"
+msgstr "Пресни действия"
+
+#: contrib/admin/templates/admin/index.html:53
+msgid "My Actions"
+msgstr "Моите действия"
+
+#: contrib/admin/templates/admin/index.html:57
+msgid "None available"
+msgstr "Няма налични"
+
+#: contrib/admin/templates/admin/base_site.html:4
+msgid "Django site admin"
+msgstr "Административен панел"
+
+#: contrib/admin/templates/admin/base_site.html:7
+msgid "Django administration"
+msgstr "Административен панел"
+
+#: contrib/admin/templates/admin/auth/user/add_form.html:6
+msgid ""
+"First, enter a username and password. Then, you'll be able to edit more user "
+"options."
+msgstr "Първо, въведете потребител и парола. След това ще можете да редактирате повече детайли. "
+
+#: contrib/admin/templates/admin/auth/user/add_form.html:12
+msgid "Username"
+msgstr "Потребител"
+
+#: contrib/admin/templates/admin/auth/user/add_form.html:18
+#: contrib/admin/templates/admin/auth/user/change_password.html:34
+msgid "Password"
+msgstr "Парола"
+
+#: contrib/admin/templates/admin/auth/user/add_form.html:23
+#: contrib/admin/templates/admin/auth/user/change_password.html:39
+msgid "Password (again)"
+msgstr "Парола (пак)"
+
+#: contrib/admin/templates/admin/auth/user/add_form.html:24
+#: contrib/admin/templates/admin/auth/user/change_password.html:40
+msgid "Enter the same password as above, for verification."
+msgstr "Въведете същата парола още веднъж за проверка. "
+
+#: contrib/admin/templates/admin/auth/user/change_password.html:28
+#, python-format
+msgid "Enter a new password for the user %(username)s."
+msgstr "Въведете нова парола за потребител %(username)s."
+
+#: contrib/admin/templates/widget/file.html:2
+msgid "Currently:"
+msgstr "Сега:"
+
+#: contrib/admin/templates/widget/file.html:3
+msgid "Change:"
+msgstr "Промяна:"
+
+#: contrib/admin/templates/widget/date_time.html:3
+msgid "Date:"
+msgstr "Дата:"
+
+#: contrib/admin/templates/widget/date_time.html:4
+msgid "Time:"
+msgstr "Час:"
+
+#: contrib/admin/templates/registration/logged_out.html:8
+msgid "Thanks for spending some quality time with the Web site today."
+msgstr "Благодарим Ви, че използвахте този сайт днес. "
+
+#: contrib/admin/templates/registration/logged_out.html:10
+msgid "Log in again"
+msgstr "Влез пак"
+
+#: contrib/admin/templates/registration/password_reset_email.html:2
+msgid "You're receiving this e-mail because you requested a password reset"
+msgstr "Получавате този e-mail, защото сте поръчали да Ви бъде издадена нова парола "
+
+#: contrib/admin/templates/registration/password_reset_email.html:3
+#, python-format
+msgid "for your user account at %(site_name)s"
+msgstr "за Вашия потребителски акаунт в %(site_name)s"
+
+#: contrib/admin/templates/registration/password_reset_email.html:5
+#, python-format
+msgid "Your new password is: %(new_password)s"
+msgstr "Новата Ви парола е: %(new_password)s"
+
+#: contrib/admin/templates/registration/password_reset_email.html:7
+msgid "Feel free to change this password by going to this page:"
+msgstr "Тази парола може да си я смените като щракнете тук: "
+
+#: contrib/admin/templates/registration/password_reset_email.html:11
+msgid "Your username, in case you've forgotten:"
+msgstr "Вашето потребителско име (ако не го помните): "
+
+#: contrib/admin/templates/registration/password_reset_email.html:13
+msgid "Thanks for using our site!"
+msgstr "Много се радваме, че използвате сайта ни!"
+
+#: contrib/admin/templates/registration/password_reset_email.html:15
+#, python-format
+msgid "The %(site_name)s team"
+msgstr "Готините хора от %(site_name)s"
+
+#: contrib/admin/templates/registration/password_reset_done.html:4
+#: contrib/admin/templates/registration/password_reset_form.html:4
+#: contrib/admin/templates/registration/password_reset_form.html:6
+#: contrib/admin/templates/registration/password_reset_form.html:10
+msgid "Password reset"
+msgstr "Нова парола"
+
+#: contrib/admin/templates/registration/password_reset_done.html:6
+#: contrib/admin/templates/registration/password_reset_done.html:10
+msgid "Password reset successful"
+msgstr "Паролата е успешно обновена"
+
+#: contrib/admin/templates/registration/password_reset_done.html:12
+msgid ""
+"We've e-mailed a new password to the e-mail address you submitted. You "
+"should be receiving it shortly."
+msgstr "Пратихме ви нова парола на адреса, който указахте. Скоро трябва да пристигне. "
+
+#: contrib/admin/templates/registration/password_change_form.html:4
+#: contrib/admin/templates/registration/password_change_form.html:6
+#: contrib/admin/templates/registration/password_change_form.html:10
+#: contrib/admin/templates/registration/password_change_done.html:4
+msgid "Password change"
+msgstr "Промяна на парола"
+
+#: contrib/admin/templates/registration/password_change_form.html:12
+msgid ""
+"Please enter your old password, for security's sake, and then enter your new "
+"password twice so we can verify you typed it in correctly."
+msgstr "Въведете си старата парола (за сигурност), след което въведете желаната нова парола два пъти за да не стават грешки. "
+
+#: contrib/admin/templates/registration/password_change_form.html:17
+msgid "Old password:"
+msgstr "Стара парола:"
+
+#: contrib/admin/templates/registration/password_change_form.html:19
+msgid "New password:"
+msgstr "Нова парола:"
+
+#: contrib/admin/templates/registration/password_change_form.html:21
+msgid "Confirm password:"
+msgstr "Потвърдете паролата:"
+
+#: contrib/admin/templates/registration/password_change_form.html:23
+msgid "Change my password"
+msgstr "Промяна на парола"
+
+#: contrib/admin/templates/registration/password_change_done.html:6
+#: contrib/admin/templates/registration/password_change_done.html:10
+msgid "Password change successful"
+msgstr "Паролата е сменена успешно"
+
+#: contrib/admin/templates/registration/password_change_done.html:12
+msgid "Your password was changed."
+msgstr "Паролата ви е сменена."
+
+#: contrib/admin/templates/registration/password_reset_form.html:12
+msgid ""
+"Forgotten your password? Enter your e-mail address below, and we'll reset "
+"your password and e-mail the new one to you."
+msgstr "Забравена парола? Няма проблеми. Въведете си e-mail адреса по-долу и ще ви изпратим нова!"
+
+#: contrib/admin/templates/registration/password_reset_form.html:16
+msgid "E-mail address:"
+msgstr "E-mail адрес:"
+
+#: contrib/admin/templates/registration/password_reset_form.html:16
+msgid "Reset my password"
+msgstr "Нова парола"
+
+#: contrib/admin/views/main.py:223
+msgid "Site administration"
+msgstr "Администрация на сайта"
+
+#: contrib/admin/views/main.py:257 contrib/admin/views/auth.py:19
+#, python-format
+msgid "The %(name)s \"%(obj)s\" was added successfully."
+msgstr "Обектът %(name)s \"%(obj)s\" бе успешно добавен. "
+
+#: contrib/admin/views/main.py:261 contrib/admin/views/main.py:347
+#: contrib/admin/views/auth.py:24
+msgid "You may edit it again below."
+msgstr "Може да го редактирате пак по-долу. "
+
+#: contrib/admin/views/main.py:271 contrib/admin/views/main.py:356
+#, python-format
+msgid "You may add another %s below."
+msgstr "Може да добавите още един обект %s по-долу. "
+
+#: contrib/admin/views/main.py:289
+#, python-format
+msgid "Add %s"
+msgstr "Добави %s"
+
+#: contrib/admin/views/main.py:335
+#, python-format
+msgid "Added %s."
+msgstr "Добавен %s."
+
+#: contrib/admin/views/main.py:337
+#, python-format
+msgid "Changed %s."
+msgstr "Променен %s."
+
+#: contrib/admin/views/main.py:339
+#, python-format
+msgid "Deleted %s."
+msgstr "Изтрит %s."
+
+#: contrib/admin/views/main.py:342
+msgid "No fields changed."
+msgstr "Няма променени полета."
+
+#: contrib/admin/views/main.py:345
+#, python-format
+msgid "The %(name)s \"%(obj)s\" was changed successfully."
+msgstr "Обектът %(name)s \"%(obj)s\" бе успешно актуализиран. "
+
+#: contrib/admin/views/main.py:353
+#, python-format
+msgid "The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
+msgstr "Обектът %(name)s \"%(obj)s\" бе успешно добавен. Може да го редактирате по-долу. "
+
+#: contrib/admin/views/main.py:391
+#, python-format
+msgid "Change %s"
+msgstr "Промени %s"
+
+#: contrib/admin/views/main.py:476
+#, python-format
+msgid "One or more %(fieldname)s in %(name)s: %(obj)s"
+msgstr "Едно или повече %(fieldname)s в %(name)s: %(obj)s"
+
+#: contrib/admin/views/main.py:481
+#, python-format
+msgid "One or more %(fieldname)s in %(name)s:"
+msgstr "Едно или повече %(fieldname)s в %(name)s:"
+
+#: contrib/admin/views/main.py:514
+#, python-format
+msgid "The %(name)s \"%(obj)s\" was deleted successfully."
+msgstr "Обектът %(name)s \"%(obj)s\" бе успешно изтрит. "
+
+#: contrib/admin/views/main.py:517
+msgid "Are you sure?"
+msgstr "Сериозно?"
+
+#: contrib/admin/views/main.py:539
+#, python-format
+msgid "Change history: %s"
+msgstr "История на промените: %s"
+
+#: contrib/admin/views/main.py:573
+#, python-format
+msgid "Select %s"
+msgstr "Изберете %s"
+
+#: contrib/admin/views/main.py:573
+#, python-format
+msgid "Select %s to change"
+msgstr "Изберете %s за промяна"
+
+#: contrib/admin/views/main.py:768
+msgid "Database error"
+msgstr "Грешка с базата данни"
+
+#: contrib/admin/views/decorators.py:62
+msgid ""
+"Please log in again, because your session has expired. Don't worry: Your "
+"submission has been saved."
+msgstr "Влезте пак, понеже сесията ви изтече. Не се притеснявайте -- данните ви са записани. "
+
+#: contrib/admin/views/decorators.py:69
+msgid ""
+"Looks like your browser isn't configured to accept cookies. Please enable "
+"cookies, reload this page, and try again."
+msgstr "Браузерът ви май не е настроен да приема cookies. Пуснете ги и заредете страницата на ново. "
+
+#: contrib/admin/views/decorators.py:83
+msgid "Usernames cannot contain the '@' character."
+msgstr "Потребителските имена не могат да съдържат символа '@'."
+
+#: contrib/admin/views/decorators.py:85
+#, python-format
+msgid "Your e-mail address is not your username. Try '%s' instead."
+msgstr "E-mail адресът ти не ти е потребителско име. Пробвай '%s'."
+
+#: contrib/admin/views/doc.py:46 contrib/admin/views/doc.py:48
+#: contrib/admin/views/doc.py:50
+msgid "tag:"
+msgstr "таг:"
+
+#: contrib/admin/views/doc.py:77 contrib/admin/views/doc.py:79
+#: contrib/admin/views/doc.py:81
+msgid "filter:"
+msgstr "филтър:"
+
+#: contrib/admin/views/doc.py:135 contrib/admin/views/doc.py:137
+#: contrib/admin/views/doc.py:139
+msgid "view:"
+msgstr "изглед:"
+
+#: contrib/admin/views/doc.py:164
+#, python-format
+msgid "App %r not found"
+msgstr "Приложението %r липсва"
+
+#: contrib/admin/views/doc.py:171
+#, python-format
+msgid "Model %(name)r not found in app %(label)r"
+msgstr "Моделът %(name)r го няма в приложение %(label)r"
+
+#: contrib/admin/views/doc.py:183
+#, python-format
+msgid "the related `%(label)s.%(type)s` object"
+msgstr "свързаният обект `%(label)s.%(type)s` "
+
+#: contrib/admin/views/doc.py:183 contrib/admin/views/doc.py:205
+#: contrib/admin/views/doc.py:219 contrib/admin/views/doc.py:224
+msgid "model:"
+msgstr "модел:"
+
+#: contrib/admin/views/doc.py:214
+#, python-format
+msgid "related `%(label)s.%(name)s` objects"
+msgstr "свързани `%(label)s.%(name)s` обекти"
+
+#: contrib/admin/views/doc.py:219
+#, python-format
+msgid "all %s"
+msgstr "всички %s"
+
+#: contrib/admin/views/doc.py:224
+#, python-format
+msgid "number of %s"
+msgstr "брой %s"
+
+#: contrib/admin/views/doc.py:229
+#, python-format
+msgid "Fields on %s objects"
+msgstr "Полета на %s обекти"
+
+#: contrib/admin/views/doc.py:291 contrib/admin/views/doc.py:301
+#: contrib/admin/views/doc.py:303 contrib/admin/views/doc.py:309
+#: contrib/admin/views/doc.py:310 contrib/admin/views/doc.py:312
+msgid "Integer"
+msgstr "Цяло число"
+
+#: contrib/admin/views/doc.py:292
+msgid "Boolean (Either True or False)"
+msgstr "Boolean (True или False)"
+
+#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:311
+#, python-format
+msgid "String (up to %(maxlength)s)"
+msgstr "Стринг (до %(maxlength)s символа)"
+
+#: contrib/admin/views/doc.py:294
+msgid "Comma-separated integers"
+msgstr "Цели числа, разделени с запетая"
+
+#: contrib/admin/views/doc.py:295
+msgid "Date (without time)"
+msgstr "Дата (без час)"
+
+#: contrib/admin/views/doc.py:296
+msgid "Date (with time)"
+msgstr "Дата (и час)"
+
+#: contrib/admin/views/doc.py:297
+msgid "E-mail address"
+msgstr "E-mail адрес"
+
+#: contrib/admin/views/doc.py:298 contrib/admin/views/doc.py:299
+#: contrib/admin/views/doc.py:302
+msgid "File path"
+msgstr "Път към файл"
+
+#: contrib/admin/views/doc.py:300
+msgid "Decimal number"
+msgstr "Десетична дроб"
+
+#: contrib/admin/views/doc.py:306
+msgid "Boolean (Either True, False or None)"
+msgstr "Boolean (Възможните стойности са True, False или None)"
+
+#: contrib/admin/views/doc.py:307
+msgid "Relation to parent model"
+msgstr "Връзка с родителския обект"
+
+#: contrib/admin/views/doc.py:308
+msgid "Phone number"
+msgstr "Телефонен номер"
+
+#: contrib/admin/views/doc.py:313
+msgid "Text"
+msgstr "Текст"
+
+#: contrib/admin/views/doc.py:314
+msgid "Time"
+msgstr "Час"
+
+#: contrib/admin/views/doc.py:316
+msgid "U.S. state (two uppercase letters)"
+msgstr "американски щат (две главни букви)"
+
+#: contrib/admin/views/doc.py:317
+msgid "XML text"
+msgstr "XML текст"
+
+#: contrib/admin/views/doc.py:343
+#, python-format
+msgid "%s does not appear to be a urlpattern object"
+msgstr "%s не прилича на обект от тип urlpattern"
+
+#: contrib/admin/views/auth.py:30
+msgid "Add user"
+msgstr "Добави потребител"
+
+#: contrib/admin/views/auth.py:57
+msgid "Password changed successfully."
+msgstr "Паролата бе успешно сменена. "
+
+#: contrib/admin/views/auth.py:64
+#, python-format
+msgid "Change password: %s"
+msgstr "Смени парола: %s"
+
+#: newforms/fields.py:103 newforms/fields.py:256
+#, python-format
+msgid "Ensure this value has at most %d characters."
+msgstr "Тази стойност трябва да има не повече от %d символа. "
+
+#: newforms/fields.py:105 newforms/fields.py:258
+#, python-format
+msgid "Ensure this value has at least %d characters."
+msgstr "Тази стойност трябва да има поне %d символа. "
+
+#: newforms/fields.py:128 core/validators.py:120
+msgid "Enter a whole number."
+msgstr "Въведете цяло число. "
+
+#: newforms/fields.py:130
+#, python-format
+msgid "Ensure this value is less than or equal to %s."
+msgstr "Тази стойност трябва да е по-малка или равна на %s. "
+
+#: newforms/fields.py:132
+#, python-format
+msgid "Ensure this value is greater than or equal to %s."
+msgstr "Тази стойност трябва да е по-голяма или равна на %s."
+
+#: newforms/fields.py:165
+msgid "Enter a valid date."
+msgstr "Въведете валидна дата. "
+
+#: newforms/fields.py:192
+msgid "Enter a valid time."
+msgstr "Въведете валиден час."
+
+#: newforms/fields.py:228
+msgid "Enter a valid date/time."
+msgstr "Въведете валидна дата / час. "
+
+#: newforms/fields.py:242
+msgid "Enter a valid value."
+msgstr "Въведете валидна стойност. "
+
+#: newforms/fields.py:271 core/validators.py:162
+msgid "Enter a valid e-mail address."
+msgstr "Въведете валиден e-mail адрес. "
+
+#: newforms/fields.py:289 newforms/fields.py:311
+msgid "Enter a valid URL."
+msgstr "Въведете валиден URL. "
+
+#: newforms/fields.py:313
+msgid "This URL appears to be a broken link."
+msgstr "Този URL май е счупен линк. "
+
+#: newforms/fields.py:362 newforms/models.py:165
+msgid "Select a valid choice. That choice is not one of the available choices."
+msgstr "Направете валиден избор. Този не е един от възможните избори. "
+
+#: newforms/fields.py:380 newforms/fields.py:456 newforms/models.py:182
+msgid "Enter a list of values."
+msgstr "Въведете списък от стойности"
+
+#: newforms/fields.py:389 newforms/models.py:188
+#, python-format
+msgid "Select a valid choice. %s is not one of the available choices."
+msgstr "Направете валиден избор. %s не е един от възможните избори. "
+
+#: template/defaultfilters.py:491
+msgid "yes,no,maybe"
+msgstr "да,не,може-би"
+
+#: views/generic/create_update.py:43
+#, python-format
+msgid "The %(verbose_name)s was created successfully."
+msgstr "Обектът %(verbose_name)s бе успешно създаден. "
+
+#: views/generic/create_update.py:117
+#, python-format
+msgid "The %(verbose_name)s was updated successfully."
+msgstr "Обектът %(verbose_name)s бе успешно актуализиран."
+
+#: views/generic/create_update.py:184
+#, python-format
+msgid "The %(verbose_name)s was deleted."
+msgstr "Обектът %(verbose_name)s бе затрит."
+
+#: core/validators.py:64
+msgid "This value must contain only letters, numbers and underscores."
+msgstr "Тази стойност може да съдържа само букви, цифри и подчертавки. "
+
+#: core/validators.py:68
+msgid ""
+"This value must contain only letters, numbers, underscores, dashes or "
+"slashes."
+msgstr "Тази стойност може да съдържа само букви, цифри, подчертавки, тиренца и наклонени чертички. "
+
+#: core/validators.py:72
+msgid "This value must contain only letters, numbers, underscores or hyphens."
+msgstr "Тази стойност трябва да съдържа само букви, цифри, подчертавки или тиренца. "
+
+#: core/validators.py:76
+msgid "Uppercase letters are not allowed here."
+msgstr "Тук не се допускат главни букви. "
+
+#: core/validators.py:80
+msgid "Lowercase letters are not allowed here."
+msgstr "Тук не се допускат малки букви. "
+
+#: core/validators.py:87
+msgid "Enter only digits separated by commas."
+msgstr "Въведете само цифри, разделени със запетая. "
+
+#: core/validators.py:99
+msgid "Enter valid e-mail addresses separated by commas."
+msgstr "Въведете валидни e-mail адреси разделени със запетая. "
+
+#: core/validators.py:103
+msgid "Please enter a valid IP address."
+msgstr "Въведете валиден IP адрес. "
+
+#: core/validators.py:107
+msgid "Empty values are not allowed here."
+msgstr "Тук не се допускат празни стойности. "
+
+#: core/validators.py:111
+msgid "Non-numeric characters aren't allowed here."
+msgstr "Тук не се допускат символи, които не са цифри. "
+
+#: core/validators.py:115
+msgid "This value can't be comprised solely of digits."
+msgstr "Тази стойност не може да бъде само цифри. "
+
+#: core/validators.py:124
+msgid "Only alphabetical characters are allowed here."
+msgstr "Само букви и цифри се допускат тук. "
+
+#: core/validators.py:139
+msgid "Year must be 1900 or later."
+msgstr "Годината трябва да бъде от 1900 нагоре."
+
+#: core/validators.py:143
+#, python-format
+msgid "Invalid date: %s"
+msgstr "Невалидна дата: %s"
+
+#: core/validators.py:153
+msgid "Enter a valid time in HH:MM format."
+msgstr "Въведете валиден час във формат ЧЧ:ММ. "
+
+#: core/validators.py:178
+msgid ""
+"Upload a valid image. The file you uploaded was either not an image or a "
+"corrupted image."
+msgstr "Качете валидна картинка. Файлът, който сте качили или не е картинка или е развалена. "
+
+#: core/validators.py:185
+#, python-format
+msgid "The URL %s does not point to a valid image."
+msgstr "URL %s не сочи към валидна картинка. "
+
+#: core/validators.py:189
+#, python-format
+msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid."
+msgstr "Телефонните номера трябва да бъдат във формат XXX-XXX-XXXX. \"%s\" не е валиден. "
+
+#: core/validators.py:197
+#, python-format
+msgid "The URL %s does not point to a valid QuickTime video."
+msgstr "URL %s не сочи към валиден QuickTime видео клип."
+
+#: core/validators.py:201
+msgid "A valid URL is required."
+msgstr "Изисква се валиден URL."
+
+#: core/validators.py:215
+#, python-format
+msgid ""
+"Valid HTML is required. Specific errors are:\n"
+"%s"
+msgstr ""
+"Изисква се валиден HTML. Конкретните грешки са:\n"
+"%s"
+
+#: core/validators.py:222
+#, python-format
+msgid "Badly formed XML: %s"
+msgstr "Неправилно форматиран XML: %s"
+
+#: core/validators.py:239
+#, python-format
+msgid "Invalid URL: %s"
+msgstr "Невалиден URL: %s"
+
+#: core/validators.py:244 core/validators.py:246
+#, python-format
+msgid "The URL %s is a broken link."
+msgstr "Този URL %s е счупен линк. "
+
+#: core/validators.py:252
+msgid "Enter a valid U.S. state abbreviation."
+msgstr "Въведете валидно съкращение на американски щат. "
+
+#: core/validators.py:266
+#, python-format
+msgid "Watch your mouth! The word %s is not allowed here."
+msgid_plural "Watch your mouth! The words %s are not allowed here."
+msgstr[0] "Ей-шш! Думичката %s не е позволена тук. "
+msgstr[1] "Ей-шш! Думичките %s не са позволени тук. "
+
+#: core/validators.py:273
+#, python-format
+msgid "This field must match the '%s' field."
+msgstr "Това поле трябва да съвпада с полето '%s' ."
+
+#: core/validators.py:292
+msgid "Please enter something for at least one field."
+msgstr "Въведете нещичко поне за едното поле. "
+
+#: core/validators.py:301 core/validators.py:312
+msgid "Please enter both fields or leave them both empty."
+msgstr "Или въведете и двете полета или ги оставете празни."
+
+#: core/validators.py:320
+#, python-format
+msgid "This field must be given if %(field)s is %(value)s"
+msgstr "Това поле трябва да е зададено ако %(field)s е %(value)s"
+
+#: core/validators.py:333
+#, python-format
+msgid "This field must be given if %(field)s is not %(value)s"
+msgstr "Това поле трябва да е зададено ако %(field)s не е %(value)s"
+
+#: core/validators.py:352
+msgid "Duplicate values are not allowed."
+msgstr "Не са позволени стойности, които се повтарят. "
+
+#: core/validators.py:367
+#, python-format
+msgid "This value must be between %(lower)s and %(upper)s."
+msgstr "Тази стойност трябва да бъде между %(lower)s и %(upper)s."
+
+#: core/validators.py:369
+#, python-format
+msgid "This value must be at least %s."
+msgstr "Тази стойност трябва да бъде поне %s. "
+
+#: core/validators.py:371
+#, python-format
+msgid "This value must be no more than %s."
+msgstr "Тази стойност не трябва да надвишава %s."
+
+#: core/validators.py:407
+#, python-format
+msgid "This value must be a power of %s."
+msgstr "Тази стойност трябва да е точна степен на %s."
+
+#: core/validators.py:418
+msgid "Please enter a valid decimal number."
+msgstr "Въведете валидна десетична дроб. "
+
+#: core/validators.py:422
+#, python-format
+msgid "Please enter a valid decimal number with at most %s total digit."
+msgid_plural "Please enter a valid decimal number with at most %s total digits."
+msgstr[0] "Въведете валидна десетична дроб с не-повече от %s цифра."
+msgstr[1] "Въведете валидна десетична дроб с не-повече от %s цифри. "
+
+#: core/validators.py:425
+#, python-format
+msgid "Please enter a valid decimal number with a whole part of at most %s digit."
+msgid_plural "Please enter a valid decimal number with a whole part of at most %s digits."
+msgstr[0] "Въведете валидна десетична дроб, цялата част на която да не надхвърля %s цифра."
+msgstr[1] "Въведете валидна десетична дроб, цялата част на която да не надхвърля %s цифри. "
+
+#: core/validators.py:428
+#, python-format
+msgid "Please enter a valid decimal number with at most %s decimal place."
+msgid_plural "Please enter a valid decimal number with at most %s decimal places."
+msgstr[0] "Въведете валидна десетична дроб с не-повече от %s цифра след десетичната точка."
+msgstr[1] "Въведете валидна десетична дроб с не-повече от %s цифри след десетичната точка. "
+
+#: core/validators.py:438
+#, python-format
+msgid "Make sure your uploaded file is at least %s bytes big."
+msgstr "Файлът, който сте качили трябва да бъде поне %s байта."
+
+#: core/validators.py:439
+#, python-format
+msgid "Make sure your uploaded file is at most %s bytes big."
+msgstr "Размерът на файла, който сте качили не трябва да надхвърля %s байта. "
+
+#: core/validators.py:456
+msgid "The format for this field is wrong."
+msgstr "Форматът на това поле е грешен."
+
+#: core/validators.py:471
+msgid "This field is invalid."
+msgstr "Това поле не е валидно"
+
+#: core/validators.py:507
+#, python-format
+msgid "Could not retrieve anything from %s."
+msgstr "Не получихме нищо от %s."
+
+#: core/validators.py:510
+#, python-format
+msgid "The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'."
+msgstr "URL %(url)s върна невалиден Content-Type хедър '%(contenttype)s'."
+
+#: core/validators.py:543
+#, python-format
+msgid ""
+"Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with "
+"\"%(start)s\".)"
+msgstr ""
+"Затворете тага %(tag)s , който е отворен на ред %(line)s. (Редът започва с "
+"\"%(start)s\".)"
+
+#: core/validators.py:547
+#, python-format
+msgid ""
+"Some text starting on line %(line)s is not allowed in that context. (Line "
+"starts with \"%(start)s\".)"
+msgstr "Некъв текст на ред %(line)s не е позволен в този контекст. (Редът започва с \"%(start)s\".)"
+
+#: core/validators.py:552
+#, python-format
+msgid ""
+"\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%"
+"(start)s\".)"
+msgstr ""
+"\"%(attr)s\" на ред %(line)s е невалиден атрибут. (Редът започва с \"%"
+"(start)s\".)"
+
+#: core/validators.py:557
+#, python-format
+msgid ""
+"\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%"
+"(start)s\".)"
+msgstr ""
+"\"<%(tag)s>\" на ред %(line)s е невалиден таг. (Редът започва с \"%"
+"(start)s\".)"
+
+#: core/validators.py:561
+#, python-format
+msgid ""
+"A tag on line %(line)s is missing one or more required attributes. (Line "
+"starts with \"%(start)s\".)"
+msgstr "На някой от таговете на ред %(line)s му липсват един или повече задължителни атрибута. (Редът започва с \"%(start)s\".)"
+
+#: core/validators.py:566
+#, python-format
+msgid ""
+"The \"%(attr)s\" attribute on line %(line)s has an invalid value. (Line "
+"starts with \"%(start)s\".)"
+msgstr "Атрибутът \"%(attr)s\" на ред %(line)s има невалидна стойност. (Редът започва с \"%(start)s\".)"
+
diff --git a/django/conf/locale/bg/LC_MESSAGES/djangojs.mo b/django/conf/locale/bg/LC_MESSAGES/djangojs.mo
new file mode 100644
index 0000000000..e912789236
Binary files /dev/null and b/django/conf/locale/bg/LC_MESSAGES/djangojs.mo differ
diff --git a/django/conf/locale/bg/LC_MESSAGES/djangojs.po b/django/conf/locale/bg/LC_MESSAGES/djangojs.po
new file mode 100644
index 0000000000..2caffdca53
--- /dev/null
+++ b/django/conf/locale/bg/LC_MESSAGES/djangojs.po
@@ -0,0 +1,106 @@
+# translation of djangojs.po to Bulgarian
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: djangojs\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2005-12-09 11:51+0100\n"
+"PO-Revision-Date: 2007-05-12 17:51+0300\n"
+"Last-Translator: Jordan Dimov \n"
+"Language-Team: Bulgarian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: contrib/admin/media/js/SelectFilter2.js:33
+#, perl-format
+msgid "Available %s"
+msgstr "Налични %s"
+
+#: contrib/admin/media/js/SelectFilter2.js:41
+msgid "Choose all"
+msgstr "Избери всички"
+
+#: contrib/admin/media/js/SelectFilter2.js:46
+msgid "Add"
+msgstr "Добави"
+
+#: contrib/admin/media/js/SelectFilter2.js:48
+msgid "Remove"
+msgstr "Премахни"
+
+#: contrib/admin/media/js/SelectFilter2.js:53
+#, perl-format
+msgid "Chosen %s"
+msgstr "Избрахме %s"
+
+#: contrib/admin/media/js/SelectFilter2.js:54
+msgid "Select your choice(s) and click "
+msgstr "Направете своя избор и щракнете "
+
+#: contrib/admin/media/js/SelectFilter2.js:59
+msgid "Clear all"
+msgstr "Изчисти всички"
+
+#: contrib/admin/media/js/dateparse.js:26
+#: contrib/admin/media/js/calendar.js:24
+msgid ""
+"January February March April May June July August September October November "
+"December"
+msgstr "Януари Февруари Март Април Май Юни Юли Август Септември Октомври Ноември Декември"
+
+#: contrib/admin/media/js/dateparse.js:27
+msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
+msgstr "Неделя Понеделник Вторник Сряда Четвъртък Петък Събота"
+
+#: contrib/admin/media/js/calendar.js:25
+msgid "S M T W T F S"
+msgstr "Н П В С Ч П С"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
+msgid "Now"
+msgstr "Сега"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
+msgid "Clock"
+msgstr "Часовник"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
+msgid "Choose a time"
+msgstr "Избери време"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
+msgid "Midnight"
+msgstr "Полунощ"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
+msgid "6 a.m."
+msgstr "6 a.m."
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
+msgid "Noon"
+msgstr "По обяд"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
+msgid "Cancel"
+msgstr "Отказ"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
+msgid "Today"
+msgstr "Днес"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
+msgid "Calendar"
+msgstr "Календар"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
+msgid "Yesterday"
+msgstr "Вчера"
+
+#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
+msgid "Tomorrow"
+msgstr "Утре"
+
diff --git a/django/contrib/formtools/preview.py b/django/contrib/formtools/preview.py
index daecba7928..351d991762 100644
--- a/django/contrib/formtools/preview.py
+++ b/django/contrib/formtools/preview.py
@@ -24,7 +24,7 @@ Usage
Subclass FormPreview and define a done() method:
- def done(self, request, clean_data):
+ def done(self, request, cleaned_data):
# ...
This method takes an HttpRequest object and a dictionary of the form data after
@@ -113,7 +113,7 @@ class FormPreview(object):
if f.is_valid():
if self.security_hash(request, f) != request.POST.get(self.unused_name('hash')):
return self.failed_hash(request) # Security hash failed.
- return self.done(request, f.clean_data)
+ return self.done(request, f.cleaned_data)
else:
return render_to_response(self.form_template,
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state},
@@ -160,6 +160,9 @@ class FormPreview(object):
# METHODS SUBCLASSES MUST OVERRIDE ########################################
- def done(self, request, clean_data):
- "Does something with the clean_data and returns an HttpResponseRedirect."
+ def done(self, request, cleaned_data):
+ """
+ Does something with the cleaned_data and returns an
+ HttpResponseRedirect.
+ """
raise NotImplementedError('You must define a done() method on your %s subclass.' % self.__class__.__name__)
diff --git a/django/contrib/localflavor/is_/forms.py b/django/contrib/localflavor/is_/forms.py
index d052acf579..41727d8fa3 100644
--- a/django/contrib/localflavor/is_/forms.py
+++ b/django/contrib/localflavor/is_/forms.py
@@ -41,7 +41,7 @@ class ISIdNumberField(RegexField):
method is modulo 11.
"""
check = [3, 2, 7, 6, 5, 4, 3, 2, 1, 0]
- return sum(int(value[i]) * check[i] for i in range(10)) % 11 == 0
+ return sum([int(value[i]) * check[i] for i in range(10)]) % 11 == 0
def _format(self, value):
"""
diff --git a/django/core/management.py b/django/core/management.py
index ea79bf5262..30a4d6848c 100644
--- a/django/core/management.py
+++ b/django/core/management.py
@@ -1407,20 +1407,24 @@ def load_data(fixture_labels, verbosity=1):
if verbosity > 1:
print "No %s fixture '%s' in %s." % \
(format, fixture_name, humanize(fixture_dir))
- if count[0] == 0:
- if verbosity > 0:
- print "No fixtures found."
- else:
- if verbosity > 0:
- print "Installed %d object(s) from %d fixture(s)" % tuple(count)
+
+ if count[0] > 0:
sequence_sql = backend.get_sql_sequence_reset(style, models)
if sequence_sql:
if verbosity > 1:
print "Resetting sequences"
for line in sequence_sql:
cursor.execute(line)
+
transaction.commit()
transaction.leave_transaction_management()
+
+ if count[0] == 0:
+ if verbosity > 0:
+ print "No fixtures found."
+ else:
+ if verbosity > 0:
+ print "Installed %d object(s) from %d fixture(s)" % tuple(count)
load_data.help_doc = 'Installs the named fixture(s) in the database'
load_data.args = "[--verbosity] fixture, fixture, ..."
diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py
index 29ce6bf9bd..66dbbff335 100644
--- a/django/core/serializers/python.py
+++ b/django/core/serializers/python.py
@@ -37,7 +37,7 @@ class Serializer(base.Serializer):
def handle_fk_field(self, obj, field):
related = getattr(obj, field.name)
if related is not None:
- related = related._get_pk_val()
+ related = getattr(related, field.rel.field_name)
self._current[field.name] = related
def handle_m2m_field(self, obj, field):
@@ -80,7 +80,10 @@ def Deserializer(object_list, **options):
# Handle FK fields
elif field.rel and isinstance(field.rel, models.ManyToOneRel):
- data[field.attname] = field.rel.to._meta.pk.to_python(field_value)
+ if field_value:
+ data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value)
+ else:
+ data[field.attname] = None
# Handle all other fields
else:
diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py
index 3a0fdb5395..633001f5f0 100644
--- a/django/core/serializers/xml_serializer.py
+++ b/django/core/serializers/xml_serializer.py
@@ -82,7 +82,7 @@ class Serializer(base.Serializer):
self._start_relational_field(field)
related = getattr(obj, field.name)
if related is not None:
- self.xml.characters(str(related._get_pk_val()))
+ self.xml.characters(str(getattr(related, field.rel.field_name)))
else:
self.xml.addQuickElement("None")
self.xml.endElement("field")
@@ -181,7 +181,7 @@ class Deserializer(base.Deserializer):
if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None':
return None
else:
- return field.rel.to._meta.pk.to_python(
+ return field.rel.to._meta.get_field(field.rel.field_name).to_python(
getInnerText(node).strip().encode(self.encoding))
def _handle_m2m_field_node(self, node, field):
diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py
index dc0fbe3ab9..1ae16feb25 100644
--- a/django/db/backends/postgresql/base.py
+++ b/django/db/backends/postgresql/base.py
@@ -223,7 +223,7 @@ def get_sql_sequence_reset(style, model_list):
if isinstance(f, models.AutoField):
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
- style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)),
+ style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(quote_name(f.column)),
style.SQL_KEYWORD('FROM'),
@@ -232,7 +232,7 @@ def get_sql_sequence_reset(style, model_list):
for f in model._meta.many_to_many:
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
- style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()),
+ style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(quote_name('id')),
style.SQL_KEYWORD('FROM'),
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index d6f34f1fe1..9898b121f4 100644
--- a/django/db/backends/postgresql_psycopg2/base.py
+++ b/django/db/backends/postgresql_psycopg2/base.py
@@ -180,7 +180,7 @@ def get_sql_sequence_reset(style, model_list):
if isinstance(f, models.AutoField):
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
- style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)),
+ style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(quote_name(f.column)),
style.SQL_KEYWORD('FROM'),
@@ -189,7 +189,7 @@ def get_sql_sequence_reset(style, model_list):
for f in model._meta.many_to_many:
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
- style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()),
+ style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(quote_name('id')),
style.SQL_KEYWORD('FROM'),
diff --git a/django/newforms/forms.py b/django/newforms/forms.py
index 42a06fd00d..0eec71a516 100644
--- a/django/newforms/forms.py
+++ b/django/newforms/forms.py
@@ -187,13 +187,13 @@ class BaseForm(StrAndUnicode):
def full_clean(self):
"""
- Cleans all of self.data and populates self.__errors and self.clean_data.
+ Cleans all of self.data and populates self.__errors and self.cleaned_data.
"""
errors = ErrorDict()
if not self.is_bound: # Stop further processing.
self.__errors = errors
return
- self.clean_data = {}
+ self.cleaned_data = {}
for name, field in self.fields.items():
# value_from_datadict() gets the data from the dictionary.
# Each widget type knows how to retrieve its own data, because some
@@ -201,18 +201,18 @@ class BaseForm(StrAndUnicode):
value = field.widget.value_from_datadict(self.data, self.add_prefix(name))
try:
value = field.clean(value)
- self.clean_data[name] = value
+ self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
- self.clean_data[name] = value
+ self.cleaned_data[name] = value
except ValidationError, e:
errors[name] = e.messages
try:
- self.clean_data = self.clean()
+ self.cleaned_data = self.clean()
except ValidationError, e:
errors[NON_FIELD_ERRORS] = e.messages
if errors:
- delattr(self, 'clean_data')
+ delattr(self, 'cleaned_data')
self.__errors = errors
def clean(self):
@@ -222,7 +222,7 @@ class BaseForm(StrAndUnicode):
not be associated with a particular field; it will have a special-case
association with the field named '__all__'.
"""
- return self.clean_data
+ return self.cleaned_data
class Form(BaseForm):
"A collection of Fields, plus their associated data."
@@ -273,6 +273,8 @@ class BoundField(StrAndUnicode):
attrs['id'] = auto_id
if not self.form.is_bound:
data = self.form.initial.get(self.name, self.field.initial)
+ if callable(data):
+ data = data()
else:
data = self.data
return widget.render(self.html_name, data, attrs=attrs)
diff --git a/django/newforms/formsets.py b/django/newforms/formsets.py
index 330fb088b0..6eaaf12a5e 100644
--- a/django/newforms/formsets.py
+++ b/django/newforms/formsets.py
@@ -33,7 +33,7 @@ class BaseFormSet(object):
if data:
self.management_form = ManagementForm(data, auto_id=self.auto_id, prefix=self.prefix)
if self.management_form.is_valid():
- self.total_forms = self.management_form.clean_data[FORM_COUNT_FIELD_NAME]
+ self.total_forms = self.management_form.cleaned_data[FORM_COUNT_FIELD_NAME]
self.required_forms = self.total_forms - self.num_extra
else:
# not sure that ValidationError is the best thing to raise here
@@ -67,7 +67,7 @@ class BaseFormSet(object):
def full_clean(self):
"""
- Cleans all of self.data and populates self.__errors and self.clean_data.
+ Cleans all of self.data and populates self.__errors and self.cleaned_data.
"""
is_valid = True
@@ -75,7 +75,7 @@ class BaseFormSet(object):
if not self.is_bound: # Stop further processing.
self.__errors = errors
return
- clean_data = []
+ cleaned_data = []
deleted_data = []
self._form_list = []
@@ -103,12 +103,12 @@ class BaseFormSet(object):
self.add_fields(form, i)
else:
# if the formset is still vaild overall and this form instance
- # is valid, keep appending to clean_data
+ # is valid, keep appending to cleaned_data
if is_valid and form.is_valid():
- if self.deletable and form.clean_data[DELETION_FIELD_NAME]:
- deleted_data.append(form.clean_data)
+ if self.deletable and form.cleaned_data[DELETION_FIELD_NAME]:
+ deleted_data.append(form.cleaned_data)
else:
- clean_data.append(form.clean_data)
+ cleaned_data.append(form.cleaned_data)
else:
is_valid = False
# append to errors regardless
@@ -117,14 +117,14 @@ class BaseFormSet(object):
deleted_data.reverse()
if self.orderable:
- clean_data.sort(lambda x,y: x[ORDERING_FIELD_NAME] - y[ORDERING_FIELD_NAME])
+ cleaned_data.sort(lambda x,y: x[ORDERING_FIELD_NAME] - y[ORDERING_FIELD_NAME])
else:
- clean_data.reverse()
+ cleaned_data.reverse()
errors.reverse()
self._form_list.reverse()
if is_valid:
- self.clean_data = clean_data
+ self.cleaned_data = cleaned_data
self.deleted_data = deleted_data
self.errors = errors
self._is_valid = is_valid
diff --git a/django/newforms/models.py b/django/newforms/models.py
index a60002b705..9d7d027031 100644
--- a/django/newforms/models.py
+++ b/django/newforms/models.py
@@ -12,19 +12,9 @@ from widgets import Select, SelectMultiple, MultipleHiddenInput
__all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
'ModelChoiceField', 'ModelMultipleChoiceField')
-def model_save(self, commit=True):
+def save_instance(form, instance, fields=None, fail_message='saved', commit=True):
"""
- Creates and returns model instance according to self.clean_data.
-
- This method is created for any form_for_model Form.
- """
- if self.errors:
- raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name)
- return save_instance(self, self._model(), commit)
-
-def save_instance(form, instance, commit=True):
- """
- Saves bound Form ``form``'s clean_data into model instance ``instance``.
+ Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
Assumes ``form`` has a field for every non-AutoField database field in
``instance``. If commit=True, then the changes to ``instance`` will be
@@ -33,30 +23,40 @@ def save_instance(form, instance, commit=True):
from django.db import models
opts = instance.__class__._meta
if form.errors:
- raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name)
- clean_data = form.clean_data
+ raise ValueError("The %s could not be %s because the data didn't validate." % (opts.object_name, fail_message))
+ cleaned_data = form.cleaned_data
for f in opts.fields:
- if not f.editable or isinstance(f, models.AutoField) or not f.name in clean_data:
+ if not f.editable or isinstance(f, models.AutoField) or not f.name in cleaned_data:
continue
- setattr(instance, f.name, clean_data[f.name])
+ if fields and f.name not in fields:
+ continue
+ setattr(instance, f.name, cleaned_data[f.name])
if commit:
instance.save()
for f in opts.many_to_many:
- if f.name in clean_data:
- setattr(instance, f.attname, clean_data[f.name])
+ if fields and f.name not in fields:
+ continue
+ if f.name in cleaned_data:
+ setattr(instance, f.attname, cleaned_data[f.name])
# GOTCHA: If many-to-many data is given and commit=False, the many-to-many
# data will be lost. This happens because a many-to-many options cannot be
# set on an object until after it's saved. Maybe we should raise an
# exception in that case.
return instance
-def make_instance_save(instance):
- "Returns the save() method for a form_for_instance Form."
+def make_model_save(model, fields, fail_message):
+ "Returns the save() method for a Form."
def save(self, commit=True):
- return save_instance(self, instance, commit)
+ return save_instance(self, model(), fields, fail_message, commit)
+ return save
+
+def make_instance_save(instance, fields, fail_message):
+ "Returns the save() method for a Form."
+ def save(self, commit=True):
+ return save_instance(self, instance, fields, fail_message, commit)
return save
-def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield()):
+def form_for_model(model, form=BaseForm, fields=None, formfield_callback=lambda f: f.formfield()):
"""
Returns a Form class for the given Django model class.
@@ -71,13 +71,16 @@ def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfiel
for f in opts.fields + opts.many_to_many:
if not f.editable:
continue
+ if fields and not f.name in fields:
+ continue
formfield = formfield_callback(f)
if formfield:
field_list.append((f.name, formfield))
- fields = SortedDictFromList(field_list)
- return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save})
+ base_fields = SortedDictFromList(field_list)
+ return type(opts.object_name + 'Form', (form,),
+ {'base_fields': base_fields, '_model': model, 'save': make_model_save(model, fields, 'created')})
-def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
+def form_for_instance(instance, form=BaseForm, fields=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
"""
Returns a Form class for the given Django model instance.
@@ -94,13 +97,15 @@ def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kw
for f in opts.fields + opts.many_to_many:
if not f.editable:
continue
+ if fields and not f.name in fields:
+ continue
current_value = f.value_from_object(instance)
formfield = formfield_callback(f, initial=current_value)
if formfield:
field_list.append((f.name, formfield))
- fields = SortedDictFromList(field_list)
+ base_fields = SortedDictFromList(field_list)
return type(opts.object_name + 'InstanceForm', (form,),
- {'base_fields': fields, '_model': model, 'save': make_instance_save(instance)})
+ {'base_fields': base_fields, '_model': model, 'save': make_instance_save(instance, fields, 'changed')})
def form_for_fields(field_list):
"Returns a Form class for the given list of Django database field instances."
diff --git a/django/test/testcases.py b/django/test/testcases.py
index 788e215ca1..dd1f73befd 100644
--- a/django/test/testcases.py
+++ b/django/test/testcases.py
@@ -45,16 +45,16 @@ class TestCase(unittest.TestCase):
if hasattr(self, 'fixtures'):
management.load_data(self.fixtures, verbosity=0)
mail.outbox = []
-
- def run(self, result=None):
- """Wrapper around default run method to perform common Django test set up.
- This means that user-defined Test Cases aren't required to include a call
- to super().setUp().
-
+
+ def __call__(self, result=None):
+ """
+ Wrapper around default __call__ method to perform common Django test
+ set up. This means that user-defined Test Cases aren't required to
+ include a call to super().setUp().
"""
self.client = Client()
self._pre_setup()
- super(TestCase, self).run(result)
+ super(TestCase, self).__call__(result)
def assertRedirects(self, response, expected_path, status_code=302, target_status_code=200):
"""Assert that a response redirected to a specific URL, and that the
@@ -62,7 +62,7 @@ class TestCase(unittest.TestCase):
"""
self.assertEqual(response.status_code, status_code,
- "Response didn't redirect: Reponse code was %d (expected %d)" %
+ "Response didn't redirect as expected: Reponse code was %d (expected %d)" %
(response.status_code, status_code))
scheme, netloc, path, params, query, fragment = urlparse(response['Location'])
self.assertEqual(path, expected_path,
@@ -70,7 +70,7 @@ class TestCase(unittest.TestCase):
redirect_response = self.client.get(path)
self.assertEqual(redirect_response.status_code, target_status_code,
"Couldn't retrieve redirection page '%s': response code was %d (expected %d)" %
- (path, response.status_code, status_code))
+ (path, redirect_response.status_code, target_status_code))
def assertContains(self, response, text, count=1, status_code=200):
"""Assert that a response indicates that a page was retreived successfully,
@@ -108,7 +108,7 @@ class TestCase(unittest.TestCase):
for err in errors:
if field:
if field in context[form].errors:
- self.assertTrue(err in context[form].errors[field],
+ self.failUnless(err in context[form].errors[field],
"The field '%s' on form '%s' in context %d does not contain the error '%s' (actual errors: %s)" %
(field, form, i, err, list(context[form].errors[field])))
elif field in context[form].fields:
@@ -117,7 +117,7 @@ class TestCase(unittest.TestCase):
else:
self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field))
else:
- self.assertTrue(err in context[form].non_field_errors(),
+ self.failUnless(err in context[form].non_field_errors(),
"The form '%s' in context %d does not contain the non-field error '%s' (actual errors: %s)" %
(form, i, err, list(context[form].non_field_errors())))
if not found_form:
@@ -127,7 +127,7 @@ class TestCase(unittest.TestCase):
"Assert that the template with the provided name was used in rendering the response"
if isinstance(response.template, list):
template_names = [t.name for t in response.template]
- self.assertTrue(template_name in template_names,
+ self.failUnless(template_name in template_names,
"Template '%s' was not one of the templates used to render the response. Templates used: %s" %
(template_name, template_names))
elif response.template:
@@ -140,9 +140,9 @@ class TestCase(unittest.TestCase):
def assertTemplateNotUsed(self, response, template_name):
"Assert that the template with the provided name was NOT used in rendering the response"
if isinstance(response.template, list):
- self.assertFalse(template_name in [t.name for t in response.template],
+ self.failIf(template_name in [t.name for t in response.template],
"Template '%s' was used unexpectedly in rendering the response" % template_name)
elif response.template:
self.assertNotEqual(template_name, response.template.name,
"Template '%s' was used unexpectedly in rendering the response" % template_name)
-
\ No newline at end of file
+
diff --git a/docs/databrowse.txt b/docs/databrowse.txt
index e9691cc879..9c03e7e4ea 100644
--- a/docs/databrowse.txt
+++ b/docs/databrowse.txt
@@ -44,7 +44,11 @@ How to use Databrowse
It doesn't matter where you put this, as long as it gets executed at
some point. A good place for it is in your URLconf file (``urls.py``).
- 3. Add the following line to your URLconf::
+ 3. Change your URLconf to import the ``databrowse`` module::
+
+ from django.contrib import databrowse
+
+ ...and add the following line to your URLconf::
(r'^databrowse/(.*)', databrowse.site.root),
diff --git a/docs/i18n.txt b/docs/i18n.txt
index 1d7a0063b2..27abadacc9 100644
--- a/docs/i18n.txt
+++ b/docs/i18n.txt
@@ -236,7 +236,7 @@ To pluralize, specify both the singular and plural forms with the
``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and
``{% endblocktrans %}``. Example::
- {% blocktrans count list|count as counter %}
+ {% blocktrans count list|length as counter %}
There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
diff --git a/docs/newforms.txt b/docs/newforms.txt
index ddb850f54c..ed43670960 100644
--- a/docs/newforms.txt
+++ b/docs/newforms.txt
@@ -9,28 +9,30 @@ framework. This document explains how to use this new library.
Migration plan
==============
-``django.newforms`` currently is only available in Django beginning
-with the 0.96 release. the Django development version -- i.e., it's
-not available in the Django 0.95 release. For the next Django release,
-our plan is to do the following:
+``django.newforms`` is new in Django's 0.96 release, but, as it won't be new
+forever, we plan to rename it to ``django.forms`` in the future. The current
+``django.forms`` package will be available as ``django.oldforms`` until Django
+1.0, when we plan to remove it for good.
- * As of revision [4208], we've copied the current ``django.forms`` to
- ``django.oldforms``. This allows you to upgrade your code *now* rather
- than waiting for the backwards-incompatible change and rushing to fix
- your code after the fact. Just change your import statements like this::
+That has direct repercussions on the forward compatibility of your code. Please
+read the following migration plan and code accordingly:
+
+ * The old forms framework (the current ``django.forms``) has been copied to
+ ``django.oldforms``. Thus, you can start upgrading your code *now*,
+ rather than waiting for the future backwards-incompatible change, by
+ changing your import statements like this::
from django import forms # old
from django import oldforms as forms # new
- * At an undecided future date, we will move the current ``django.newforms``
- to ``django.forms``. This will be a backwards-incompatible change, and
- anybody who is still using the old version of ``django.forms`` at that
- time will need to change their import statements, as described in the
- previous bullet.
+ * In the next Django release (0.97), we will move the current
+ ``django.newforms`` to ``django.forms``. This will be a
+ backwards-incompatible change, and anybody who is still using the old
+ version of ``django.forms`` at that time will need to change their import
+ statements, as described in the previous bullet.
* We will remove ``django.oldforms`` in the release *after* the next Django
- release -- the release that comes after the release in which we're
- creating the new ``django.forms``.
+ release -- either 0.98 or 1.0, whichever comes first.
With this in mind, we recommend you use the following import statement when
using ``django.newforms``::
@@ -184,7 +186,7 @@ e-mail address::
>>> f.is_valid()
False
-Access the ``Form`` attribute ``errors`` to get a dictionary of error messages::
+Access the ``errors`` attribute to get a dictionary of error messages::
>>> f.errors
{'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']}
@@ -197,6 +199,10 @@ You can access ``errors`` without having to call ``is_valid()`` first. The
form's data will be validated the first time either you call ``is_valid()`` or
access ``errors``.
+The validation routines will only get called once, regardless of how many times
+you access ``errors`` or call ``is_valid()``. This means that if validation has
+side effects, those side effects will only be triggered once.
+
Behavior of unbound forms
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -224,7 +230,7 @@ object. Regardless of whether you pass it a string in the format
it's valid.
Once you've created a ``Form`` instance with a set of data and validated it,
-you can access the clean data via the ``clean_data`` attribute of the ``Form``
+you can access the clean data via the ``cleaned_data`` attribute of the ``Form``
object::
>>> data = {'subject': 'hello',
@@ -234,7 +240,7 @@ object::
>>> f = ContactForm(data)
>>> f.is_valid()
True
- >>> f.clean_data
+ >>> f.cleaned_data
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
Note that any text-based field -- such as ``CharField`` or ``EmailField`` --
@@ -242,7 +248,7 @@ always cleans the input into a Unicode string. We'll cover the encoding
implications later in this document.
If your data does *not* validate, your ``Form`` instance will not have a
-``clean_data`` attribute::
+``cleaned_data`` attribute::
>>> data = {'subject': '',
... 'message': 'Hi there',
@@ -251,15 +257,15 @@ If your data does *not* validate, your ``Form`` instance will not have a
>>> f = ContactForm(data)
>>> f.is_valid()
False
- >>> f.clean_data
+ >>> f.cleaned_data
Traceback (most recent call last):
...
- AttributeError: 'ContactForm' object has no attribute 'clean_data'
+ AttributeError: 'ContactForm' object has no attribute 'cleaned_data'
-``clean_data`` will always *only* contain a key for fields defined in the
+``cleaned_data`` will always *only* contain a key for fields defined in the
``Form``, even if you pass extra data when you define the ``Form``. In this
example, we pass a bunch of extra fields to the ``ContactForm`` constructor,
-but ``clean_data`` contains only the form's fields::
+but ``cleaned_data`` contains only the form's fields::
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
@@ -271,9 +277,30 @@ but ``clean_data`` contains only the form's fields::
>>> f = ContactForm(data)
>>> f.is_valid()
True
- >>> f.clean_data # Doesn't contain extra_field_1, etc.
+ >>> f.cleaned_data # Doesn't contain extra_field_1, etc.
{'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'}
+``cleaned_data`` will include a key and value for *all* fields defined in the
+``Form``, even if the data didn't include a value for fields that are not
+required. In this example, the data dictionary doesn't include a value for the
+``nick_name`` field, but ``cleaned_data`` includes it, with an empty value::
+
+ >>> class OptionalPersonForm(Form):
+ ... first_name = CharField()
+ ... last_name = CharField()
+ ... nick_name = CharField(required=False)
+ >>> data = {'first_name': u'John', 'last_name': u'Lennon'}
+ >>> f = OptionalPersonForm(data)
+ >>> f.is_valid()
+ True
+ >>> f.cleaned_data
+ {'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'}
+
+In this above example, the ``cleaned_data`` value for ``nick_name`` is set to an
+empty string, because ``nick_name`` is ``CharField``, and ``CharField``\s treat
+empty values as an empty string. Each field type knows what its "blank" value
+is -- e.g., for ``DateField``, it's ``None`` instead of the empty string.
+
Behavior of unbound forms
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -281,10 +308,10 @@ It's meaningless to request "clean" data in a form with no data, but, for the
record, here's what happens with unbound forms::
>>> f = ContactForm()
- >>> f.clean_data
+ >>> f.cleaned_data
Traceback (most recent call last):
...
- AttributeError: 'ContactForm' object has no attribute 'clean_data'
+ AttributeError: 'ContactForm' object has no attribute 'cleaned_data'
Outputting forms as HTML
------------------------
@@ -454,7 +481,7 @@ field::
If ``auto_id`` is set to a string containing the format character ``'%s'``,
then the form output will include ``
'
+
+#######################
+# Miscellaneous Tests #
+#######################
+
+There once was a problem with Form fields called "data". Let's make sure that
+doesn't come back.
+>>> class DataForm(Form):
+... data = CharField(max_length=10)
+>>> f = DataForm({'data': 'xyzzy'})
+>>> f.is_valid()
+True
+>>> f.cleaned_data
+{'data': u'xyzzy'}
"""
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index f15034ec66..943cddd1d7 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -1775,7 +1775,7 @@ True
u''
>>> p.errors.as_text()
u''
->>> p.clean_data
+>>> p.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
>>> print p['first_name']
@@ -1811,10 +1811,10 @@ True
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
>>> p.is_valid()
False
->>> p.clean_data
+>>> p.cleaned_data
Traceback (most recent call last):
...
-AttributeError: 'Person' object has no attribute 'clean_data'
+AttributeError: 'Person' object has no attribute 'cleaned_data'
>>> print p
First name:
This field is required.
Last name:
This field is required.
@@ -1845,10 +1845,10 @@ False
{}
>>> p.is_valid()
False
->>> p.clean_data
+>>> p.cleaned_data
Traceback (most recent call last):
...
-AttributeError: 'Person' object has no attribute 'clean_data'
+AttributeError: 'Person' object has no attribute 'cleaned_data'
>>> print p
First name:
Last name:
@@ -1887,10 +1887,10 @@ u'
first_name
This field is re
* This field is required.
* birthday
* This field is required.
->>> p.clean_data
+>>> p.cleaned_data
Traceback (most recent call last):
...
-AttributeError: 'Person' object has no attribute 'clean_data'
+AttributeError: 'Person' object has no attribute 'cleaned_data'
>>> p['first_name'].errors
[u'This field is required.']
>>> p['first_name'].errors.as_ul()
@@ -1906,17 +1906,45 @@ u'* This field is required.'
>>> print p['birthday']
-clean_data will always *only* contain a key for fields defined in the
+cleaned_data will always *only* contain a key for fields defined in the
Form, even if you pass extra data when you define the Form. In this
example, we pass a bunch of extra fields to the form constructor,
-but clean_data contains only the form's fields.
+but cleaned_data contains only the form's fields.
>>> data = {'first_name': u'John', 'last_name': u'Lennon', 'birthday': u'1940-10-9', 'extra1': 'hello', 'extra2': 'hello'}
>>> p = Person(data)
>>> p.is_valid()
True
->>> p.clean_data
+>>> p.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
+cleaned_data will include a key and value for *all* fields defined in the Form,
+even if the Form's data didn't include a value for fields that are not
+required. In this example, the data dictionary doesn't include a value for the
+"nick_name" field, but cleaned_data includes it. For CharFields, it's set to the
+empty string.
+>>> class OptionalPersonForm(Form):
+... first_name = CharField()
+... last_name = CharField()
+... nick_name = CharField(required=False)
+>>> data = {'first_name': u'John', 'last_name': u'Lennon'}
+>>> f = OptionalPersonForm(data)
+>>> f.is_valid()
+True
+>>> f.cleaned_data
+{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'}
+
+For DateFields, it's set to None.
+>>> class OptionalPersonForm(Form):
+... first_name = CharField()
+... last_name = CharField()
+... birth_date = DateField(required=False)
+>>> data = {'first_name': u'John', 'last_name': u'Lennon'}
+>>> f = OptionalPersonForm(data)
+>>> f.is_valid()
+True
+>>> f.cleaned_data
+{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'}
+
"auto_id" tells the Form to add an "id" attribute to each form element.
If it's a string that contains '%s', Django will use that as a format string
into which the field's name will be inserted. It will also put a around
@@ -2265,19 +2293,19 @@ returns a list of input.
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False)
>>> f.errors
{}
->>> f.clean_data
+>>> f.cleaned_data
{'composers': [u'J'], 'name': u'Yesterday'}
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False)
>>> f.errors
{}
->>> f.clean_data
+>>> f.cleaned_data
{'composers': [u'J', u'P'], 'name': u'Yesterday'}
Validation errors are HTML-escaped when output as HTML.
>>> class EscapingForm(Form):
... special_name = CharField()
... def clean_special_name(self):
-... raise ValidationError("Something's wrong with '%s'" % self.clean_data['special_name'])
+... raise ValidationError("Something's wrong with '%s'" % self.cleaned_data['special_name'])
>>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False)
>>> print f
@@ -2292,7 +2320,7 @@ There are a couple of ways to do multiple-field validation. If you want the
validation message to be associated with a particular field, implement the
clean_XXX() method on the Form, where XXX is the field name. As in
Field.clean(), the clean_XXX() method should return the cleaned value. In the
-clean_XXX() method, you have access to self.clean_data, which is a dictionary
+clean_XXX() method, you have access to self.cleaned_data, which is a dictionary
of all the data that has been cleaned *so far*, in order by the fields,
including the current field (e.g., the field XXX if you're in clean_XXX()).
>>> class UserRegistration(Form):
@@ -2300,9 +2328,9 @@ including the current field (e.g., the field XXX if you're in clean_XXX()).
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput)
... def clean_password2(self):
-... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
+... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
... raise ValidationError(u'Please make sure your passwords match.')
-... return self.clean_data['password2']
+... return self.cleaned_data['password2']
>>> f = UserRegistration(auto_id=False)
>>> f.errors
{}
@@ -2315,14 +2343,14 @@ including the current field (e.g., the field XXX if you're in clean_XXX()).
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
->>> f.clean_data
+>>> f.cleaned_data
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
Another way of doing multiple-field validation is by implementing the
Form's clean() method. If you do this, any ValidationError raised by that
method will not be associated with a particular field; it will have a
special-case association with the field named '__all__'.
-Note that in Form.clean(), you have access to self.clean_data, a dictionary of
+Note that in Form.clean(), you have access to self.cleaned_data, a dictionary of
all the fields/values that have *not* raised a ValidationError. Also note
Form.clean() is required to return a dictionary of all clean data.
>>> class UserRegistration(Form):
@@ -2330,9 +2358,9 @@ Form.clean() is required to return a dictionary of all clean data.
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput)
... def clean(self):
-... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
+... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
... raise ValidationError(u'Please make sure your passwords match.')
-... return self.clean_data
+... return self.cleaned_data
>>> f = UserRegistration(auto_id=False)
>>> f.errors
{}
@@ -2359,7 +2387,7 @@ Form.clean() is required to return a dictionary of all clean data.
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
->>> f.clean_data
+>>> f.cleaned_data
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
# Dynamic construction ########################################################
@@ -2753,6 +2781,64 @@ then the latter will get precedence.
Username:
Password:
+# Callable initial data ########################################################
+
+The previous technique dealt with raw values as initial data, but it's also
+possible to specify callable data.
+
+>>> class UserRegistration(Form):
+... username = CharField(max_length=10)
+... password = CharField(widget=PasswordInput)
+
+We need to define functions that get called later.
+>>> def initial_django():
+... return 'django'
+>>> def initial_stephane():
+... return 'stephane'
+
+Here, we're not submitting any data, so the initial value will be displayed.
+>>> p = UserRegistration(initial={'username': initial_django}, auto_id=False)
+>>> print p.as_ul()
+
Username:
+
Password:
+
+The 'initial' parameter is meaningless if you pass data.
+>>> p = UserRegistration({}, initial={'username': initial_django}, auto_id=False)
+>>> print p.as_ul()
+
+
+A callable 'initial' value is *not* used as a fallback if data is not provided.
+In this example, we don't provide a value for 'username', and the form raises a
+validation error rather than using the initial value for 'username'.
+>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django})
+>>> p.errors
+{'username': [u'This field is required.']}
+>>> p.is_valid()
+False
+
+If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(),
+then the latter will get precedence.
+>>> class UserRegistration(Form):
+... username = CharField(max_length=10, initial=initial_django)
+... password = CharField(widget=PasswordInput)
+>>> p = UserRegistration(auto_id=False)
+>>> print p.as_ul()
+
Username:
+
Password:
+>>> p = UserRegistration(initial={'username': initial_stephane}, auto_id=False)
+>>> print p.as_ul()
+
Username:
+
Password:
+
# Help text ###################################################################
You can specify descriptive text for a field by using the 'help_text' argument
@@ -2869,7 +2955,7 @@ actual field name.
{}
>>> p.is_valid()
True
->>> p.clean_data
+>>> p.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
Let's try submitting some bad data to make sure form.errors and field.errors
@@ -2913,12 +2999,12 @@ of the same form.
>>> p1 = Person(data, prefix='person1')
>>> p1.is_valid()
True
->>> p1.clean_data
+>>> p1.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
>>> p2 = Person(data, prefix='person2')
>>> p2.is_valid()
True
->>> p2.clean_data
+>>> p2.cleaned_data
{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)}
By default, forms append a hyphen between the prefix and the field name, but a
@@ -2944,7 +3030,7 @@ self.prefix.
>>> p = Person(data, prefix='foo')
>>> p.is_valid()
True
->>> p.clean_data
+>>> p.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
# Forms with NullBooleanFields ################################################
@@ -3006,16 +3092,16 @@ is different than its data. This is handled transparently, though.
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput)
... def clean(self):
-... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
+... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
... raise ValidationError(u'Please make sure your passwords match.')
-... return self.clean_data
+... return self.cleaned_data
>>> def my_function(method, post_data):
... if method == 'POST':
... form = UserRegistration(post_data, auto_id=False)
... else:
... form = UserRegistration(auto_id=False)
... if form.is_valid():
-... return 'VALID: %r' % form.clean_data
+... return 'VALID: %r' % form.cleaned_data
... t = Template('')
... return t.render(Context({'form': form}))
@@ -3053,9 +3139,9 @@ VALID: {'username': u'adrian', 'password1': u'secret', 'password2': u'secret'}
... password1 = CharField(widget=PasswordInput)
... password2 = CharField(widget=PasswordInput)
... def clean(self):
-... if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']:
+... if self.cleaned_data.get('password1') and self.cleaned_data.get('password2') and self.cleaned_data['password1'] != self.cleaned_data['password2']:
... raise ValidationError(u'Please make sure your passwords match.')
-... return self.clean_data
+... return self.cleaned_data
You have full flexibility in displaying form fields in a template. Just pass a
Form instance to the template, and use "dot" access to refer to individual
@@ -3321,7 +3407,7 @@ True
# MultiWidget and MultiValueField #############################################
-# MultiWidgets are widgets composed of other widgets. They are usually
+# MultiWidgets are widgets composed of other widgets. They are usually
# combined with MultiValueFields - a field that is composed of other fields.
# MulitWidgets can themselved be composed of other MultiWidgets.
# SplitDateTimeWidget is one example of a MultiWidget.
@@ -3329,7 +3415,7 @@ True
>>> class ComplexMultiWidget(MultiWidget):
... def __init__(self, attrs=None):
... widgets = (
-... TextInput(),
+... TextInput(),
... SelectMultiple(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))),
... SplitDateTimeWidget(),
... )
@@ -3354,13 +3440,13 @@ True
>>> class ComplexField(MultiValueField):
-... def __init__(self, required=True, widget=None, label=None, initial=None):
+... def __init__(self, required=True, widget=None, label=None, initial=None):
... fields = (
-... CharField(),
+... CharField(),
... MultipleChoiceField(choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo'))),
... SplitDateTimeField()
... )
-... super(ComplexField, self).__init__(fields, required, widget, label, initial)
+... super(ComplexField, self).__init__(fields, required, widget, label, initial)
...
... def compress(self, data_list):
... if data_list:
@@ -3405,7 +3491,7 @@ ValidationError: [u'This field is required.']
->>> f.clean_data
+>>> f.cleaned_data
{'field1': u'some text,JP,2007-04-25 06:24:00'}
#################################
diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py
index c287b6e0d6..fea5c94cab 100644
--- a/tests/regressiontests/serializers_regress/models.py
+++ b/tests/regressiontests/serializers_regress/models.py
@@ -100,6 +100,12 @@ class Anchor(models.Model):
something for other models to point at"""
data = models.CharField(maxlength=30)
+
+class UniqueAnchor(models.Model):
+ """This is a model that can be used as
+ something for other models to point at"""
+
+ data = models.CharField(unique=True, maxlength=30)
class FKData(models.Model):
data = models.ForeignKey(Anchor, null=True)
@@ -116,6 +122,10 @@ class FKSelfData(models.Model):
class M2MSelfData(models.Model):
data = models.ManyToManyField('self', null=True, symmetrical=False)
+
+class FKDataToField(models.Model):
+ data = models.ForeignKey(UniqueAnchor, null=True, to_field='data')
+
# The following test classes are for validating the
# deserialization of objects that use a user-defined
# field as the primary key.
diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py
index 97b3fbacbe..317739dac4 100644
--- a/tests/regressiontests/serializers_regress/tests.py
+++ b/tests/regressiontests/serializers_regress/tests.py
@@ -159,6 +159,7 @@ The end."""),
(data_obj, 300, Anchor, "Anchor 1"),
(data_obj, 301, Anchor, "Anchor 2"),
+ (data_obj, 302, UniqueAnchor, "UAnchor 1"),
(fk_obj, 400, FKData, 300), # Post reference
(fk_obj, 401, FKData, 500), # Pre reference
@@ -184,8 +185,13 @@ The end."""),
(m2m_obj, 445, M2MSelfData, []),
(m2m_obj, 446, M2MSelfData, []),
+ (fk_obj, 450, FKDataToField, "UAnchor 1"),
+ (fk_obj, 451, FKDataToField, "UAnchor 2"),
+ (fk_obj, 452, FKDataToField, None),
+
(data_obj, 500, Anchor, "Anchor 3"),
(data_obj, 501, Anchor, "Anchor 4"),
+ (data_obj, 502, UniqueAnchor, "UAnchor 2"),
(pk_obj, 601, BooleanPKData, True),
(pk_obj, 602, BooleanPKData, False),
diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
index c39fafe314..40d022a47a 100644
--- a/tests/regressiontests/test_client_regress/models.py
+++ b/tests/regressiontests/test_client_regress/models.py
@@ -60,7 +60,35 @@ class AssertTemplateUsedTests(TestCase):
self.assertTemplateUsed(response, "Valid POST Template")
except AssertionError, e:
self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: ['form_view.html', 'base.html']")
+
+class AssertRedirectsTests(TestCase):
+ def test_redirect_page(self):
+ "An assertion is raised if the original page couldn't be retrieved as expected"
+ # This page will redirect with code 301, not 302
+ response = self.client.get('/test_client/permanent_redirect_view/')
+ try:
+ self.assertRedirects(response, '/test_client/get_view/')
+ except AssertionError, e:
+ self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)")
+
+ def test_incorrect_target(self):
+ "An assertion is raised if the response redirects to another target"
+ response = self.client.get('/test_client/permanent_redirect_view/')
+ try:
+ # Should redirect to get_view
+ self.assertRedirects(response, '/test_client/some_view/')
+ except AssertionError, e:
+ self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)")
+ def test_target_page(self):
+ "An assertion is raised if the reponse redirect target cannot be retrieved as expected"
+ response = self.client.get('/test_client/double_redirect_view/')
+ try:
+ # The redirect target responds with a 301 code, not 200
+ self.assertRedirects(response, '/test_client/permanent_redirect_view/')
+ except AssertionError, e:
+ self.assertEquals(str(e), "Couldn't retrieve redirection page '/test_client/permanent_redirect_view/': response code was 301 (expected 200)")
+
class AssertFormErrorTests(TestCase):
def test_unknown_form(self):
"An assertion is raised if the form name is unknown"