Merged recent changes from trunk.

This commit is contained in:
Russell Keith-Magee 2012-08-20 13:09:09 +08:00
commit 8e3fd703d0
940 changed files with 21954 additions and 15445 deletions

21
AUTHORS
View File

@ -29,6 +29,8 @@ The PRIMARY AUTHORS are (and/or have been):
* Julien Phalip * Julien Phalip
* Aymeric Augustin * Aymeric Augustin
* Claude Paroz * Claude Paroz
* Anssi Kääriäinen
* Florian Apolloner
More information on the main contributors to Django can be found in More information on the main contributors to Django can be found in
docs/internals/committers.txt. docs/internals/committers.txt.
@ -50,6 +52,7 @@ answer newbie questions, and generally made Django that much better:
Antoni Aloy Antoni Aloy
Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com> Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
AgarFu <heaven@croasanaso.sytes.net> AgarFu <heaven@croasanaso.sytes.net>
James Aylett
Dagur Páll Ammendrup <dagurp@gmail.com> Dagur Páll Ammendrup <dagurp@gmail.com>
Collin Anderson <cmawebsite@gmail.com> Collin Anderson <cmawebsite@gmail.com>
Jeff Anderson <jefferya@programmerq.net> Jeff Anderson <jefferya@programmerq.net>
@ -59,7 +62,6 @@ answer newbie questions, and generally made Django that much better:
andy@jadedplanet.net andy@jadedplanet.net
Fabrice Aneche <akh@nobugware.com> Fabrice Aneche <akh@nobugware.com>
ant9000@netwise.it ant9000@netwise.it
Florian Apolloner <florian@apolloner.eu>
arien <regexbot@gmail.com> arien <regexbot@gmail.com>
David Ascher <http://ascher.ca/> David Ascher <http://ascher.ca/>
atlithorn <atlithorn@gmail.com> atlithorn <atlithorn@gmail.com>
@ -84,6 +86,7 @@ answer newbie questions, and generally made Django that much better:
Esdras Beleza <linux@esdrasbeleza.com> Esdras Beleza <linux@esdrasbeleza.com>
Chris Bennett <chrisrbennett@yahoo.com> Chris Bennett <chrisrbennett@yahoo.com>
James Bennett James Bennett
Danilo Bargen
Shai Berger <shai@platonix.com> Shai Berger <shai@platonix.com>
Julian Bez Julian Bez
Arvis Bickovskis <viestards.lists@gmail.com> Arvis Bickovskis <viestards.lists@gmail.com>
@ -136,9 +139,10 @@ answer newbie questions, and generally made Django that much better:
Robert Coup Robert Coup
Pete Crosier <pete.crosier@gmail.com> Pete Crosier <pete.crosier@gmail.com>
Matt Croydon <http://www.postneo.com/> Matt Croydon <http://www.postneo.com/>
Leah Culver <leah.culver@gmail.com>
flavio.curella@gmail.com
Jure Cuhalev <gandalf@owca.info> Jure Cuhalev <gandalf@owca.info>
Leah Culver <leah.culver@gmail.com>
Raúl Cumplido <raulcumplido@gmail.com>
flavio.curella@gmail.com
John D'Agostino <john.dagostino@gmail.com> John D'Agostino <john.dagostino@gmail.com>
dackze+django@gmail.com dackze+django@gmail.com
Jim Dalton <jim.dalton@gmail.com> Jim Dalton <jim.dalton@gmail.com>
@ -157,6 +161,7 @@ answer newbie questions, and generally made Django that much better:
Rajesh Dhawan <rajesh.dhawan@gmail.com> Rajesh Dhawan <rajesh.dhawan@gmail.com>
Sander Dijkhuis <sander.dijkhuis@gmail.com> Sander Dijkhuis <sander.dijkhuis@gmail.com>
Jordan Dimov <s3x3y1@gmail.com> Jordan Dimov <s3x3y1@gmail.com>
Riccardo Di Virgilio
Nebojša Dorđević Nebojša Dorđević
dne@mayonnaise.net dne@mayonnaise.net
dready <wil@mojipage.com> dready <wil@mojipage.com>
@ -171,6 +176,7 @@ answer newbie questions, and generally made Django that much better:
Clint Ecker Clint Ecker
Nick Efford <nick@efford.org> Nick Efford <nick@efford.org>
eibaan@gmail.com eibaan@gmail.com
David Eklund
Julia Elman Julia Elman
enlight enlight
Enrico <rico.bl@gmail.com> Enrico <rico.bl@gmail.com>
@ -219,6 +225,7 @@ answer newbie questions, and generally made Django that much better:
pradeep.gowda@gmail.com pradeep.gowda@gmail.com
Collin Grady <collin@collingrady.com> Collin Grady <collin@collingrady.com>
Gabriel Grant <g@briel.ca> Gabriel Grant <g@briel.ca>
Daniel Greenfeld
Simon Greenhill <dev@simon.net.nz> Simon Greenhill <dev@simon.net.nz>
Owen Griffiths Owen Griffiths
Espen Grindhaug <http://grindhaug.org/> Espen Grindhaug <http://grindhaug.org/>
@ -229,6 +236,7 @@ answer newbie questions, and generally made Django that much better:
Scot Hacker <shacker@birdhouse.org> Scot Hacker <shacker@birdhouse.org>
dAniel hAhler dAniel hAhler
hambaloney hambaloney
Will Hardy <django@willhardy.com.au>
Brian Harring <ferringb@gmail.com> Brian Harring <ferringb@gmail.com>
Brant Harris Brant Harris
Ronny Haryanto <http://ronny.haryan.to/> Ronny Haryanto <http://ronny.haryan.to/>
@ -273,7 +281,6 @@ answer newbie questions, and generally made Django that much better:
jpellerin@gmail.com jpellerin@gmail.com
junzhang.jn@gmail.com junzhang.jn@gmail.com
Xia Kai <http://blog.xiaket.org/> Xia Kai <http://blog.xiaket.org/>
Anssi Kääriäinen
Antti Kaihola <http://djangopeople.net/akaihola/> Antti Kaihola <http://djangopeople.net/akaihola/>
Peter van Kampen Peter van Kampen
Bahadır Kandemir <bahadir@pardus.org.tr> Bahadır Kandemir <bahadir@pardus.org.tr>
@ -366,6 +373,7 @@ answer newbie questions, and generally made Django that much better:
michael.mcewan@gmail.com michael.mcewan@gmail.com
Paul McLanahan <paul@mclanahan.net> Paul McLanahan <paul@mclanahan.net>
Tobias McNulty <http://www.caktusgroup.com/blog> Tobias McNulty <http://www.caktusgroup.com/blog>
Andrews Medina <andrewsmedina@gmail.com>
Zain Memon Zain Memon
Christian Metts Christian Metts
michal@plovarna.cz michal@plovarna.cz
@ -422,6 +430,7 @@ answer newbie questions, and generally made Django that much better:
polpak@yahoo.com polpak@yahoo.com
Ross Poulton <ross@rossp.org> Ross Poulton <ross@rossp.org>
Mihai Preda <mihai_preda@yahoo.com> Mihai Preda <mihai_preda@yahoo.com>
Daniele Procida <daniele@vurt.org>
Matthias Pronk <django@masida.nl> Matthias Pronk <django@masida.nl>
Jyrki Pulliainen <jyrki.pulliainen@gmail.com> Jyrki Pulliainen <jyrki.pulliainen@gmail.com>
Thejaswi Puthraya <thejaswi.puthraya@gmail.com> Thejaswi Puthraya <thejaswi.puthraya@gmail.com>
@ -450,6 +459,7 @@ answer newbie questions, and generally made Django that much better:
Armin Ronacher Armin Ronacher
Daniel Roseman <http://roseman.org.uk/> Daniel Roseman <http://roseman.org.uk/>
Rozza <ross.lawley@gmail.com> Rozza <ross.lawley@gmail.com>
Audrey Roy <http://audreymroy.com/>
Oliver Rutherfurd <http://rutherfurd.net/> Oliver Rutherfurd <http://rutherfurd.net/>
ryankanno ryankanno
Gonzalo Saavedra <gonzalosaavedra@gmail.com> Gonzalo Saavedra <gonzalosaavedra@gmail.com>
@ -458,6 +468,7 @@ answer newbie questions, and generally made Django that much better:
Vinay Sajip <vinay_sajip@yahoo.co.uk> Vinay Sajip <vinay_sajip@yahoo.co.uk>
Bartolome Sanchez Salado <i42sasab@uco.es> Bartolome Sanchez Salado <i42sasab@uco.es>
Kadesarin Sanjek Kadesarin Sanjek
Tim Saylor <tim.saylor@gmail.com>
Massimo Scamarcia <massimo.scamarcia@gmail.com> Massimo Scamarcia <massimo.scamarcia@gmail.com>
Paulo Scardine <paulo@scardine.com.br> Paulo Scardine <paulo@scardine.com.br>
David Schein David Schein
@ -502,6 +513,7 @@ answer newbie questions, and generally made Django that much better:
Aaron Swartz <http://www.aaronsw.com/> Aaron Swartz <http://www.aaronsw.com/>
Ville Säävuori <http://www.unessa.net/> Ville Säävuori <http://www.unessa.net/>
Mart Sõmermaa <http://mrts.pri.ee/> Mart Sõmermaa <http://mrts.pri.ee/>
Marc Tamlyn
Christian Tanzer <tanzer@swing.co.at> Christian Tanzer <tanzer@swing.co.at>
Tyler Tarabula <tyler.tarabula@gmail.com> Tyler Tarabula <tyler.tarabula@gmail.com>
Tyson Tate <tyson@fallingbullets.com> Tyson Tate <tyson@fallingbullets.com>
@ -550,6 +562,7 @@ answer newbie questions, and generally made Django that much better:
Mike Wiacek <mjwiacek@google.com> Mike Wiacek <mjwiacek@google.com>
Frank Wierzbicki Frank Wierzbicki
charly.wilhelm@gmail.com charly.wilhelm@gmail.com
Simon Williams
Derek Willis <http://blog.thescoop.org/> Derek Willis <http://blog.thescoop.org/>
Rachel Willmer <http://www.willmer.com/kb/> Rachel Willmer <http://www.willmer.com/kb/>
Jakub Wilk <ubanus@users.sf.net> Jakub Wilk <ubanus@users.sf.net>

10
django/bin/django-2to3.py Executable file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env python
# This works exactly like 2to3, except that it uses Django's fixers rather
# than 2to3's built-in fixers.
import sys
from lib2to3.main import main
sys.exit(main("django.utils.2to3_fixes"))

View File

@ -15,6 +15,7 @@ from django.conf import global_settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import LazyObject, empty from django.utils.functional import LazyObject, empty
from django.utils import importlib from django.utils import importlib
from django.utils import six
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
@ -73,7 +74,7 @@ class BaseSettings(object):
elif name == "ADMIN_MEDIA_PREFIX": elif name == "ADMIN_MEDIA_PREFIX":
warnings.warn("The ADMIN_MEDIA_PREFIX setting has been removed; " warnings.warn("The ADMIN_MEDIA_PREFIX setting has been removed; "
"use STATIC_URL instead.", DeprecationWarning) "use STATIC_URL instead.", DeprecationWarning)
elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, basestring): elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, six.string_types):
raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set " raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set "
"to a tuple, not a string.") "to a tuple, not a string.")
object.__setattr__(self, name, value) object.__setattr__(self, name, value)
@ -102,7 +103,10 @@ class Settings(BaseSettings):
if setting == setting.upper(): if setting == setting.upper():
setting_value = getattr(mod, setting) setting_value = getattr(mod, setting)
if setting in tuple_settings and \ if setting in tuple_settings and \
isinstance(setting_value, basestring): isinstance(setting_value, six.string_types):
warnings.warn("The %s setting must be a tuple. Please fix your "
"settings, as auto-correction is now deprecated." % setting,
PendingDeprecationWarning)
setting_value = (setting_value,) # In case the user forgot the comma. setting_value = (setting_value,) # In case the user forgot the comma.
setattr(self, setting, setting_value) setattr(self, setting, setting_value)
@ -154,7 +158,7 @@ class UserSettingsHolder(BaseSettings):
return getattr(self.default_settings, name) return getattr(self.default_settings, name)
def __dir__(self): def __dir__(self):
return self.__dict__.keys() + dir(self.default_settings) return list(self.__dict__) + dir(self.default_settings)
# For Python < 2.6: # For Python < 2.6:
__members__ = property(lambda self: self.__dir__()) __members__ = property(lambda self: self.__dir__())

View File

@ -270,19 +270,19 @@ SECRET_KEY = ''
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
# Absolute filesystem path to the directory that will hold user-uploaded files. # Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/home/media/media.lawrence.com/media/" # Example: "/var/www/example.com/media/"
MEDIA_ROOT = '' MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. # URL that handles the media served from MEDIA_ROOT.
# Example: "http://media.lawrence.com/media/" # Examples: "http://example.com/media/", "http://media.example.com/"
MEDIA_URL = '' MEDIA_URL = ''
# Absolute path to the directory that holds static files. # Absolute path to the directory static files should be collected to.
# Example: "/home/media/media.lawrence.com/static/" # Example: "/var/www/example.com/static/"
STATIC_ROOT = '' STATIC_ROOT = ''
# URL that handles the static files served from STATIC_ROOT. # URL that handles the static files served from STATIC_ROOT.
# Example: "http://media.lawrence.com/static/" # Example: "http://example.com/static/", "http://static.example.com/"
STATIC_URL = None STATIC_URL = None
# List of upload handler classes to be applied in order. # List of upload handler classes to be applied in order.
@ -451,7 +451,7 @@ MIDDLEWARE_CLASSES = (
SESSION_COOKIE_NAME = 'sessionid' # Cookie name. This can be whatever you want. SESSION_COOKIE_NAME = 'sessionid' # Cookie name. This can be whatever you want.
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks). SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2 # Age of cookie, in seconds (default: 2 weeks).
SESSION_COOKIE_DOMAIN = None # A string like ".lawrence.com", or None for standard domain cookie. SESSION_COOKIE_DOMAIN = None # A string like ".example.com", or None for standard domain cookie.
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only). SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
SESSION_COOKIE_PATH = '/' # The path of the session cookie. SESSION_COOKIE_PATH = '/' # The path of the session cookie.
SESSION_COOKIE_HTTPONLY = True # Whether to use the non-RFC standard httpOnly flag (IE, FF3+, others) SESSION_COOKIE_HTTPONLY = True # Whether to use the non-RFC standard httpOnly flag (IE, FF3+, others)

View File

@ -1,434 +1,436 @@
from __future__ import unicode_literals
LANG_INFO = { LANG_INFO = {
'ar': { 'ar': {
'bidi': True, 'bidi': True,
'code': 'ar', 'code': 'ar',
'name': 'Arabic', 'name': 'Arabic',
'name_local': u'\u0627\u0644\u0639\u0631\u0628\u064a\u0651\u0629', 'name_local': '\u0627\u0644\u0639\u0631\u0628\u064a\u0651\u0629',
}, },
'az': { 'az': {
'bidi': True, 'bidi': True,
'code': 'az', 'code': 'az',
'name': 'Azerbaijani', 'name': 'Azerbaijani',
'name_local': u'az\u0259rbaycan dili', 'name_local': 'az\u0259rbaycan dili',
}, },
'bg': { 'bg': {
'bidi': False, 'bidi': False,
'code': 'bg', 'code': 'bg',
'name': 'Bulgarian', 'name': 'Bulgarian',
'name_local': u'\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438', 'name_local': '\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438',
}, },
'bn': { 'bn': {
'bidi': False, 'bidi': False,
'code': 'bn', 'code': 'bn',
'name': 'Bengali', 'name': 'Bengali',
'name_local': u'\u09ac\u09be\u0982\u09b2\u09be', 'name_local': '\u09ac\u09be\u0982\u09b2\u09be',
}, },
'bs': { 'bs': {
'bidi': False, 'bidi': False,
'code': 'bs', 'code': 'bs',
'name': 'Bosnian', 'name': 'Bosnian',
'name_local': u'bosanski', 'name_local': 'bosanski',
}, },
'ca': { 'ca': {
'bidi': False, 'bidi': False,
'code': 'ca', 'code': 'ca',
'name': 'Catalan', 'name': 'Catalan',
'name_local': u'catal\xe0', 'name_local': 'catal\xe0',
}, },
'cs': { 'cs': {
'bidi': False, 'bidi': False,
'code': 'cs', 'code': 'cs',
'name': 'Czech', 'name': 'Czech',
'name_local': u'\u010desky', 'name_local': '\u010desky',
}, },
'cy': { 'cy': {
'bidi': False, 'bidi': False,
'code': 'cy', 'code': 'cy',
'name': 'Welsh', 'name': 'Welsh',
'name_local': u'Cymraeg', 'name_local': 'Cymraeg',
}, },
'da': { 'da': {
'bidi': False, 'bidi': False,
'code': 'da', 'code': 'da',
'name': 'Danish', 'name': 'Danish',
'name_local': u'Dansk', 'name_local': 'Dansk',
}, },
'de': { 'de': {
'bidi': False, 'bidi': False,
'code': 'de', 'code': 'de',
'name': 'German', 'name': 'German',
'name_local': u'Deutsch', 'name_local': 'Deutsch',
}, },
'el': { 'el': {
'bidi': False, 'bidi': False,
'code': 'el', 'code': 'el',
'name': 'Greek', 'name': 'Greek',
'name_local': u'\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac', 'name_local': '\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac',
}, },
'en': { 'en': {
'bidi': False, 'bidi': False,
'code': 'en', 'code': 'en',
'name': 'English', 'name': 'English',
'name_local': u'English', 'name_local': 'English',
}, },
'en-gb': { 'en-gb': {
'bidi': False, 'bidi': False,
'code': 'en-gb', 'code': 'en-gb',
'name': 'British English', 'name': 'British English',
'name_local': u'British English', 'name_local': 'British English',
}, },
'eo': { 'eo': {
'bidi': False, 'bidi': False,
'code': 'eo', 'code': 'eo',
'name': 'Esperanto', 'name': 'Esperanto',
'name_local': u'Esperanto', 'name_local': 'Esperanto',
}, },
'es': { 'es': {
'bidi': False, 'bidi': False,
'code': 'es', 'code': 'es',
'name': 'Spanish', 'name': 'Spanish',
'name_local': u'espa\xf1ol', 'name_local': 'espa\xf1ol',
}, },
'es-ar': { 'es-ar': {
'bidi': False, 'bidi': False,
'code': 'es-ar', 'code': 'es-ar',
'name': 'Argentinian Spanish', 'name': 'Argentinian Spanish',
'name_local': u'espa\xf1ol de Argentina', 'name_local': 'espa\xf1ol de Argentina',
}, },
'es-mx': { 'es-mx': {
'bidi': False, 'bidi': False,
'code': 'es-mx', 'code': 'es-mx',
'name': 'Mexican Spanish', 'name': 'Mexican Spanish',
'name_local': u'espa\xf1ol de Mexico', 'name_local': 'espa\xf1ol de Mexico',
}, },
'es-ni': { 'es-ni': {
'bidi': False, 'bidi': False,
'code': 'es-ni', 'code': 'es-ni',
'name': 'Nicaraguan Spanish', 'name': 'Nicaraguan Spanish',
'name_local': u'espa\xf1ol de Nicaragua', 'name_local': 'espa\xf1ol de Nicaragua',
}, },
'et': { 'et': {
'bidi': False, 'bidi': False,
'code': 'et', 'code': 'et',
'name': 'Estonian', 'name': 'Estonian',
'name_local': u'eesti', 'name_local': 'eesti',
}, },
'eu': { 'eu': {
'bidi': False, 'bidi': False,
'code': 'eu', 'code': 'eu',
'name': 'Basque', 'name': 'Basque',
'name_local': u'Basque', 'name_local': 'Basque',
}, },
'fa': { 'fa': {
'bidi': True, 'bidi': True,
'code': 'fa', 'code': 'fa',
'name': 'Persian', 'name': 'Persian',
'name_local': u'\u0641\u0627\u0631\u0633\u06cc', 'name_local': '\u0641\u0627\u0631\u0633\u06cc',
}, },
'fi': { 'fi': {
'bidi': False, 'bidi': False,
'code': 'fi', 'code': 'fi',
'name': 'Finnish', 'name': 'Finnish',
'name_local': u'suomi', 'name_local': 'suomi',
}, },
'fr': { 'fr': {
'bidi': False, 'bidi': False,
'code': 'fr', 'code': 'fr',
'name': 'French', 'name': 'French',
'name_local': u'Fran\xe7ais', 'name_local': 'Fran\xe7ais',
}, },
'fy-nl': { 'fy-nl': {
'bidi': False, 'bidi': False,
'code': 'fy-nl', 'code': 'fy-nl',
'name': 'Frisian', 'name': 'Frisian',
'name_local': u'Frisian', 'name_local': 'Frisian',
}, },
'ga': { 'ga': {
'bidi': False, 'bidi': False,
'code': 'ga', 'code': 'ga',
'name': 'Irish', 'name': 'Irish',
'name_local': u'Gaeilge', 'name_local': 'Gaeilge',
}, },
'gl': { 'gl': {
'bidi': False, 'bidi': False,
'code': 'gl', 'code': 'gl',
'name': 'Galician', 'name': 'Galician',
'name_local': u'galego', 'name_local': 'galego',
}, },
'he': { 'he': {
'bidi': True, 'bidi': True,
'code': 'he', 'code': 'he',
'name': 'Hebrew', 'name': 'Hebrew',
'name_local': u'\u05e2\u05d1\u05e8\u05d9\u05ea', 'name_local': '\u05e2\u05d1\u05e8\u05d9\u05ea',
}, },
'hi': { 'hi': {
'bidi': False, 'bidi': False,
'code': 'hi', 'code': 'hi',
'name': 'Hindi', 'name': 'Hindi',
'name_local': u'Hindi', 'name_local': 'Hindi',
}, },
'hr': { 'hr': {
'bidi': False, 'bidi': False,
'code': 'hr', 'code': 'hr',
'name': 'Croatian', 'name': 'Croatian',
'name_local': u'Hrvatski', 'name_local': 'Hrvatski',
}, },
'hu': { 'hu': {
'bidi': False, 'bidi': False,
'code': 'hu', 'code': 'hu',
'name': 'Hungarian', 'name': 'Hungarian',
'name_local': u'Magyar', 'name_local': 'Magyar',
}, },
'id': { 'id': {
'bidi': False, 'bidi': False,
'code': 'id', 'code': 'id',
'name': 'Indonesian', 'name': 'Indonesian',
'name_local': u'Bahasa Indonesia', 'name_local': 'Bahasa Indonesia',
}, },
'is': { 'is': {
'bidi': False, 'bidi': False,
'code': 'is', 'code': 'is',
'name': 'Icelandic', 'name': 'Icelandic',
'name_local': u'\xcdslenska', 'name_local': '\xcdslenska',
}, },
'it': { 'it': {
'bidi': False, 'bidi': False,
'code': 'it', 'code': 'it',
'name': 'Italian', 'name': 'Italian',
'name_local': u'italiano', 'name_local': 'italiano',
}, },
'ja': { 'ja': {
'bidi': False, 'bidi': False,
'code': 'ja', 'code': 'ja',
'name': 'Japanese', 'name': 'Japanese',
'name_local': u'\u65e5\u672c\u8a9e', 'name_local': '\u65e5\u672c\u8a9e',
}, },
'ka': { 'ka': {
'bidi': False, 'bidi': False,
'code': 'ka', 'code': 'ka',
'name': 'Georgian', 'name': 'Georgian',
'name_local': u'\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8', 'name_local': '\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8',
}, },
'kk': { 'kk': {
'bidi': False, 'bidi': False,
'code': 'kk', 'code': 'kk',
'name': 'Kazakh', 'name': 'Kazakh',
'name_local': u'\u049a\u0430\u0437\u0430\u049b', 'name_local': '\u049a\u0430\u0437\u0430\u049b',
}, },
'km': { 'km': {
'bidi': False, 'bidi': False,
'code': 'km', 'code': 'km',
'name': 'Khmer', 'name': 'Khmer',
'name_local': u'Khmer', 'name_local': 'Khmer',
}, },
'kn': { 'kn': {
'bidi': False, 'bidi': False,
'code': 'kn', 'code': 'kn',
'name': 'Kannada', 'name': 'Kannada',
'name_local': u'Kannada', 'name_local': 'Kannada',
}, },
'ko': { 'ko': {
'bidi': False, 'bidi': False,
'code': 'ko', 'code': 'ko',
'name': 'Korean', 'name': 'Korean',
'name_local': u'\ud55c\uad6d\uc5b4', 'name_local': '\ud55c\uad6d\uc5b4',
}, },
'lt': { 'lt': {
'bidi': False, 'bidi': False,
'code': 'lt', 'code': 'lt',
'name': 'Lithuanian', 'name': 'Lithuanian',
'name_local': u'Lithuanian', 'name_local': 'Lithuanian',
}, },
'lv': { 'lv': {
'bidi': False, 'bidi': False,
'code': 'lv', 'code': 'lv',
'name': 'Latvian', 'name': 'Latvian',
'name_local': u'latvie\u0161u', 'name_local': 'latvie\u0161u',
}, },
'mk': { 'mk': {
'bidi': False, 'bidi': False,
'code': 'mk', 'code': 'mk',
'name': 'Macedonian', 'name': 'Macedonian',
'name_local': u'\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438', 'name_local': '\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438',
}, },
'ml': { 'ml': {
'bidi': False, 'bidi': False,
'code': 'ml', 'code': 'ml',
'name': 'Malayalam', 'name': 'Malayalam',
'name_local': u'Malayalam', 'name_local': 'Malayalam',
}, },
'mn': { 'mn': {
'bidi': False, 'bidi': False,
'code': 'mn', 'code': 'mn',
'name': 'Mongolian', 'name': 'Mongolian',
'name_local': u'Mongolian', 'name_local': 'Mongolian',
}, },
'nb': { 'nb': {
'bidi': False, 'bidi': False,
'code': 'nb', 'code': 'nb',
'name': 'Norwegian Bokmal', 'name': 'Norwegian Bokmal',
'name_local': u'Norsk (bokm\xe5l)', 'name_local': 'Norsk (bokm\xe5l)',
}, },
'ne': { 'ne': {
'bidi': False, 'bidi': False,
'code': 'ne', 'code': 'ne',
'name': 'Nepali', 'name': 'Nepali',
'name_local': u'\u0928\u0947\u092a\u093e\u0932\u0940', 'name_local': '\u0928\u0947\u092a\u093e\u0932\u0940',
}, },
'nl': { 'nl': {
'bidi': False, 'bidi': False,
'code': 'nl', 'code': 'nl',
'name': 'Dutch', 'name': 'Dutch',
'name_local': u'Nederlands', 'name_local': 'Nederlands',
}, },
'nn': { 'nn': {
'bidi': False, 'bidi': False,
'code': 'nn', 'code': 'nn',
'name': 'Norwegian Nynorsk', 'name': 'Norwegian Nynorsk',
'name_local': u'Norsk (nynorsk)', 'name_local': 'Norsk (nynorsk)',
}, },
'no': { 'no': {
'bidi': False, 'bidi': False,
'code': 'no', 'code': 'no',
'name': 'Norwegian', 'name': 'Norwegian',
'name_local': u'Norsk', 'name_local': 'Norsk',
}, },
'pa': { 'pa': {
'bidi': False, 'bidi': False,
'code': 'pa', 'code': 'pa',
'name': 'Punjabi', 'name': 'Punjabi',
'name_local': u'Punjabi', 'name_local': 'Punjabi',
}, },
'pl': { 'pl': {
'bidi': False, 'bidi': False,
'code': 'pl', 'code': 'pl',
'name': 'Polish', 'name': 'Polish',
'name_local': u'polski', 'name_local': 'polski',
}, },
'pt': { 'pt': {
'bidi': False, 'bidi': False,
'code': 'pt', 'code': 'pt',
'name': 'Portuguese', 'name': 'Portuguese',
'name_local': u'Portugu\xeas', 'name_local': 'Portugu\xeas',
}, },
'pt-br': { 'pt-br': {
'bidi': False, 'bidi': False,
'code': 'pt-br', 'code': 'pt-br',
'name': 'Brazilian Portuguese', 'name': 'Brazilian Portuguese',
'name_local': u'Portugu\xeas Brasileiro', 'name_local': 'Portugu\xeas Brasileiro',
}, },
'ro': { 'ro': {
'bidi': False, 'bidi': False,
'code': 'ro', 'code': 'ro',
'name': 'Romanian', 'name': 'Romanian',
'name_local': u'Rom\xe2n\u0103', 'name_local': 'Rom\xe2n\u0103',
}, },
'ru': { 'ru': {
'bidi': False, 'bidi': False,
'code': 'ru', 'code': 'ru',
'name': 'Russian', 'name': 'Russian',
'name_local': u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439', 'name_local': '\u0420\u0443\u0441\u0441\u043a\u0438\u0439',
}, },
'sk': { 'sk': {
'bidi': False, 'bidi': False,
'code': 'sk', 'code': 'sk',
'name': 'Slovak', 'name': 'Slovak',
'name_local': u'slovensk\xfd', 'name_local': 'slovensk\xfd',
}, },
'sl': { 'sl': {
'bidi': False, 'bidi': False,
'code': 'sl', 'code': 'sl',
'name': 'Slovenian', 'name': 'Slovenian',
'name_local': u'Sloven\u0161\u010dina', 'name_local': 'Sloven\u0161\u010dina',
}, },
'sq': { 'sq': {
'bidi': False, 'bidi': False,
'code': 'sq', 'code': 'sq',
'name': 'Albanian', 'name': 'Albanian',
'name_local': u'Albanian', 'name_local': 'Albanian',
}, },
'sr': { 'sr': {
'bidi': False, 'bidi': False,
'code': 'sr', 'code': 'sr',
'name': 'Serbian', 'name': 'Serbian',
'name_local': u'\u0441\u0440\u043f\u0441\u043a\u0438', 'name_local': '\u0441\u0440\u043f\u0441\u043a\u0438',
}, },
'sr-latn': { 'sr-latn': {
'bidi': False, 'bidi': False,
'code': 'sr-latn', 'code': 'sr-latn',
'name': 'Serbian Latin', 'name': 'Serbian Latin',
'name_local': u'srpski (latinica)', 'name_local': 'srpski (latinica)',
}, },
'sv': { 'sv': {
'bidi': False, 'bidi': False,
'code': 'sv', 'code': 'sv',
'name': 'Swedish', 'name': 'Swedish',
'name_local': u'Svenska', 'name_local': 'Svenska',
}, },
'sw': { 'sw': {
'bidi': False, 'bidi': False,
'code': 'sw', 'code': 'sw',
'name': 'Swahili', 'name': 'Swahili',
'name_local': u'Kiswahili', 'name_local': 'Kiswahili',
}, },
'ta': { 'ta': {
'bidi': False, 'bidi': False,
'code': 'ta', 'code': 'ta',
'name': 'Tamil', 'name': 'Tamil',
'name_local': u'\u0ba4\u0bae\u0bbf\u0bb4\u0bcd', 'name_local': '\u0ba4\u0bae\u0bbf\u0bb4\u0bcd',
}, },
'te': { 'te': {
'bidi': False, 'bidi': False,
'code': 'te', 'code': 'te',
'name': 'Telugu', 'name': 'Telugu',
'name_local': u'\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41', 'name_local': '\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41',
}, },
'th': { 'th': {
'bidi': False, 'bidi': False,
'code': 'th', 'code': 'th',
'name': 'Thai', 'name': 'Thai',
'name_local': u'Thai', 'name_local': 'Thai',
}, },
'tr': { 'tr': {
'bidi': False, 'bidi': False,
'code': 'tr', 'code': 'tr',
'name': 'Turkish', 'name': 'Turkish',
'name_local': u'T\xfcrk\xe7e', 'name_local': 'T\xfcrk\xe7e',
}, },
'tt': { 'tt': {
'bidi': False, 'bidi': False,
'code': 'tt', 'code': 'tt',
'name': 'Tatar', 'name': 'Tatar',
'name_local': u'\u0422\u0430\u0442\u0430\u0440\u0447\u0430', 'name_local': '\u0422\u0430\u0442\u0430\u0440\u0447\u0430',
}, },
'uk': { 'uk': {
'bidi': False, 'bidi': False,
'code': 'uk', 'code': 'uk',
'name': 'Ukrainian', 'name': 'Ukrainian',
'name_local': u'\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430', 'name_local': '\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430',
}, },
'ur': { 'ur': {
'bidi': False, 'bidi': False,
'code': 'ur', 'code': 'ur',
'name': 'Urdu', 'name': 'Urdu',
'name_local': u'\u0627\u0631\u062f\u0648', 'name_local': '\u0627\u0631\u062f\u0648',
}, },
'vi': { 'vi': {
'bidi': False, 'bidi': False,
'code': 'vi', 'code': 'vi',
'name': 'Vietnamese', 'name': 'Vietnamese',
'name_local': u'Vietnamese', 'name_local': 'Vietnamese',
}, },
'zh-cn': { 'zh-cn': {
'bidi': False, 'bidi': False,
'code': 'zh-cn', 'code': 'zh-cn',
'name': 'Simplified Chinese', 'name': 'Simplified Chinese',
'name_local': u'\u7b80\u4f53\u4e2d\u6587', 'name_local': '\u7b80\u4f53\u4e2d\u6587',
}, },
'zh-tw': { 'zh-tw': {
'bidi': False, 'bidi': False,
'code': 'zh-tw', 'code': 'zh-tw',
'name': 'Traditional Chinese', 'name': 'Traditional Chinese',
'name_local': u'\u7e41\u9ad4\u4e2d\u6587', 'name_local': '\u7e41\u9ad4\u4e2d\u6587',
} }
} }

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -19,5 +20,5 @@ SHORT_DATE_FORMAT = 'd.m.Y'
# TIME_INPUT_FORMATS = # TIME_INPUT_FORMATS =
# DATETIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS =
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space THOUSAND_SEPARATOR = ' ' # Non-breaking space
# NUMBER_GROUPING = # NUMBER_GROUPING =

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -33,5 +34,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25' '%Y-%m-%d', # '2006-10-25'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -37,7 +37,7 @@ DATETIME_INPUT_FORMATS = (
'%m/%d/%y %H:%M', # '10/25/06 14:30' '%m/%d/%y %H:%M', # '10/25/06 14:30'
'%m/%d/%y', # '10/25/06' '%m/%d/%y', # '10/25/06'
) )
DECIMAL_SEPARATOR = u'.' DECIMAL_SEPARATOR = '.'
THOUSAND_SEPARATOR = u',' THOUSAND_SEPARATOR = ','
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
DATE_FORMAT = r'j \d\e F \d\e Y' DATE_FORMAT = r'j \d\e F \d\e Y'
TIME_FORMAT = 'H:i:s' TIME_FORMAT = 'H:i:s'
@ -24,5 +25,5 @@ DATETIME_INPUT_FORMATS = (
'%d/%m/%y %H:%M', '%d/%m/%y %H:%M',
) )
DECIMAL_SEPARATOR = '.' # ',' is also official (less common): NOM-008-SCFI-2002 DECIMAL_SEPARATOR = '.' # ',' is also official (less common): NOM-008-SCFI-2002
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -19,5 +20,5 @@ SHORT_DATE_FORMAT = 'd.m.Y'
# TIME_INPUT_FORMATS = # TIME_INPUT_FORMATS =
# DATETIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS =
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space THOUSAND_SEPARATOR = ' ' # Non-breaking space
# NUMBER_GROUPING = # NUMBER_GROUPING =

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,12 +1,13 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = 'j. E Y' DATE_FORMAT = 'j. E Y'
TIME_FORMAT = 'G.i.s' TIME_FORMAT = 'G.i.s'
# DATETIME_FORMAT = DATETIME_FORMAT = r'j. E Y \k\e\l\l\o G.i.s'
YEAR_MONTH_FORMAT = 'F Y' YEAR_MONTH_FORMAT = 'F Y'
MONTH_DAY_FORMAT = 'j. F' MONTH_DAY_FORMAT = 'j. F'
SHORT_DATE_FORMAT = 'j.n.Y' SHORT_DATE_FORMAT = 'j.n.Y'
@ -19,5 +20,5 @@ SHORT_DATE_FORMAT = 'j.n.Y'
# TIME_INPUT_FORMATS = # TIME_INPUT_FORMATS =
# DATETIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS =
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space THOUSAND_SEPARATOR = ' ' # Non-breaking space
# NUMBER_GROUPING = # NUMBER_GROUPING =

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -37,5 +38,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25' '%Y-%m-%d', # '2006-10-25'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,17 +1,18 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = 'd F Y' DATE_FORMAT = r'j \d\e F \d\e Y'
TIME_FORMAT = 'H:i:s' TIME_FORMAT = 'H:i:s'
# DATETIME_FORMAT = DATETIME_FORMAT = r'j \d\e F \d\e Y \á\s H:i'
YEAR_MONTH_FORMAT = 'F Y' YEAR_MONTH_FORMAT = r'F \d\e Y'
MONTH_DAY_FORMAT = 'j F' MONTH_DAY_FORMAT = r'j \d\e F'
SHORT_DATE_FORMAT = 'j M, Y' SHORT_DATE_FORMAT = 'd-m-Y'
# SHORT_DATETIME_FORMAT = SHORT_DATETIME_FORMAT = 'd-m-Y, H:i'
# FIRST_DAY_OF_WEEK = FIRST_DAY_OF_WEEK = 1 # Monday
# The *_INPUT_FORMATS strings use the Python strftime format syntax, # The *_INPUT_FORMATS strings use the Python strftime format syntax,
# see http://docs.python.org/library/datetime.html#strftime-strptime-behavior # see http://docs.python.org/library/datetime.html#strftime-strptime-behavior

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -28,5 +29,5 @@ DATETIME_INPUT_FORMATS = (
'%Y.%m.%d.', # '2006.10.25.' '%Y.%m.%d.', # '2006.10.25.'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space THOUSAND_SEPARATOR = ' ' # Non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -37,5 +38,5 @@ DATETIME_INPUT_FORMATS = (
'%d.%m.%y', # '25.10.06' '%d.%m.%y', # '25.10.06'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space THOUSAND_SEPARATOR = ' ' # Non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -39,5 +40,5 @@ DATETIME_INPUT_FORMATS = (
'%d.%m.%y', # '25.10.06' '%d.%m.%y', # '25.10.06'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -39,5 +40,5 @@ DATETIME_INPUT_FORMATS = (
'%d.%m.%y', # '25.10.06' '%d.%m.%y', # '25.10.06'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -33,5 +34,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25' '%Y-%m-%d', # '2006-10-25'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' THOUSAND_SEPARATOR = ' '
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -36,5 +37,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25' '%Y-%m-%d', # '2006-10-25'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -33,5 +34,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25' '%Y-%m-%d', # '2006-10-25'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -36,5 +37,5 @@ DATETIME_INPUT_FORMATS = (
'%m/%d/%y', # '10/25/06' '%m/%d/%y', # '10/25/06'
) )
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3 NUMBER_GROUPING = 3

View File

@ -2,6 +2,8 @@
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = 'j E Y р.' DATE_FORMAT = 'j E Y р.'
@ -19,5 +21,5 @@ SHORT_DATE_FORMAT = 'j M Y'
# TIME_INPUT_FORMATS = # TIME_INPUT_FORMATS =
# DATETIME_INPUT_FORMATS = # DATETIME_INPUT_FORMATS =
DECIMAL_SEPARATOR = ',' DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' THOUSAND_SEPARATOR = ' '
# NUMBER_GROUPING = # NUMBER_GROUPING =

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# This file is distributed under the same license as the Django package. # This file is distributed under the same license as the Django package.
# #
from __future__ import unicode_literals
# The *_FORMAT strings use the Django date format syntax, # The *_FORMAT strings use the Django date format syntax,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -44,22 +44,22 @@ USE_L10N = True
USE_TZ = True USE_TZ = True
# Absolute filesystem path to the directory that will hold user-uploaded files. # Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/home/media/media.lawrence.com/media/" # Example: "/var/www/example.com/media/"
MEDIA_ROOT = '' MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a # URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash. # trailing slash.
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" # Examples: "http://example.com/media/", "http://media.example.com/"
MEDIA_URL = '' MEDIA_URL = ''
# Absolute path to the directory static files should be collected to. # Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files # Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS. # in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/home/media/media.lawrence.com/static/" # Example: "/var/www/example.com/static/"
STATIC_ROOT = '' STATIC_ROOT = ''
# URL prefix for static files. # URL prefix for static files.
# Example: "http://media.lawrence.com/static/" # Example: "http://example.com/static/", "http://static.example.com/"
STATIC_URL = '/static/' STATIC_URL = '/static/'
# Additional locations of static files # Additional locations of static files

View File

@ -2,6 +2,7 @@ from django.core.urlresolvers import (RegexURLPattern,
RegexURLResolver, LocaleRegexURLResolver) RegexURLResolver, LocaleRegexURLResolver)
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.importlib import import_module from django.utils.importlib import import_module
from django.utils import six
__all__ = ['handler403', 'handler404', 'handler500', 'include', 'patterns', 'url'] __all__ = ['handler403', 'handler404', 'handler500', 'include', 'patterns', 'url']
@ -20,7 +21,7 @@ def include(arg, namespace=None, app_name=None):
# No namespace hint - use manually provided namespace # No namespace hint - use manually provided namespace
urlconf_module = arg urlconf_module = arg
if isinstance(urlconf_module, basestring): if isinstance(urlconf_module, six.string_types):
urlconf_module = import_module(urlconf_module) urlconf_module = import_module(urlconf_module)
patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module) patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
@ -52,7 +53,7 @@ def url(regex, view, kwargs=None, name=None, prefix=''):
urlconf_module, app_name, namespace = view urlconf_module, app_name, namespace = view
return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace) return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace)
else: else:
if isinstance(view, basestring): if isinstance(view, six.string_types):
if not view: if not view:
raise ImproperlyConfigured('Empty URL pattern view name not permitted (for pattern %r)' % regex) raise ImproperlyConfigured('Empty URL pattern view name not permitted (for pattern %r)' % regex)
if prefix: if prefix:

View File

@ -1,5 +1,5 @@
from django.conf import settings from django.conf import settings
from django.conf.urls import patterns from django.conf.urls import patterns, url
from django.core.urlresolvers import LocaleRegexURLResolver from django.core.urlresolvers import LocaleRegexURLResolver
def i18n_patterns(prefix, *args): def i18n_patterns(prefix, *args):
@ -16,5 +16,5 @@ def i18n_patterns(prefix, *args):
urlpatterns = patterns('', urlpatterns = patterns('',
(r'^setlang/$', 'django.views.i18n.set_language'), url(r'^setlang/$', 'django.views.i18n.set_language', name='set_language'),
) )

View File

@ -7,7 +7,7 @@ from django.contrib.admin import helpers
from django.contrib.admin.util import get_deleted_objects, model_ngettext from django.contrib.admin.util import get_deleted_objects, model_ngettext
from django.db import router from django.db import router
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.encoding import force_unicode from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy, ugettext as _ from django.utils.translation import ugettext_lazy, ugettext as _
def delete_selected(modeladmin, request, queryset): def delete_selected(modeladmin, request, queryset):
@ -42,7 +42,7 @@ def delete_selected(modeladmin, request, queryset):
n = queryset.count() n = queryset.count()
if n: if n:
for obj in queryset: for obj in queryset:
obj_display = force_unicode(obj) obj_display = force_text(obj)
modeladmin.log_deletion(request, obj, obj_display) modeladmin.log_deletion(request, obj, obj_display)
queryset.delete() queryset.delete()
modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % { modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % {
@ -52,9 +52,9 @@ def delete_selected(modeladmin, request, queryset):
return None return None
if len(queryset) == 1: if len(queryset) == 1:
objects_name = force_unicode(opts.verbose_name) objects_name = force_text(opts.verbose_name)
else: else:
objects_name = force_unicode(opts.verbose_name_plural) objects_name = force_text(opts.verbose_name_plural)
if perms_needed or protected: if perms_needed or protected:
title = _("Cannot delete %(name)s") % {"name": objects_name} title = _("Cannot delete %(name)s") % {"name": objects_name}

View File

@ -9,7 +9,7 @@ import datetime
from django.db import models from django.db import models
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone from django.utils import timezone
@ -195,7 +195,7 @@ class RelatedFieldListFilter(FieldListFilter):
} }
for pk_val, val in self.lookup_choices: for pk_val, val in self.lookup_choices:
yield { yield {
'selected': self.lookup_val == smart_unicode(pk_val), 'selected': self.lookup_val == smart_text(pk_val),
'query_string': cl.get_query_string({ 'query_string': cl.get_query_string({
self.lookup_kwarg: pk_val, self.lookup_kwarg: pk_val,
}, [self.lookup_kwarg_isnull]), }, [self.lookup_kwarg_isnull]),
@ -272,7 +272,7 @@ class ChoicesFieldListFilter(FieldListFilter):
} }
for lookup, title in self.field.flatchoices: for lookup, title in self.field.flatchoices:
yield { yield {
'selected': smart_unicode(lookup) == self.lookup_val, 'selected': smart_text(lookup) == self.lookup_val,
'query_string': cl.get_query_string({ 'query_string': cl.get_query_string({
self.lookup_kwarg: lookup}), self.lookup_kwarg: lookup}),
'display': title, 'display': title,
@ -381,7 +381,7 @@ class AllValuesFieldListFilter(FieldListFilter):
if val is None: if val is None:
include_none = True include_none = True
continue continue
val = smart_unicode(val) val = smart_text(val)
yield { yield {
'selected': self.lookup_val == val, 'selected': self.lookup_val == val,
'query_string': cl.get_query_string({ 'query_string': cl.get_query_string({

View File

@ -1,9 +1,10 @@
from __future__ import unicode_literals
from django import forms from django import forms
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy, ugettext as _ from django.utils.translation import ugettext_lazy, ugettext as _
ERROR_MESSAGE = ugettext_lazy("Please enter the correct username and password " ERROR_MESSAGE = ugettext_lazy("Please enter the correct username and password "
@ -26,7 +27,7 @@ class AdminAuthenticationForm(AuthenticationForm):
if username and password: if username and password:
self.user_cache = authenticate(username=username, password=password) self.user_cache = authenticate(username=username, password=password)
if self.user_cache is None: if self.user_cache is None:
if u'@' in username: if '@' in username:
# Mistakenly entered e-mail address instead of username? Look it up. # Mistakenly entered e-mail address instead of username? Look it up.
try: try:
user = User.objects.get(email=username) user = User.objects.get(email=username)

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from django import forms from django import forms
from django.contrib.admin.util import (flatten_fieldsets, lookup_field, from django.contrib.admin.util import (flatten_fieldsets, lookup_field,
display_for_field, label_for_field, help_text_for_field) display_for_field, label_for_field, help_text_for_field)
@ -7,9 +9,10 @@ from django.core.exceptions import ObjectDoesNotExist
from django.db.models.fields.related import ManyToManyRel from django.db.models.fields.related import ManyToManyRel
from django.forms.util import flatatt from django.forms.util import flatatt
from django.template.defaultfilters import capfirst from django.template.defaultfilters import capfirst
from django.utils.encoding import force_unicode, smart_unicode from django.utils.encoding import force_text, smart_text
from django.utils.html import escape, conditional_escape from django.utils.html import conditional_escape, format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils import six
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
@ -47,7 +50,7 @@ class AdminForm(object):
try: try:
fieldset_name, fieldset_options = self.fieldsets[0] fieldset_name, fieldset_options = self.fieldsets[0]
field_name = fieldset_options['fields'][0] field_name = fieldset_options['fields'][0]
if not isinstance(field_name, basestring): if not isinstance(field_name, six.string_types):
field_name = field_name[0] field_name = field_name[0]
return self.form[field_name] return self.form[field_name]
except (KeyError, IndexError): except (KeyError, IndexError):
@ -69,7 +72,7 @@ class Fieldset(object):
description=None, model_admin=None): description=None, model_admin=None):
self.form = form self.form = form
self.name, self.fields = name, fields self.name, self.fields = name, fields
self.classes = u' '.join(classes) self.classes = ' '.join(classes)
self.description = description self.description = description
self.model_admin = model_admin self.model_admin = model_admin
self.readonly_fields = readonly_fields self.readonly_fields = readonly_fields
@ -91,7 +94,7 @@ class Fieldset(object):
class Fieldline(object): class Fieldline(object):
def __init__(self, form, field, readonly_fields=None, model_admin=None): def __init__(self, form, field, readonly_fields=None, model_admin=None):
self.form = form # A django.forms.Form instance self.form = form # A django.forms.Form instance
if not hasattr(field, "__iter__"): if not hasattr(field, "__iter__") or isinstance(field, six.text_type):
self.fields = [field] self.fields = [field]
else: else:
self.fields = field self.fields = field
@ -109,7 +112,7 @@ class Fieldline(object):
yield AdminField(self.form, field, is_first=(i == 0)) yield AdminField(self.form, field, is_first=(i == 0))
def errors(self): def errors(self):
return mark_safe(u'\n'.join([self.form[f].errors.as_ul() for f in self.fields if f not in self.readonly_fields]).strip('\n')) return mark_safe('\n'.join([self.form[f].errors.as_ul() for f in self.fields if f not in self.readonly_fields]).strip('\n'))
class AdminField(object): class AdminField(object):
def __init__(self, form, field, is_first): def __init__(self, form, field, is_first):
@ -119,16 +122,16 @@ class AdminField(object):
def label_tag(self): def label_tag(self):
classes = [] classes = []
contents = conditional_escape(force_unicode(self.field.label)) contents = conditional_escape(force_text(self.field.label))
if self.is_checkbox: if self.is_checkbox:
classes.append(u'vCheckboxLabel') classes.append('vCheckboxLabel')
else: else:
contents += u':' contents += ':'
if self.field.field.required: if self.field.field.required:
classes.append(u'required') classes.append('required')
if not self.is_first: if not self.is_first:
classes.append(u'inline') classes.append('inline')
attrs = classes and {'class': u' '.join(classes)} or {} attrs = classes and {'class': ' '.join(classes)} or {}
return self.field.label_tag(contents=mark_safe(contents), attrs=attrs) return self.field.label_tag(contents=mark_safe(contents), attrs=attrs)
def errors(self): def errors(self):
@ -161,11 +164,9 @@ class AdminReadonlyField(object):
if not self.is_first: if not self.is_first:
attrs["class"] = "inline" attrs["class"] = "inline"
label = self.field['label'] label = self.field['label']
contents = capfirst(force_unicode(escape(label))) + u":" return format_html('<label{0}>{1}:</label>',
return mark_safe('<label%(attrs)s>%(contents)s</label>' % { flatatt(attrs),
"attrs": flatatt(attrs), capfirst(force_text(label)))
"contents": contents,
})
def contents(self): def contents(self):
from django.contrib.admin.templatetags.admin_list import _boolean_icon from django.contrib.admin.templatetags.admin_list import _boolean_icon
@ -181,14 +182,14 @@ class AdminReadonlyField(object):
if boolean: if boolean:
result_repr = _boolean_icon(value) result_repr = _boolean_icon(value)
else: else:
result_repr = smart_unicode(value) result_repr = smart_text(value)
if getattr(attr, "allow_tags", False): if getattr(attr, "allow_tags", False):
result_repr = mark_safe(result_repr) result_repr = mark_safe(result_repr)
else: else:
if value is None: if value is None:
result_repr = EMPTY_CHANGELIST_VALUE result_repr = EMPTY_CHANGELIST_VALUE
elif isinstance(f.rel, ManyToManyRel): elif isinstance(f.rel, ManyToManyRel):
result_repr = ", ".join(map(unicode, value.all())) result_repr = ", ".join(map(six.text_type, value.all()))
else: else:
result_repr = display_for_field(value, f) result_repr = display_for_field(value, f)
return conditional_escape(result_repr) return conditional_escape(result_repr)
@ -324,11 +325,11 @@ class AdminErrorList(forms.util.ErrorList):
""" """
def __init__(self, form, inline_formsets): def __init__(self, form, inline_formsets):
if form.is_bound: if form.is_bound:
self.extend(form.errors.values()) self.extend(list(six.itervalues(form.errors)))
for inline_formset in inline_formsets: for inline_formset in inline_formsets:
self.extend(inline_formset.non_form_errors()) self.extend(inline_formset.non_form_errors())
for errors_in_inline_form in inline_formset.errors: for errors_in_inline_form in inline_formset.errors:
self.extend(errors_in_inline_form.values()) self.extend(list(six.itervalues(errors_in_inline_form)))
def normalize_fieldsets(fieldsets): def normalize_fieldsets(fieldsets):
""" """

View File

@ -1,10 +1,12 @@
from __future__ import unicode_literals
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.util import quote from django.contrib.admin.util import quote
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_text
from django.utils.safestring import mark_safe from django.utils.encoding import python_2_unicode_compatible
ADDITION = 1 ADDITION = 1
CHANGE = 2 CHANGE = 2
@ -13,10 +15,11 @@ DELETION = 3
class LogEntryManager(models.Manager): class LogEntryManager(models.Manager):
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''): def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
e = self.model(None, None, user_id, content_type_id, smart_unicode(object_id), object_repr[:200], action_flag, change_message) e = self.model(None, None, user_id, content_type_id, smart_text(object_id), object_repr[:200], action_flag, change_message)
e.save() e.save()
@python_2_unicode_compatible
class LogEntry(models.Model): class LogEntry(models.Model):
action_time = models.DateTimeField(_('action time'), auto_now=True) action_time = models.DateTimeField(_('action time'), auto_now=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL) user = models.ForeignKey(settings.AUTH_USER_MODEL)
@ -35,9 +38,9 @@ class LogEntry(models.Model):
ordering = ('-action_time',) ordering = ('-action_time',)
def __repr__(self): def __repr__(self):
return smart_unicode(self.action_time) return smart_text(self.action_time)
def __unicode__(self): def __str__(self):
if self.action_flag == ADDITION: if self.action_flag == ADDITION:
return _('Added "%(object)s".') % {'object': self.object_repr} return _('Added "%(object)s".') % {'object': self.object_repr}
elif self.action_flag == CHANGE: elif self.action_flag == CHANGE:
@ -66,5 +69,5 @@ class LogEntry(models.Model):
This is relative to the Django admin index page. This is relative to the Django admin index page.
""" """
if self.content_type and self.object_id: if self.content_type and self.object_id:
return mark_safe(u"%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, quote(self.object_id))) return "%s/%s/%s/" % (self.content_type.app_label, self.content_type.model, quote(self.object_id))
return None return None

View File

@ -24,10 +24,11 @@ from django.utils.decorators import method_decorator
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.html import escape, escapejs from django.utils.html import escape, escapejs
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils import six
from django.utils.text import capfirst, get_text_list from django.utils.text import capfirst, get_text_list
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ungettext from django.utils.translation import ungettext
from django.utils.encoding import force_unicode from django.utils.encoding import force_text
HORIZONTAL, VERTICAL = 1, 2 HORIZONTAL, VERTICAL = 1, 2
# returns the <ul> class for a given radio_admin field # returns the <ul> class for a given radio_admin field
@ -49,7 +50,7 @@ FORMFIELD_FOR_DBFIELD_DEFAULTS = {
models.TextField: {'widget': widgets.AdminTextareaWidget}, models.TextField: {'widget': widgets.AdminTextareaWidget},
models.URLField: {'widget': widgets.AdminURLFieldWidget}, models.URLField: {'widget': widgets.AdminURLFieldWidget},
models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget}, models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget},
models.BigIntegerField: {'widget': widgets.AdminIntegerFieldWidget}, models.BigIntegerField: {'widget': widgets.AdminBigIntegerFieldWidget},
models.CharField: {'widget': widgets.AdminTextInputWidget}, models.CharField: {'widget': widgets.AdminTextInputWidget},
models.ImageField: {'widget': widgets.AdminFileWidget}, models.ImageField: {'widget': widgets.AdminFileWidget},
models.FileField: {'widget': widgets.AdminFileWidget}, models.FileField: {'widget': widgets.AdminFileWidget},
@ -57,9 +58,8 @@ FORMFIELD_FOR_DBFIELD_DEFAULTS = {
csrf_protect_m = method_decorator(csrf_protect) csrf_protect_m = method_decorator(csrf_protect)
class BaseModelAdmin(object): class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
"""Functionality common to both ModelAdmin and InlineAdmin.""" """Functionality common to both ModelAdmin and InlineAdmin."""
__metaclass__ = forms.MediaDefiningClass
raw_id_fields = () raw_id_fields = ()
fields = None fields = None
@ -425,7 +425,7 @@ class ModelAdmin(BaseModelAdmin):
if self.declared_fieldsets: if self.declared_fieldsets:
return self.declared_fieldsets return self.declared_fieldsets
form = self.get_form(request, obj) form = self.get_form(request, obj)
fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj)) fields = list(form.base_fields) + list(self.get_readonly_fields(request, obj))
return [(None, {'fields': fields})] return [(None, {'fields': fields})]
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):
@ -520,7 +520,7 @@ class ModelAdmin(BaseModelAdmin):
user_id = request.user.pk, user_id = request.user.pk,
content_type_id = ContentType.objects.get_for_model(object).pk, content_type_id = ContentType.objects.get_for_model(object).pk,
object_id = object.pk, object_id = object.pk,
object_repr = force_unicode(object), object_repr = force_text(object),
action_flag = ADDITION action_flag = ADDITION
) )
@ -535,7 +535,7 @@ class ModelAdmin(BaseModelAdmin):
user_id = request.user.pk, user_id = request.user.pk,
content_type_id = ContentType.objects.get_for_model(object).pk, content_type_id = ContentType.objects.get_for_model(object).pk,
object_id = object.pk, object_id = object.pk,
object_repr = force_unicode(object), object_repr = force_text(object),
action_flag = CHANGE, action_flag = CHANGE,
change_message = message change_message = message
) )
@ -560,7 +560,7 @@ class ModelAdmin(BaseModelAdmin):
""" """
A list_display column containing a checkbox widget. A list_display column containing a checkbox widget.
""" """
return helpers.checkbox.render(helpers.ACTION_CHECKBOX_NAME, force_unicode(obj.pk)) return helpers.checkbox.render(helpers.ACTION_CHECKBOX_NAME, force_text(obj.pk))
action_checkbox.short_description = mark_safe('<input type="checkbox" id="action-toggle" />') action_checkbox.short_description = mark_safe('<input type="checkbox" id="action-toggle" />')
action_checkbox.allow_tags = True action_checkbox.allow_tags = True
@ -608,7 +608,7 @@ class ModelAdmin(BaseModelAdmin):
tuple (name, description). tuple (name, description).
""" """
choices = [] + default_choices choices = [] + default_choices
for func, name, description in self.get_actions(request).itervalues(): for func, name, description in six.itervalues(self.get_actions(request)):
choice = (name, description % model_format_dict(self.opts)) choice = (name, description % model_format_dict(self.opts))
choices.append(choice) choices.append(choice)
return choices return choices
@ -674,17 +674,17 @@ class ModelAdmin(BaseModelAdmin):
for formset in formsets: for formset in formsets:
for added_object in formset.new_objects: for added_object in formset.new_objects:
change_message.append(_('Added %(name)s "%(object)s".') change_message.append(_('Added %(name)s "%(object)s".')
% {'name': force_unicode(added_object._meta.verbose_name), % {'name': force_text(added_object._meta.verbose_name),
'object': force_unicode(added_object)}) 'object': force_text(added_object)})
for changed_object, changed_fields in formset.changed_objects: for changed_object, changed_fields in formset.changed_objects:
change_message.append(_('Changed %(list)s for %(name)s "%(object)s".') change_message.append(_('Changed %(list)s for %(name)s "%(object)s".')
% {'list': get_text_list(changed_fields, _('and')), % {'list': get_text_list(changed_fields, _('and')),
'name': force_unicode(changed_object._meta.verbose_name), 'name': force_text(changed_object._meta.verbose_name),
'object': force_unicode(changed_object)}) 'object': force_text(changed_object)})
for deleted_object in formset.deleted_objects: for deleted_object in formset.deleted_objects:
change_message.append(_('Deleted %(name)s "%(object)s".') change_message.append(_('Deleted %(name)s "%(object)s".')
% {'name': force_unicode(deleted_object._meta.verbose_name), % {'name': force_text(deleted_object._meta.verbose_name),
'object': force_unicode(deleted_object)}) 'object': force_text(deleted_object)})
change_message = ' '.join(change_message) change_message = ' '.join(change_message)
return change_message or _('No fields changed.') return change_message or _('No fields changed.')
@ -745,7 +745,7 @@ class ModelAdmin(BaseModelAdmin):
'has_file_field': True, # FIXME - this should check if form or formsets have a FileField, 'has_file_field': True, # FIXME - this should check if form or formsets have a FileField,
'has_absolute_url': hasattr(self.model, 'get_absolute_url'), 'has_absolute_url': hasattr(self.model, 'get_absolute_url'),
'ordered_objects': ordered_objects, 'ordered_objects': ordered_objects,
'form_url': mark_safe(form_url), 'form_url': form_url,
'opts': opts, 'opts': opts,
'content_type_id': ContentType.objects.get_for_model(self.model).id, 'content_type_id': ContentType.objects.get_for_model(self.model).id,
'save_as': self.save_as, 'save_as': self.save_as,
@ -769,7 +769,7 @@ class ModelAdmin(BaseModelAdmin):
opts = obj._meta opts = obj._meta
pk_value = obj._get_pk_val() pk_value = obj._get_pk_val()
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj)} msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': force_text(opts.verbose_name), 'obj': force_text(obj)}
# Here, we distinguish between different save types by checking for # Here, we distinguish between different save types by checking for
# the presence of keys in request.POST. # the presence of keys in request.POST.
if "_continue" in request.POST: if "_continue" in request.POST:
@ -782,10 +782,10 @@ class ModelAdmin(BaseModelAdmin):
return HttpResponse( return HttpResponse(
'<!DOCTYPE html><html><head><title></title></head><body>' '<!DOCTYPE html><html><head><title></title></head><body>'
'<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script></body></html>' % \ '<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script></body></html>' % \
# escape() calls force_unicode. # escape() calls force_text.
(escape(pk_value), escapejs(obj))) (escape(pk_value), escapejs(obj)))
elif "_addanother" in request.POST: elif "_addanother" in request.POST:
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_text(opts.verbose_name)))
return HttpResponseRedirect(request.path) return HttpResponseRedirect(request.path)
else: else:
self.message_user(request, msg) self.message_user(request, msg)
@ -819,7 +819,7 @@ class ModelAdmin(BaseModelAdmin):
pk_value = obj._get_pk_val() pk_value = obj._get_pk_val()
msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_unicode(verbose_name), 'obj': force_unicode(obj)} msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': force_text(verbose_name), 'obj': force_text(obj)}
if "_continue" in request.POST: if "_continue" in request.POST:
self.message_user(request, msg + ' ' + _("You may edit it again below.")) self.message_user(request, msg + ' ' + _("You may edit it again below."))
if "_popup" in request.REQUEST: if "_popup" in request.REQUEST:
@ -827,14 +827,14 @@ class ModelAdmin(BaseModelAdmin):
else: else:
return HttpResponseRedirect(request.path) return HttpResponseRedirect(request.path)
elif "_saveasnew" in request.POST: elif "_saveasnew" in request.POST:
msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(verbose_name), 'obj': obj} msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_text(verbose_name), 'obj': obj}
self.message_user(request, msg) self.message_user(request, msg)
return HttpResponseRedirect(reverse('admin:%s_%s_change' % return HttpResponseRedirect(reverse('admin:%s_%s_change' %
(opts.app_label, module_name), (opts.app_label, module_name),
args=(pk_value,), args=(pk_value,),
current_app=self.admin_site.name)) current_app=self.admin_site.name))
elif "_addanother" in request.POST: elif "_addanother" in request.POST:
self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(verbose_name))) self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_text(verbose_name)))
return HttpResponseRedirect(reverse('admin:%s_%s_add' % return HttpResponseRedirect(reverse('admin:%s_%s_add' %
(opts.app_label, module_name), (opts.app_label, module_name),
current_app=self.admin_site.name)) current_app=self.admin_site.name))
@ -995,10 +995,9 @@ class ModelAdmin(BaseModelAdmin):
media = media + inline_admin_formset.media media = media + inline_admin_formset.media
context = { context = {
'title': _('Add %s') % force_unicode(opts.verbose_name), 'title': _('Add %s') % force_text(opts.verbose_name),
'adminform': adminForm, 'adminform': adminForm,
'is_popup': "_popup" in request.REQUEST, 'is_popup': "_popup" in request.REQUEST,
'show_delete': False,
'media': media, 'media': media,
'inline_admin_formsets': inline_admin_formsets, 'inline_admin_formsets': inline_admin_formsets,
'errors': helpers.AdminErrorList(form, formsets), 'errors': helpers.AdminErrorList(form, formsets),
@ -1020,7 +1019,7 @@ class ModelAdmin(BaseModelAdmin):
raise PermissionDenied raise PermissionDenied
if obj is None: if obj is None:
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)}) raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_text(opts.verbose_name), 'key': escape(object_id)})
if request.method == 'POST' and "_saveasnew" in request.POST: if request.method == 'POST' and "_saveasnew" in request.POST:
return self.add_view(request, form_url=reverse('admin:%s_%s_add' % return self.add_view(request, form_url=reverse('admin:%s_%s_add' %
@ -1086,7 +1085,7 @@ class ModelAdmin(BaseModelAdmin):
media = media + inline_admin_formset.media media = media + inline_admin_formset.media
context = { context = {
'title': _('Change %s') % force_unicode(opts.verbose_name), 'title': _('Change %s') % force_text(opts.verbose_name),
'adminform': adminForm, 'adminform': adminForm,
'object_id': object_id, 'object_id': object_id,
'original': obj, 'original': obj,
@ -1195,14 +1194,14 @@ class ModelAdmin(BaseModelAdmin):
if changecount: if changecount:
if changecount == 1: if changecount == 1:
name = force_unicode(opts.verbose_name) name = force_text(opts.verbose_name)
else: else:
name = force_unicode(opts.verbose_name_plural) name = force_text(opts.verbose_name_plural)
msg = ungettext("%(count)s %(name)s was changed successfully.", msg = ungettext("%(count)s %(name)s was changed successfully.",
"%(count)s %(name)s were changed successfully.", "%(count)s %(name)s were changed successfully.",
changecount) % {'count': changecount, changecount) % {'count': changecount,
'name': name, 'name': name,
'obj': force_unicode(obj)} 'obj': force_text(obj)}
self.message_user(request, msg) self.message_user(request, msg)
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
@ -1229,7 +1228,7 @@ class ModelAdmin(BaseModelAdmin):
'All %(total_count)s selected', cl.result_count) 'All %(total_count)s selected', cl.result_count)
context = { context = {
'module_name': force_unicode(opts.verbose_name_plural), 'module_name': force_text(opts.verbose_name_plural),
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)}, 'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
'selection_note_all': selection_note_all % {'total_count': cl.result_count}, 'selection_note_all': selection_note_all % {'total_count': cl.result_count},
'title': cl.title, 'title': cl.title,
@ -1264,7 +1263,7 @@ class ModelAdmin(BaseModelAdmin):
raise PermissionDenied raise PermissionDenied
if obj is None: if obj is None:
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)}) raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_text(opts.verbose_name), 'key': escape(object_id)})
using = router.db_for_write(self.model) using = router.db_for_write(self.model)
@ -1276,11 +1275,11 @@ class ModelAdmin(BaseModelAdmin):
if request.POST: # The user has already confirmed the deletion. if request.POST: # The user has already confirmed the deletion.
if perms_needed: if perms_needed:
raise PermissionDenied raise PermissionDenied
obj_display = force_unicode(obj) obj_display = force_text(obj)
self.log_deletion(request, obj, obj_display) self.log_deletion(request, obj, obj_display)
self.delete_model(request, obj) self.delete_model(request, obj)
self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)}) self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_text(opts.verbose_name), 'obj': force_text(obj_display)})
if not self.has_change_permission(request, None): if not self.has_change_permission(request, None):
return HttpResponseRedirect(reverse('admin:index', return HttpResponseRedirect(reverse('admin:index',
@ -1289,7 +1288,7 @@ class ModelAdmin(BaseModelAdmin):
(opts.app_label, opts.module_name), (opts.app_label, opts.module_name),
current_app=self.admin_site.name)) current_app=self.admin_site.name))
object_name = force_unicode(opts.verbose_name) object_name = force_text(opts.verbose_name)
if perms_needed or protected: if perms_needed or protected:
title = _("Cannot delete %(name)s") % {"name": object_name} title = _("Cannot delete %(name)s") % {"name": object_name}
@ -1321,15 +1320,15 @@ class ModelAdmin(BaseModelAdmin):
opts = model._meta opts = model._meta
app_label = opts.app_label app_label = opts.app_label
action_list = LogEntry.objects.filter( action_list = LogEntry.objects.filter(
object_id = object_id, object_id = unquote(object_id),
content_type__id__exact = ContentType.objects.get_for_model(model).id content_type__id__exact = ContentType.objects.get_for_model(model).id
).select_related().order_by('action_time') ).select_related().order_by('action_time')
# If no history was found, see whether this object even exists. # If no history was found, see whether this object even exists.
obj = get_object_or_404(model, pk=unquote(object_id)) obj = get_object_or_404(model, pk=unquote(object_id))
context = { context = {
'title': _('Change history: %s') % force_unicode(obj), 'title': _('Change history: %s') % force_text(obj),
'action_list': action_list, 'action_list': action_list,
'module_name': capfirst(force_unicode(opts.verbose_name_plural)), 'module_name': capfirst(force_text(opts.verbose_name_plural)),
'object': obj, 'object': obj,
'app_label': app_label, 'app_label': app_label,
'opts': opts, 'opts': opts,
@ -1416,7 +1415,7 @@ class InlineModelAdmin(BaseModelAdmin):
if self.declared_fieldsets: if self.declared_fieldsets:
return self.declared_fieldsets return self.declared_fieldsets
form = self.get_formset(request, obj).form form = self.get_formset(request, obj).form
fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj)) fields = list(form.base_fields) + list(self.get_readonly_fields(request, obj))
return [(None, {'fields': fields})] return [(None, {'fields': fields})]
def queryset(self, request): def queryset(self, request):

View File

@ -10,6 +10,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse, NoReverseMatch
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils import six
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.cache import never_cache from django.views.decorators.cache import never_cache
@ -136,7 +137,7 @@ class AdminSite(object):
""" """
Get all the enabled actions as an iterable of (name, func). Get all the enabled actions as an iterable of (name, func).
""" """
return self._actions.iteritems() return six.iteritems(self._actions)
def has_permission(self, request): def has_permission(self, request):
""" """
@ -234,14 +235,15 @@ class AdminSite(object):
wrap(self.i18n_javascript, cacheable=True), wrap(self.i18n_javascript, cacheable=True),
name='jsi18n'), name='jsi18n'),
url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$', url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
wrap(contenttype_views.shortcut)), wrap(contenttype_views.shortcut),
name='view_on_site'),
url(r'^(?P<app_label>\w+)/$', url(r'^(?P<app_label>\w+)/$',
wrap(self.app_index), wrap(self.app_index),
name='app_list') name='app_list')
) )
# Add in each model's views. # Add in each model's views.
for model, model_admin in self._registry.iteritems(): for model, model_admin in six.iteritems(self._registry):
urlpatterns += patterns('', urlpatterns += patterns('',
url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name), url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
include(model_admin.urls)) include(model_admin.urls))
@ -373,7 +375,7 @@ class AdminSite(object):
} }
# Sort the apps alphabetically. # Sort the apps alphabetically.
app_list = app_dict.values() app_list = list(six.itervalues(app_dict))
app_list.sort(key=lambda x: x['name']) app_list.sort(key=lambda x: x['name'])
# Sort the models alphabetically within each app. # Sort the models alphabetically within each app.

View File

@ -226,6 +226,10 @@ body.popup .submit-row {
width: 5em; width: 5em;
} }
.vBigIntegerField {
width: 10em;
}
.vForeignKeyRawIdAdminField { .vForeignKeyRawIdAdminField {
width: 5em; width: 5em;
} }

View File

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

View File

@ -1,6 +1,6 @@
(function(a){a.fn.actions=function(m){var b=a.extend({},a.fn.actions.defaults,m),f=a(this),e=!1,j=function(c){c?h():i();a(f).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)},g=function(){var c=a(f).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},!0));a(b.allToggle).attr("checked",function(){c==f.length?(value=!0,h()):(value=!1,k());return value})},h=function(){a(b.acrossClears).hide(); (function(a){a.fn.actions=function(n){var b=a.extend({},a.fn.actions.defaults,n),e=a(this),g=false,k=function(c){c?i():j();a(e).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)},f=function(){var c=a(e).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},true));a(b.allToggle).attr("checked",function(){if(c==e.length){value=true;i()}else{value=false;l()}return value})},i=
a(b.acrossQuestions).show();a(b.allContainer).hide()},l=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},i=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},k=function(){i();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show();a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass); function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()},m=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},j=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},l=function(){j();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show();
g();1==a(b.acrossInput).val()&&l()});a(b.allToggle).show().click(function(){j(a(this).attr("checked"));g()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);l()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",!1);k();j(0);g()});lastChecked=null;a(f).click(function(c){c||(c=window.event);var d=c.target?c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&!0===c.shiftKey){var e=!1;a(lastChecked).attr("checked", a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);f();a(b.acrossInput).val()==1&&m()});a(b.allToggle).show().click(function(){k(a(this).attr("checked"));f()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);m()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",false);l();k(0);f()});lastChecked=null;a(e).click(function(c){if(!c)c=window.event;var d=c.target?
d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(f).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))e=e?false:true;e&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);lastChecked=d;g()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){e=!0});a('form#changelist-form button[name="index"]').click(function(){if(e)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))}); c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&c.shiftKey===true){var h=false;a(lastChecked).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(e).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))h=h?false:true;h&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);lastChecked=d;f()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){g=
a('form#changelist-form input[name="_save"]').click(function(){var b=!1;a("div.actions select option:selected").each(function(){a(this).val()&&(b=!0)});if(b)return e?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})}; true});a('form#changelist-form button[name="index"]').click(function(){if(g)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').click(function(){var c=false;a("div.actions select option:selected").each(function(){if(a(this).val())c=true});if(c)return g?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):
a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery); confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(django.jQuery);

View File

@ -1,2 +1,2 @@
(function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){0==a(b).find("div.errors").length&&a(b).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser'+c+'" class="collapse-toggle" href="#">'+gettext("Show")+"</a>)")});a("fieldset.collapse a.collapse-toggle").toggle(function(){a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]);return!1},function(){a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", (function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){a(b).find("div.errors").length==0&&a(b).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser'+c+'" class="collapse-toggle" href="#">'+gettext("Show")+"</a>)")});a("fieldset.collapse a.collapse-toggle").toggle(function(){a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]);return false},function(){a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset",
[a(this).attr("id")]);return!1})})})(django.jQuery); [a(this).attr("id")]);return false})})})(django.jQuery);

View File

@ -44,7 +44,7 @@
if ($(this).attr("tagName") == "TR") { if ($(this).attr("tagName") == "TR") {
// If forms are laid out as table rows, insert the // If forms are laid out as table rows, insert the
// "add" button in a new table row: // "add" button in a new table row:
var numCols = this.eq(0).children().length; var numCols = this.eq(-1).children().length;
$(this).parent().append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>"); $(this).parent().append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
addButton = $(this).parent().find("tr:last a"); addButton = $(this).parent().find("tr:last a");
} else { } else {

View File

@ -1,5 +1,5 @@
(function(b){b.fn.formset=function(c){var a=b.extend({},b.fn.formset.defaults,c),j=function(a,f,d){var e=RegExp("("+f+"-(\\d+|__prefix__))"),f=f+"-"+d;b(a).attr("for")&&b(a).attr("for",b(a).attr("for").replace(e,f));a.id&&(a.id=a.id.replace(e,f));a.name&&(a.name=a.name.replace(e,f))},c=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off"),h=parseInt(c.val(),10),g=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off"),c=""===g.val()||0<g.val()-c.val();b(this).each(function(){b(this).not("."+ (function(b){b.fn.formset=function(g){var a=b.extend({},b.fn.formset.defaults,g),k=function(c,f,e){var d=RegExp("("+f+"-(\\d+|__prefix__))");f=f+"-"+e;b(c).attr("for")&&b(c).attr("for",b(c).attr("for").replace(d,f));if(c.id)c.id=c.id.replace(d,f);if(c.name)c.name=c.name.replace(d,f)};g=b("#id_"+a.prefix+"-TOTAL_FORMS").attr("autocomplete","off");var l=parseInt(g.val(),10),h=b("#id_"+a.prefix+"-MAX_NUM_FORMS").attr("autocomplete","off");g=h.val()===""||h.val()-g.val()>0;b(this).each(function(){b(this).not("."+
a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&c){var i;"TR"==b(this).attr("tagName")?(c=this.eq(0).children().length,b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+c+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>"),i=b(this).parent().find("tr:last a")):(b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>"),i=b(this).filter(":last").next().find("a"));i.click(function(c){c.preventDefault(); a.emptyCssClass).addClass(a.formCssClass)});if(b(this).length&&g){var j;if(b(this).attr("tagName")=="TR"){g=this.eq(-1).children().length;b(this).parent().append('<tr class="'+a.addCssClass+'"><td colspan="'+g+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>");j=b(this).parent().find("tr:last a")}else{b(this).filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>");j=b(this).filter(":last").next().find("a")}j.click(function(c){c.preventDefault();
var f=b("#id_"+a.prefix+"-TOTAL_FORMS"),c=b("#"+a.prefix+"-empty"),d=c.clone(true);d.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+h);d.is("tr")?d.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>"):d.is("ul")||d.is("ol")?d.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):d.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+ var f=b("#id_"+a.prefix+"-TOTAL_FORMS");c=b("#"+a.prefix+"-empty");var e=c.clone(true);e.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);if(e.is("tr"))e.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>");else e.is("ul")||e.is("ol")?e.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):e.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+
a.deleteText+"</a></span>");d.find("*").each(function(){j(this,a.prefix,f.val())});d.insertBefore(b(c));b(f).val(parseInt(f.val(),10)+1);h=h+1;g.val()!==""&&g.val()-f.val()<=0&&i.parent().hide();d.find("a."+a.deleteCssClass).click(function(e){e.preventDefault();e=b(this).parents("."+a.formCssClass);e.remove();h=h-1;a.removed&&a.removed(e);e=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(e.length);(g.val()===""||g.val()-e.length>0)&&i.parent().show();for(var c=0,d=e.length;c<d;c++){j(b(e).get(c), a.deleteText+"</a></span>");e.find("*").each(function(){k(this,a.prefix,f.val())});e.insertBefore(b(c));b(f).val(parseInt(f.val(),10)+1);l+=1;h.val()!==""&&h.val()-f.val()<=0&&j.parent().hide();e.find("a."+a.deleteCssClass).click(function(d){d.preventDefault();d=b(this).parents("."+a.formCssClass);d.remove();l-=1;a.removed&&a.removed(d);d=b("."+a.formCssClass);b("#id_"+a.prefix+"-TOTAL_FORMS").val(d.length);if(h.val()===""||h.val()-d.length>0)j.parent().show();for(var i=0,m=d.length;i<m;i++){k(b(d).get(i),
a.prefix,c);b(e.get(c)).find("*").each(function(){j(this,a.prefix,c)})}});a.added&&a.added(d)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery); a.prefix,i);b(d.get(i)).find("*").each(function(){k(this,a.prefix,i)})}});a.added&&a.added(e)})}return this};b.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null}})(django.jQuery);

View File

@ -1 +1 @@
(function(a){a.fn.prepopulate=function(d,e){return this.each(function(){var b=a(this);b.data("_changed",!1);b.change(function(){b.data("_changed",!0)});var c=function(){if(!0!=b.data("_changed")){var c=[];a.each(d,function(b,d){0<a(d).val().length&&c.push(a(d).val())});b.val(URLify(c.join(" "),e))}};a(d.join(",")).keyup(c).change(c).focus(c)})}})(django.jQuery); (function(a){a.fn.prepopulate=function(d,g){return this.each(function(){var b=a(this);b.data("_changed",false);b.change(function(){b.data("_changed",true)});var c=function(){if(b.data("_changed")!=true){var e=[];a.each(d,function(h,f){a(f).val().length>0&&e.push(a(f).val())});b.val(URLify(e.join(" "),g))}};a(d.join(",")).keyup(c).change(c).focus(c)})}})(django.jQuery);

View File

@ -3,8 +3,7 @@
{% load admin_urls %} {% load admin_urls %}
{% block extrahead %}{{ block.super }} {% block extrahead %}{{ block.super }}
{% url 'admin:jsi18n' as jsi18nurl %} <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../../jsi18n/" }}"></script>
{% endblock %} {% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %} {% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %} {% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}

View File

@ -3,8 +3,7 @@
{% load admin_urls %} {% load admin_urls %}
{% block extrahead %}{{ block.super }} {% block extrahead %}{{ block.super }}
{% url 'admin:jsi18n' as jsi18nurl %} <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../jsi18n/" }}"></script>
{{ media }} {{ media }}
{% endblock %} {% endblock %}
@ -31,7 +30,7 @@
<ul class="object-tools"> <ul class="object-tools">
{% block object-tools-items %} {% block object-tools-items %}
<li><a href="history/" class="historylink">{% trans "History" %}</a></li> <li><a href="history/" class="historylink">{% trans "History" %}</a></li>
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%} {% if has_absolute_url %}<li><a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
{% endblock %} {% endblock %}
</ul> </ul>
{% endif %}{% endif %} {% endif %}{% endif %}
@ -65,7 +64,7 @@
{% block submit_buttons_bottom %}{% submit_row %}{% endblock %} {% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
{% if adminform and add %} {% if adminform.first_field and add %}
<script type="text/javascript">document.getElementById("{{ adminform.first_field.id_for_label }}").focus();</script> <script type="text/javascript">document.getElementById("{{ adminform.first_field.id_for_label }}").focus();</script>
{% endif %} {% endif %}

View File

@ -9,8 +9,7 @@
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" /> <link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />
{% endif %} {% endif %}
{% if cl.formset or action_form %} {% if cl.formset or action_form %}
{% url 'admin:jsi18n' as jsi18nurl %} <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
<script type="text/javascript" src="{{ jsi18nurl|default:'../../jsi18n/' }}"></script>
{% endif %} {% endif %}
{{ media.css }} {{ media.css }}
{% if not actions_on_top and not actions_on_bottom %} {% if not actions_on_top and not actions_on_bottom %}

View File

@ -7,7 +7,7 @@
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}{{ object.pk }}">{{ object|truncatewords:"18" }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
&rsaquo; {% trans 'Delete' %} &rsaquo; {% trans 'Delete' %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -6,7 +6,7 @@
{% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}"> {% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
<h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;<span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span> <h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;<span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span>
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %} {% if inline_admin_form.show_url %}<a href="{% url 'admin:view_on_site' inline_admin_form.original_content_type_id inline_admin_form.original.pk %}">{% trans "View on site" %}</a>{% endif %}
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %} {% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
</h3> </h3>
{% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %} {% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %}

View File

@ -27,7 +27,7 @@
<td class="original"> <td class="original">
{% if inline_admin_form.original or inline_admin_form.show_url %}<p> {% if inline_admin_form.original or inline_admin_form.show_url %}<p>
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %} {% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %} {% if inline_admin_form.show_url %}<a href="{% url 'admin:view_on_site' inline_admin_form.original_content_type_id inline_admin_form.original.pk %}">{% trans "View on site" %}</a>{% endif %}
</p>{% endif %} </p>{% endif %}
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %} {% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
{{ inline_admin_form.fk_field.field }} {{ inline_admin_form.fk_field.field }}

View File

@ -7,7 +7,7 @@
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}{{ object.pk }}">{{ object|truncatewords:"18" }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
&rsaquo; {% trans 'History' %} &rsaquo; {% trans 'History' %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import datetime import datetime
from django.contrib.admin.util import (lookup_field, display_for_field, from django.contrib.admin.util import (lookup_field, display_for_field,
@ -8,11 +10,12 @@ from django.contrib.admin.templatetags.admin_static import static
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
from django.utils import formats from django.utils import formats
from django.utils.html import escape, conditional_escape from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils import six
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.encoding import smart_unicode, force_unicode from django.utils.encoding import smart_text, force_text
from django.template import Library from django.template import Library
from django.template.loader import get_template from django.template.loader import get_template
from django.template.context import Context from django.template.context import Context
@ -27,11 +30,14 @@ def paginator_number(cl,i):
Generates an individual page index link in a paginated list. Generates an individual page index link in a paginated list.
""" """
if i == DOT: if i == DOT:
return u'... ' return '... '
elif i == cl.page_num: elif i == cl.page_num:
return mark_safe(u'<span class="this-page">%d</span> ' % (i+1)) return format_html('<span class="this-page">{0}</span> ', i+1)
else: else:
return mark_safe(u'<a href="%s"%s>%d</a> ' % (escape(cl.get_query_string({PAGE_VAR: i})), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1)) return format_html('<a href="{0}"{1}>{2}</a> ',
cl.get_query_string({PAGE_VAR: i}),
mark_safe(' class="end"' if i == cl.paginator.num_pages-1 else ''),
i+1)
@register.inclusion_tag('admin/pagination.html') @register.inclusion_tag('admin/pagination.html')
def pagination(cl): def pagination(cl):
@ -120,7 +126,7 @@ def result_headers(cl):
if i in ordering_field_columns: if i in ordering_field_columns:
sorted = True sorted = True
order_type = ordering_field_columns.get(i).lower() order_type = ordering_field_columns.get(i).lower()
sort_priority = ordering_field_columns.keys().index(i) + 1 sort_priority = list(ordering_field_columns).index(i) + 1
th_classes.append('sorted %sending' % order_type) th_classes.append('sorted %sending' % order_type)
new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type] new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type]
@ -157,13 +163,14 @@ def result_headers(cl):
"url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}), "url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}),
"url_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}), "url_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}),
"url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}), "url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}),
"class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '') "class_attrib": format_html(' class="{0}"', ' '.join(th_classes))
if th_classes else '',
} }
def _boolean_icon(field_val): def _boolean_icon(field_val):
icon_url = static('admin/img/icon-%s.gif' % icon_url = static('admin/img/icon-%s.gif' %
{True: 'yes', False: 'no', None: 'unknown'}[field_val]) {True: 'yes', False: 'no', None: 'unknown'}[field_val])
return mark_safe(u'<img src="%s" alt="%s" />' % (icon_url, field_val)) return format_html('<img src="{0}" alt="{1}" />', icon_url, field_val)
def items_for_result(cl, result, form): def items_for_result(cl, result, form):
""" """
@ -179,8 +186,8 @@ def items_for_result(cl, result, form):
result_repr = EMPTY_CHANGELIST_VALUE result_repr = EMPTY_CHANGELIST_VALUE
else: else:
if f is None: if f is None:
if field_name == u'action_checkbox': if field_name == 'action_checkbox':
row_class = ' class="action-checkbox"' row_class = mark_safe(' class="action-checkbox"')
allow_tags = getattr(attr, 'allow_tags', False) allow_tags = getattr(attr, 'allow_tags', False)
boolean = getattr(attr, 'boolean', False) boolean = getattr(attr, 'boolean', False)
if boolean: if boolean:
@ -188,24 +195,22 @@ def items_for_result(cl, result, form):
result_repr = display_for_value(value, boolean) result_repr = display_for_value(value, boolean)
# Strip HTML tags in the resulting text, except if the # Strip HTML tags in the resulting text, except if the
# function has an "allow_tags" attribute set to True. # function has an "allow_tags" attribute set to True.
if not allow_tags: if allow_tags:
result_repr = escape(result_repr)
else:
result_repr = mark_safe(result_repr) result_repr = mark_safe(result_repr)
if isinstance(value, (datetime.date, datetime.time)): if isinstance(value, (datetime.date, datetime.time)):
row_class = ' class="nowrap"' row_class = mark_safe(' class="nowrap"')
else: else:
if isinstance(f.rel, models.ManyToOneRel): if isinstance(f.rel, models.ManyToOneRel):
field_val = getattr(result, f.name) field_val = getattr(result, f.name)
if field_val is None: if field_val is None:
result_repr = EMPTY_CHANGELIST_VALUE result_repr = EMPTY_CHANGELIST_VALUE
else: else:
result_repr = escape(field_val) result_repr = field_val
else: else:
result_repr = display_for_field(value, f) result_repr = display_for_field(value, f)
if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)): if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
row_class = ' class="nowrap"' row_class = mark_safe(' class="nowrap"')
if force_unicode(result_repr) == '': if force_text(result_repr) == '':
result_repr = mark_safe('&nbsp;') result_repr = mark_safe('&nbsp;')
# If list_display_links not defined, add the link tag to the first field # If list_display_links not defined, add the link tag to the first field
if (first and not cl.list_display_links) or field_name in cl.list_display_links: if (first and not cl.list_display_links) or field_name in cl.list_display_links:
@ -219,9 +224,15 @@ def items_for_result(cl, result, form):
else: else:
attr = pk attr = pk
value = result.serializable_value(attr) value = result.serializable_value(attr)
result_id = repr(force_unicode(value))[1:] result_id = repr(force_text(value))[1:]
yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \ yield format_html('<{0}{1}><a href="{2}"{3}>{4}</a></{5}>',
(table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag)) table_tag,
row_class,
url,
format_html(' onclick="opener.dismissRelatedLookupPopup(window, {0}); return false;"', result_id)
if cl.is_popup else '',
result_repr,
table_tag)
else: else:
# By default the fields come from ModelAdmin.list_editable, but if we pull # By default the fields come from ModelAdmin.list_editable, but if we pull
# the fields out of the form instead of list_editable custom admins # the fields out of the form instead of list_editable custom admins
@ -230,12 +241,10 @@ def items_for_result(cl, result, form):
field_name == cl.model._meta.pk.name and field_name == cl.model._meta.pk.name and
form[cl.model._meta.pk.name].is_hidden)): form[cl.model._meta.pk.name].is_hidden)):
bf = form[field_name] bf = form[field_name]
result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf)) result_repr = mark_safe(force_text(bf.errors) + force_text(bf))
else: yield format_html('<td{0}>{1}</td>', row_class, result_repr)
result_repr = conditional_escape(result_repr)
yield mark_safe(u'<td%s>%s</td>' % (row_class, result_repr))
if form and not form[cl.model._meta.pk.name].is_hidden: if form and not form[cl.model._meta.pk.name].is_hidden:
yield mark_safe(u'<td>%s</td>' % force_unicode(form[cl.model._meta.pk.name])) yield format_html('<td>{0}</td>', force_text(form[cl.model._meta.pk.name]))
class ResultList(list): class ResultList(list):
# Wrapper class used to return items in a list_editable # Wrapper class used to return items in a list_editable
@ -258,7 +267,7 @@ def result_hidden_fields(cl):
if cl.formset: if cl.formset:
for res, form in zip(cl.result_list, cl.formset.forms): for res, form in zip(cl.result_list, cl.formset.forms):
if form[cl.model._meta.pk.name].is_hidden: if form[cl.model._meta.pk.name].is_hidden:
yield mark_safe(force_unicode(form[cl.model._meta.pk.name])) yield mark_safe(force_text(form[cl.model._meta.pk.name]))
@register.inclusion_tag("admin/change_list_results.html") @register.inclusion_tag("admin/change_list_results.html")
def result_list(cl): def result_list(cl):

View File

@ -32,7 +32,7 @@ def submit_row(context):
'onclick_attrib': (opts.get_ordered_objects() and change 'onclick_attrib': (opts.get_ordered_objects() and change
and 'onclick="submitOrderForm();"' or ''), and 'onclick="submitOrderForm();"' or ''),
'show_delete_link': (not is_popup and context['has_delete_permission'] 'show_delete_link': (not is_popup and context['has_delete_permission']
and (change or context['show_delete'])), and change and context.get('show_delete', True)),
'show_save_as_new': not is_popup and change and save_as, 'show_save_as_new': not is_popup and change and save_as,
'show_save_and_add_another': context['has_add_permission'] and 'show_save_and_add_another': context['has_add_permission'] and
not is_popup and (not save_as or context['add']), not is_popup and (not save_as or context['add']),

View File

@ -1,8 +1,14 @@
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse
from django import template from django import template
from django.contrib.admin.util import quote
register = template.Library() register = template.Library()
@register.filter @register.filter
def admin_urlname(value, arg): def admin_urlname(value, arg):
return 'admin:%s_%s_%s' % (value.app_label, value.module_name, arg) return 'admin:%s_%s_%s' % (value.app_label, value.module_name, arg)
@register.filter
def admin_urlquote(value):
return quote(value)

View File

@ -17,7 +17,7 @@ class AdminLogNode(template.Node):
user_id = self.user user_id = self.user
if not user_id.isdigit(): if not user_id.isdigit():
user_id = context[self.user].id user_id = context[self.user].id
context[self.varname] = LogEntry.objects.filter(user__id__exact=user_id).select_related('content_type', 'user')[:self.limit] context[self.varname] = LogEntry.objects.filter(user__id__exact=user_id).select_related('content_type', 'user')[:int(self.limit)]
return '' return ''
@register.tag @register.tag

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import datetime import datetime
import decimal import decimal
@ -7,11 +9,11 @@ from django.db.models.deletion import Collector
from django.db.models.related import RelatedObject from django.db.models.related import RelatedObject
from django.forms.forms import pretty_name from django.forms.forms import pretty_name
from django.utils import formats from django.utils import formats
from django.utils.html import escape from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_unicode, smart_unicode, smart_str from django.utils.encoding import force_text, smart_text, smart_bytes
from django.utils import six
from django.utils.translation import ungettext from django.utils.translation import ungettext
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
@ -50,7 +52,7 @@ def quote(s):
quoting is slightly different so that it doesn't get automatically quoting is slightly different so that it doesn't get automatically
unquoted by the Web browser. unquoted by the Web browser.
""" """
if not isinstance(s, basestring): if not isinstance(s, six.string_types):
return s return s
res = list(s) res = list(s)
for i in range(len(res)): for i in range(len(res)):
@ -122,15 +124,15 @@ def get_deleted_objects(objs, opts, user, admin_site, using):
if not user.has_perm(p): if not user.has_perm(p):
perms_needed.add(opts.verbose_name) perms_needed.add(opts.verbose_name)
# Display a link to the admin page. # Display a link to the admin page.
return mark_safe(u'%s: <a href="%s">%s</a>' % return format_html('{0}: <a href="{1}">{2}</a>',
(escape(capfirst(opts.verbose_name)), capfirst(opts.verbose_name),
admin_url, admin_url,
escape(obj))) obj)
else: else:
# Don't display link to edit, because it either has no # Don't display link to edit, because it either has no
# admin or is edited inline. # admin or is edited inline.
return u'%s: %s' % (capfirst(opts.verbose_name), return '%s: %s' % (capfirst(opts.verbose_name),
force_unicode(obj)) force_text(obj))
to_delete = collector.nested(format_callback) to_delete = collector.nested(format_callback)
@ -205,8 +207,8 @@ def model_format_dict(obj):
else: else:
opts = obj opts = obj
return { return {
'verbose_name': force_unicode(opts.verbose_name), 'verbose_name': force_text(opts.verbose_name),
'verbose_name_plural': force_unicode(opts.verbose_name_plural) 'verbose_name_plural': force_text(opts.verbose_name_plural)
} }
@ -272,11 +274,11 @@ def label_for_field(name, model, model_admin=None, return_attr=False):
label = field.verbose_name label = field.verbose_name
except models.FieldDoesNotExist: except models.FieldDoesNotExist:
if name == "__unicode__": if name == "__unicode__":
label = force_unicode(model._meta.verbose_name) label = force_text(model._meta.verbose_name)
attr = unicode attr = six.text_type
elif name == "__str__": elif name == "__str__":
label = smart_str(model._meta.verbose_name) label = smart_bytes(model._meta.verbose_name)
attr = str attr = bytes
else: else:
if callable(name): if callable(name):
attr = name attr = name
@ -309,7 +311,7 @@ def help_text_for_field(name, model):
help_text = model._meta.get_field_by_name(name)[0].help_text help_text = model._meta.get_field_by_name(name)[0].help_text
except models.FieldDoesNotExist: except models.FieldDoesNotExist:
help_text = "" help_text = ""
return smart_unicode(help_text) return smart_text(help_text)
def display_for_field(value, field): def display_for_field(value, field):
@ -333,7 +335,7 @@ def display_for_field(value, field):
elif isinstance(field, models.FloatField): elif isinstance(field, models.FloatField):
return formats.number_format(value) return formats.number_format(value)
else: else:
return smart_unicode(value) return smart_text(value)
def display_for_value(value, boolean=False): def display_for_value(value, boolean=False):
@ -348,10 +350,10 @@ def display_for_value(value, boolean=False):
return formats.localize(timezone.template_localtime(value)) return formats.localize(timezone.template_localtime(value))
elif isinstance(value, (datetime.date, datetime.time)): elif isinstance(value, (datetime.date, datetime.time)):
return formats.localize(value) return formats.localize(value)
elif isinstance(value, (decimal.Decimal, float, int, long)): elif isinstance(value, six.integer_types + (decimal.Decimal, float)):
return formats.number_format(value) return formats.number_format(value)
else: else:
return smart_unicode(value) return smart_text(value)
class NotRelationField(Exception): class NotRelationField(Exception):

View File

@ -6,7 +6,7 @@ from django.core.paginator import InvalidPage
from django.db import models from django.db import models
from django.db.models.fields import FieldDoesNotExist from django.db.models.fields import FieldDoesNotExist
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.encoding import force_unicode, smart_str from django.utils.encoding import force_text, smart_bytes
from django.utils.translation import ugettext, ugettext_lazy from django.utils.translation import ugettext, ugettext_lazy
from django.utils.http import urlencode from django.utils.http import urlencode
@ -75,7 +75,7 @@ class ChangeList(object):
title = ugettext('Select %s') title = ugettext('Select %s')
else: else:
title = ugettext('Select %s to change') title = ugettext('Select %s to change')
self.title = title % force_unicode(self.opts.verbose_name) self.title = title % force_text(self.opts.verbose_name)
self.pk_attname = self.lookup_opts.pk.attname self.pk_attname = self.lookup_opts.pk.attname
def get_filters(self, request): def get_filters(self, request):
@ -94,7 +94,7 @@ class ChangeList(object):
# 'key' will be used as a keyword argument later, so Python # 'key' will be used as a keyword argument later, so Python
# requires it to be a string. # requires it to be a string.
del lookup_params[key] del lookup_params[key]
lookup_params[smart_str(key)] = value lookup_params[smart_bytes(key)] = value
if not self.model_admin.lookup_allowed(key, value): if not self.model_admin.lookup_allowed(key, value):
raise SuspiciousOperation("Filtering by %s not allowed" % key) raise SuspiciousOperation("Filtering by %s not allowed" % key)
@ -148,7 +148,7 @@ class ChangeList(object):
if remove is None: remove = [] if remove is None: remove = []
p = self.params.copy() p = self.params.copy()
for r in remove: for r in remove:
for k in p.keys(): for k in list(p):
if k.startswith(r): if k.startswith(r):
del p[k] del p[k]
for k, v in new_params.items(): for k, v in new_params.items():

View File

@ -1,18 +1,21 @@
""" """
Form Widget classes specific to the Django admin site. Form Widget classes specific to the Django admin site.
""" """
from __future__ import unicode_literals
import copy import copy
from django import forms from django import forms
from django.contrib.admin.templatetags.admin_static import static from django.contrib.admin.templatetags.admin_static import static
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.forms.widgets import RadioFieldRenderer from django.forms.widgets import RadioFieldRenderer
from django.forms.util import flatatt from django.forms.util import flatatt
from django.utils.html import escape from django.utils.html import escape, format_html, format_html_join
from django.utils.text import Truncator from django.utils.text import Truncator
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.encoding import force_unicode from django.utils.encoding import force_text
from django.utils import six
class FilteredSelectMultiple(forms.SelectMultiple): class FilteredSelectMultiple(forms.SelectMultiple):
@ -39,12 +42,12 @@ class FilteredSelectMultiple(forms.SelectMultiple):
if self.is_stacked: if self.is_stacked:
attrs['class'] += 'stacked' attrs['class'] += 'stacked'
output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)] output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {') output.append('<script type="text/javascript">addEvent(window, "load", function(e) {')
# TODO: "id_" is hard-coded here. This should instead use the correct # TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically. # API to determine the ID dynamically.
output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' output.append('SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n'
% (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), static('admin/'))) % (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), static('admin/')))
return mark_safe(u''.join(output)) return mark_safe(''.join(output))
class AdminDateWidget(forms.DateInput): class AdminDateWidget(forms.DateInput):
@ -83,24 +86,25 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget):
forms.MultiWidget.__init__(self, widgets, attrs) forms.MultiWidget.__init__(self, widgets, attrs)
def format_output(self, rendered_widgets): def format_output(self, rendered_widgets):
return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \ return format_html('<p class="datetime">{0} {1}<br />{2} {3}</p>',
(_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1])) _('Date:'), rendered_widgets[0],
_('Time:'), rendered_widgets[1])
class AdminRadioFieldRenderer(RadioFieldRenderer): class AdminRadioFieldRenderer(RadioFieldRenderer):
def render(self): def render(self):
"""Outputs a <ul> for this set of radio fields.""" """Outputs a <ul> for this set of radio fields."""
return mark_safe(u'<ul%s>\n%s\n</ul>' % ( return format_html('<ul{0}>\n{1}\n</ul>',
flatatt(self.attrs), flatatt(self.attrs),
u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self])) format_html_join('\n', '<li>{0}</li>',
) ((force_text(w),) for w in self)))
class AdminRadioSelect(forms.RadioSelect): class AdminRadioSelect(forms.RadioSelect):
renderer = AdminRadioFieldRenderer renderer = AdminRadioFieldRenderer
class AdminFileWidget(forms.ClearableFileInput): class AdminFileWidget(forms.ClearableFileInput):
template_with_initial = (u'<p class="file-upload">%s</p>' template_with_initial = ('<p class="file-upload">%s</p>'
% forms.ClearableFileInput.template_with_initial) % forms.ClearableFileInput.template_with_initial)
template_with_clear = (u'<span class="clearable-file-input">%s</span>' template_with_clear = ('<span class="clearable-file-input">%s</span>'
% forms.ClearableFileInput.template_with_clear) % forms.ClearableFileInput.template_with_clear)
def url_params_from_lookup_dict(lookups): def url_params_from_lookup_dict(lookups):
@ -113,12 +117,12 @@ def url_params_from_lookup_dict(lookups):
items = [] items = []
for k, v in lookups.items(): for k, v in lookups.items():
if isinstance(v, (tuple, list)): if isinstance(v, (tuple, list)):
v = u','.join([str(x) for x in v]) v = ','.join([str(x) for x in v])
elif isinstance(v, bool): elif isinstance(v, bool):
# See django.db.fields.BooleanField.get_prep_lookup # See django.db.fields.BooleanField.get_prep_lookup
v = ('0', '1')[v] v = ('0', '1')[v]
else: else:
v = unicode(v) v = six.text_type(v)
items.append((k, v)) items.append((k, v))
params.update(dict(items)) params.update(dict(items))
return params return params
@ -148,21 +152,21 @@ class ForeignKeyRawIdWidget(forms.TextInput):
params = self.url_parameters() params = self.url_parameters()
if params: if params:
url = u'?' + u'&amp;'.join([u'%s=%s' % (k, v) for k, v in params.items()]) url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()])
else: else:
url = u'' url = ''
if "class" not in attrs: if "class" not in attrs:
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook. attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
# TODO: "lookup_id_" is hard-coded here. This should instead use # TODO: "lookup_id_" is hard-coded here. This should instead use
# the correct API to determine the ID dynamically. # the correct API to determine the ID dynamically.
extra.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' extra.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> '
% (related_url, url, name)) % (related_url, url, name))
extra.append(u'<img src="%s" width="16" height="16" alt="%s" /></a>' extra.append('<img src="%s" width="16" height="16" alt="%s" /></a>'
% (static('admin/img/selector-search.gif'), _('Lookup'))) % (static('admin/img/selector-search.gif'), _('Lookup')))
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
if value: if value:
output.append(self.label_for_value(value)) output.append(self.label_for_value(value))
return mark_safe(u''.join(output)) return mark_safe(''.join(output))
def base_url_parameters(self): def base_url_parameters(self):
return url_params_from_lookup_dict(self.rel.limit_choices_to) return url_params_from_lookup_dict(self.rel.limit_choices_to)
@ -193,7 +197,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
# The related object is registered with the same AdminSite # The related object is registered with the same AdminSite
attrs['class'] = 'vManyToManyRawIdAdminField' attrs['class'] = 'vManyToManyRawIdAdminField'
if value: if value:
value = ','.join([force_unicode(v) for v in value]) value = ','.join([force_text(v) for v in value])
else: else:
value = '' value = ''
return super(ManyToManyRawIdWidget, self).render(name, value, attrs) return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
@ -217,7 +221,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
if len(initial) != len(data): if len(initial) != len(data):
return True return True
for pk1, pk2 in zip(initial, data): for pk1, pk2 in zip(initial, data):
if force_unicode(pk1) != force_unicode(pk2): if force_text(pk1) != force_text(pk2):
return True return True
return False return False
@ -261,11 +265,11 @@ class RelatedFieldWidgetWrapper(forms.Widget):
related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name) related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
# TODO: "add_id_" is hard-coded here. This should instead use the # TODO: "add_id_" is hard-coded here. This should instead use the
# correct API to determine the ID dynamically. # correct API to determine the ID dynamically.
output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' output.append('<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> '
% (related_url, name)) % (related_url, name))
output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>' output.append('<img src="%s" width="10" height="10" alt="%s"/></a>'
% (static('admin/img/icon_addlink.gif'), _('Add Another'))) % (static('admin/img/icon_addlink.gif'), _('Add Another')))
return mark_safe(u''.join(output)) return mark_safe(''.join(output))
def build_attrs(self, extra_attrs=None, **kwargs): def build_attrs(self, extra_attrs=None, **kwargs):
"Helper function for building an attribute dictionary." "Helper function for building an attribute dictionary."
@ -303,12 +307,17 @@ class AdminURLFieldWidget(forms.TextInput):
super(AdminURLFieldWidget, self).__init__(attrs=final_attrs) super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
class AdminIntegerFieldWidget(forms.TextInput): class AdminIntegerFieldWidget(forms.TextInput):
class_name = 'vIntegerField'
def __init__(self, attrs=None): def __init__(self, attrs=None):
final_attrs = {'class': 'vIntegerField'} final_attrs = {'class': self.class_name}
if attrs is not None: if attrs is not None:
final_attrs.update(attrs) final_attrs.update(attrs)
super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs) super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)
class AdminBigIntegerFieldWidget(AdminIntegerFieldWidget):
class_name = 'vBigIntegerField'
class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput): class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput):
def __init__(self, attrs=None): def __init__(self, attrs=None):
final_attrs = {'class': 'vCommaSeparatedIntegerField'} final_attrs = {'class': 'vCommaSeparatedIntegerField'}

View File

@ -22,7 +22,7 @@ your computer is "internal").</p>
{% endblocktrans %} {% endblocktrans %}
<div id="content-main"> <div id="content-main">
<h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('HEAD',location.href,false);x.send(null);try{view=x.getResponseHeader('x-view');}catch(e){alert('No view found for this page');return;}if(view=='undefined'){alert('No view found for this page');}document.location='{{ admin_url }}doc/views/'+view+'/';})()">{% trans "Documentation for this page" %}</a></h3> <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('HEAD',location.href,false);x.send(null);try{view=x.getResponseHeader('x-view');}catch(e){alert('No view found for this page');return;}if(view=='undefined'){alert('No view found for this page');}document.location='{{ admin_url|escapejs }}doc/views/'+view+'/';})()">{% trans "Documentation for this page" %}</a></h3>
<p>{% trans "Jumps you from any page to the documentation for the view that generates that page." %}</p> <p>{% trans "Jumps you from any page to the documentation for the view that generates that page." %}</p>
<h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{type=x.getResponseHeader('x-object-type');id=x.getResponseHeader('x-object-id');}catch(e){type='(none)';id='(none)';}d=document;b=d.body;e=d.createElement('div');e.id='xxxhhh';s=e.style;s.position='absolute';s.left='10px';s.top='10px';s.font='10px monospace';s.border='1px black solid';s.padding='4px';s.backgroundColor='#eee';e.appendChild(d.createTextNode('Type: '+type));e.appendChild(d.createElement('br'));e.appendChild(d.createTextNode('ID: '+id));e.appendChild(d.createElement('br'));l=d.createElement('a');l.href='#';l.onclick=function(){b.removeChild(e);};l.appendChild(d.createTextNode('[close]'));l.style.textDecoration='none';e.appendChild(l);b.appendChild(e);})();">{% trans "Show object ID" %}</a></h3> <h3><a href="javascript:(function(){if(typeof ActiveXObject!='undefined'){x=new ActiveXObject('Microsoft.XMLHTTP')}else if(typeof XMLHttpRequest!='undefined'){x=new XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{type=x.getResponseHeader('x-object-type');id=x.getResponseHeader('x-object-id');}catch(e){type='(none)';id='(none)';}d=document;b=d.body;e=d.createElement('div');e.id='xxxhhh';s=e.style;s.position='absolute';s.left='10px';s.top='10px';s.font='10px monospace';s.border='1px black solid';s.padding='4px';s.backgroundColor='#eee';e.appendChild(d.createTextNode('Type: '+type));e.appendChild(d.createElement('br'));e.appendChild(d.createTextNode('ID: '+id));e.appendChild(d.createElement('br'));l=d.createElement('a');l.href='#';l.onclick=function(){b.removeChild(e);};l.appendChild(d.createTextNode('[close]'));l.style.textDecoration='none';e.appendChild(l);b.appendChild(e);})();">{% trans "Show object ID" %}</a></h3>

View File

@ -1,4 +1,4 @@
from __future__ import absolute_import from __future__ import absolute_import, unicode_literals
from django.contrib.admindocs import views from django.contrib.admindocs import views
from django.db.models import fields as builtin_fields from django.db.models import fields as builtin_fields
@ -20,17 +20,17 @@ class TestFieldType(unittest.TestCase):
def test_builtin_fields(self): def test_builtin_fields(self):
self.assertEqual( self.assertEqual(
views.get_readable_field_data_type(builtin_fields.BooleanField()), views.get_readable_field_data_type(builtin_fields.BooleanField()),
_(u'Boolean (Either True or False)') _('Boolean (Either True or False)')
) )
def test_custom_fields(self): def test_custom_fields(self):
self.assertEqual( self.assertEqual(
views.get_readable_field_data_type(fields.CustomField()), views.get_readable_field_data_type(fields.CustomField()),
_(u'A custom field type') _('A custom field type')
) )
self.assertEqual( self.assertEqual(
views.get_readable_field_data_type(fields.DescriptionLackingField()), views.get_readable_field_data_type(fields.DescriptionLackingField()),
_(u'Field of type: %(field_type)s') % { _('Field of type: %(field_type)s') % {
'field_type': 'DescriptionLackingField' 'field_type': 'DescriptionLackingField'
} }
) )

View File

@ -6,7 +6,7 @@ from email.errors import HeaderParseError
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.encoding import smart_str from django.utils.encoding import smart_bytes
try: try:
import docutils.core import docutils.core
import docutils.nodes import docutils.nodes
@ -66,7 +66,7 @@ def parse_rst(text, default_reference_context, thing_being_parsed=None):
"link_base" : reverse('django-admindocs-docroot').rstrip('/') "link_base" : reverse('django-admindocs-docroot').rstrip('/')
} }
if thing_being_parsed: if thing_being_parsed:
thing_being_parsed = smart_str("<%s>" % thing_being_parsed) thing_being_parsed = smart_bytes("<%s>" % thing_being_parsed)
parts = docutils.core.publish_parts(text, source_path=thing_being_parsed, parts = docutils.core.publish_parts(text, source_path=thing_being_parsed,
destination_path=None, writer_name='html', destination_path=None, writer_name='html',
settings_overrides=overrides) settings_overrides=overrides)

View File

@ -14,6 +14,7 @@ from django.core import urlresolvers
from django.contrib.admindocs import utils from django.contrib.admindocs import utils
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.utils.importlib import import_module from django.utils.importlib import import_module
from django.utils import six
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -37,7 +38,7 @@ def bookmarklets(request):
admin_root = urlresolvers.reverse('admin:index') admin_root = urlresolvers.reverse('admin:index')
return render_to_response('admin_doc/bookmarklets.html', { return render_to_response('admin_doc/bookmarklets.html', {
'root_path': admin_root, 'root_path': admin_root,
'admin_url': mark_safe("%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root)), 'admin_url': "%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root),
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@staff_member_required @staff_member_required
@ -48,7 +49,7 @@ def template_tag_index(request):
load_all_installed_template_libraries() load_all_installed_template_libraries()
tags = [] tags = []
app_libs = template.libraries.items() app_libs = list(six.iteritems(template.libraries))
builtin_libs = [(None, lib) for lib in template.builtins] builtin_libs = [(None, lib) for lib in template.builtins]
for module_name, library in builtin_libs + app_libs: for module_name, library in builtin_libs + app_libs:
for tag_name, tag_func in library.tags.items(): for tag_name, tag_func in library.tags.items():
@ -83,7 +84,7 @@ def template_filter_index(request):
load_all_installed_template_libraries() load_all_installed_template_libraries()
filters = [] filters = []
app_libs = template.libraries.items() app_libs = list(six.iteritems(template.libraries))
builtin_libs = [(None, lib) for lib in template.builtins] builtin_libs = [(None, lib) for lib in template.builtins]
for module_name, library in builtin_libs + app_libs: for module_name, library in builtin_libs + app_libs:
for filter_name, filter_func in library.filters.items(): for filter_name, filter_func in library.filters.items():

View File

@ -12,6 +12,7 @@ from django.template.response import TemplateResponse
from django.utils.html import escape from django.utils.html import escape
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils import six
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext, ugettext_lazy as _
from django.views.decorators.csrf import csrf_protect from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters from django.views.decorators.debug import sensitive_post_parameters
@ -128,13 +129,13 @@ class UserAdmin(admin.ModelAdmin):
else: else:
form = self.change_password_form(user) form = self.change_password_form(user)
fieldsets = [(None, {'fields': form.base_fields.keys()})] fieldsets = [(None, {'fields': list(form.base_fields)})]
adminForm = admin.helpers.AdminForm(form, fieldsets, {}) adminForm = admin.helpers.AdminForm(form, fieldsets, {})
context = { context = {
'title': _('Change password: %s') % escape(user.username), 'title': _('Change password: %s') % escape(user.username),
'adminForm': adminForm, 'adminForm': adminForm,
'form_url': mark_safe(form_url), 'form_url': form_url,
'form': form, 'form': form,
'is_popup': '_popup' in request.REQUEST, 'is_popup': '_popup' in request.REQUEST,
'add': True, 'add': True,

View File

@ -1,3 +1,4 @@
from __future__ import unicode_literals
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
@ -40,7 +41,7 @@ class ModelBackend(object):
if user_obj.is_anonymous() or obj is not None: if user_obj.is_anonymous() or obj is not None:
return set() return set()
if not hasattr(user_obj, '_perm_cache'): if not hasattr(user_obj, '_perm_cache'):
user_obj._perm_cache = set([u"%s.%s" % (p.content_type.app_label, p.codename) for p in user_obj.user_permissions.select_related()]) user_obj._perm_cache = set(["%s.%s" % (p.content_type.app_label, p.codename) for p in user_obj.user_permissions.select_related()])
user_obj._perm_cache.update(self.get_group_permissions(user_obj)) user_obj._perm_cache.update(self.get_group_permissions(user_obj))
return user_obj._perm_cache return user_obj._perm_cache

View File

@ -11,8 +11,9 @@ class PermLookupDict(object):
def __getitem__(self, perm_name): def __getitem__(self, perm_name):
return self.user.has_perm("%s.%s" % (self.module_name, perm_name)) return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
def __nonzero__(self): def __bool__(self):
return self.user.has_module_perms(self.module_name) return self.user.has_module_perms(self.module_name)
__nonzero__ = __bool__ # Python 2
class PermWrapper(object): class PermWrapper(object):

View File

@ -1,9 +1,13 @@
import urlparse try:
from urllib.parse import urlparse
except ImportError: # Python 2
from urlparse import urlparse
from functools import wraps from functools import wraps
from django.conf import settings from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.utils.decorators import available_attrs from django.utils.decorators import available_attrs
from django.utils.encoding import force_str
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME): def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
@ -19,11 +23,12 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
if test_func(request.user): if test_func(request.user):
return view_func(request, *args, **kwargs) return view_func(request, *args, **kwargs)
path = request.build_absolute_uri() path = request.build_absolute_uri()
# urlparse chokes on lazy objects in Python 3
login_url_as_str = force_str(login_url or settings.LOGIN_URL)
# If the login url is the same scheme and net location then just # If the login url is the same scheme and net location then just
# use the path as the "next" url. # use the path as the "next" url.
login_scheme, login_netloc = urlparse.urlparse(login_url or login_scheme, login_netloc = urlparse(login_url_as_str)[:2]
settings.LOGIN_URL)[:2] current_scheme, current_netloc = urlparse(path)[:2]
current_scheme, current_netloc = urlparse.urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)): (not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path() path = request.get_full_path()

View File

@ -1,17 +1,21 @@
from __future__ import unicode_literals
from django import forms from django import forms
from django.forms.util import flatatt from django.forms.util import flatatt
from django.template import loader from django.template import loader
from django.utils.encoding import smart_str from django.utils.datastructures import SortedDict
from django.utils.html import format_html, format_html_join
from django.utils.http import int_to_base36 from django.utils.http import int_to_base36
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext, ugettext_lazy as _
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, get_hasher from django.contrib.auth.hashers import UNUSABLE_PASSWORD, is_password_usable, identify_hasher
from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import get_current_site from django.contrib.sites.models import get_current_site
UNMASKED_DIGITS_TO_SHOW = 6 UNMASKED_DIGITS_TO_SHOW = 6
mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p) - UNMASKED_DIGITS_TO_SHOW, 0)) mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p) - UNMASKED_DIGITS_TO_SHOW, 0))
@ -26,23 +30,18 @@ class ReadOnlyPasswordHashWidget(forms.Widget):
final_attrs = self.build_attrs(attrs) final_attrs = self.build_attrs(attrs)
encoded = smart_str(encoded)
if len(encoded) == 32 and '$' not in encoded:
algorithm = 'unsalted_md5'
else:
algorithm = encoded.split('$', 1)[0]
try: try:
hasher = get_hasher(algorithm) hasher = identify_hasher(encoded)
except ValueError: except ValueError:
summary = "<strong>Invalid password format or unknown hashing algorithm.</strong>" summary = mark_safe("<strong>Invalid password format or unknown hashing algorithm.</strong>")
else: else:
summary = "" summary = format_html_join('',
for key, value in hasher.safe_summary(encoded).iteritems(): "<strong>{0}</strong>: {1} ",
summary += "<strong>%(key)s</strong>: %(value)s " % {"key": ugettext(key), "value": value} ((ugettext(key), value)
for key, value in hasher.safe_summary(encoded).items())
)
return mark_safe("<div%(attrs)s>%(summary)s</div>" % {"attrs": flatatt(final_attrs), "summary": summary}) return format_html("<div{0}>{1}</div>", flatatt(final_attrs), summary)
class ReadOnlyPasswordHashField(forms.Field): class ReadOnlyPasswordHashField(forms.Field):
@ -90,9 +89,9 @@ class UserCreationForm(forms.ModelForm):
raise forms.ValidationError(self.error_messages['duplicate_username']) raise forms.ValidationError(self.error_messages['duplicate_username'])
def clean_password2(self): def clean_password2(self):
password1 = self.cleaned_data.get("password1", "") password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data["password2"] password2 = self.cleaned_data.get("password2")
if password1 != password2: if password1 and password2 and password1 != password2:
raise forms.ValidationError( raise forms.ValidationError(
self.error_messages['password_mismatch']) self.error_messages['password_mismatch'])
return password2 return password2
@ -296,8 +295,11 @@ class PasswordChangeForm(SetPasswordForm):
raise forms.ValidationError( raise forms.ValidationError(
self.error_messages['password_incorrect']) self.error_messages['password_incorrect'])
return old_password return old_password
PasswordChangeForm.base_fields.keyOrder = ['old_password', 'new_password1',
'new_password2'] PasswordChangeForm.base_fields = SortedDict([
(k, PasswordChangeForm.base_fields[k])
for k in ['old_password', 'new_password1', 'new_password2']
])
class AdminPasswordChangeForm(forms.Form): class AdminPasswordChangeForm(forms.Form):

View File

@ -1,3 +1,6 @@
from __future__ import unicode_literals
import base64
import hashlib import hashlib
from django.dispatch import receiver from django.dispatch import receiver
@ -5,7 +8,7 @@ from django.conf import settings
from django.test.signals import setting_changed from django.test.signals import setting_changed
from django.utils import importlib from django.utils import importlib
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.utils.encoding import smart_str from django.utils.encoding import smart_bytes
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.crypto import ( from django.utils.crypto import (
pbkdf2, constant_time_compare, get_random_string) pbkdf2, constant_time_compare, get_random_string)
@ -40,20 +43,12 @@ def check_password(password, encoded, setter=None, preferred='default'):
return False return False
preferred = get_hasher(preferred) preferred = get_hasher(preferred)
raw_password = password hasher = identify_hasher(encoded)
password = smart_str(password)
encoded = smart_str(encoded)
if len(encoded) == 32 and '$' not in encoded:
hasher = get_hasher('unsalted_md5')
else:
algorithm = encoded.split('$', 1)[0]
hasher = get_hasher(algorithm)
must_update = hasher.algorithm != preferred.algorithm must_update = hasher.algorithm != preferred.algorithm
is_correct = hasher.verify(password, encoded) is_correct = hasher.verify(password, encoded)
if setter and is_correct and must_update: if setter and is_correct and must_update:
setter(raw_password) setter(password)
return is_correct return is_correct
@ -69,11 +64,9 @@ def make_password(password, salt=None, hasher='default'):
return UNUSABLE_PASSWORD return UNUSABLE_PASSWORD
hasher = get_hasher(hasher) hasher = get_hasher(hasher)
password = smart_str(password)
if not salt: if not salt:
salt = hasher.salt() salt = hasher.salt()
salt = smart_str(salt)
return hasher.encode(password, salt) return hasher.encode(password, salt)
@ -125,6 +118,21 @@ def get_hasher(algorithm='default'):
return HASHERS[algorithm] return HASHERS[algorithm]
def identify_hasher(encoded):
"""
Returns an instance of a loaded password hasher.
Identifies hasher algorithm by examining encoded hash, and calls
get_hasher() to return hasher. Raises ValueError if
algorithm cannot be identified, or if hasher is not loaded.
"""
if len(encoded) == 32 and '$' not in encoded:
algorithm = 'unsalted_md5'
else:
algorithm = encoded.split('$', 1)[0]
return get_hasher(algorithm)
def mask_hash(hash, show=6, char="*"): def mask_hash(hash, show=6, char="*"):
""" """
Returns the given hash, with only the first ``show`` number shown. The Returns the given hash, with only the first ``show`` number shown. The
@ -211,7 +219,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
if not iterations: if not iterations:
iterations = self.iterations iterations = self.iterations
hash = pbkdf2(password, salt, iterations, digest=self.digest) hash = pbkdf2(password, salt, iterations, digest=self.digest)
hash = hash.encode('base64').strip() hash = base64.b64encode(hash).decode('ascii').strip()
return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash) return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash)
def verify(self, password, encoded): def verify(self, password, encoded):
@ -291,7 +299,7 @@ class SHA1PasswordHasher(BasePasswordHasher):
def encode(self, password, salt): def encode(self, password, salt):
assert password assert password
assert salt and '$' not in salt assert salt and '$' not in salt
hash = hashlib.sha1(salt + password).hexdigest() hash = hashlib.sha1(smart_bytes(salt + password)).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash) return "%s$%s$%s" % (self.algorithm, salt, hash)
def verify(self, password, encoded): def verify(self, password, encoded):
@ -319,7 +327,7 @@ class MD5PasswordHasher(BasePasswordHasher):
def encode(self, password, salt): def encode(self, password, salt):
assert password assert password
assert salt and '$' not in salt assert salt and '$' not in salt
hash = hashlib.md5(salt + password).hexdigest() hash = hashlib.md5(smart_bytes(salt + password)).hexdigest()
return "%s$%s$%s" % (self.algorithm, salt, hash) return "%s$%s$%s" % (self.algorithm, salt, hash)
def verify(self, password, encoded): def verify(self, password, encoded):
@ -353,7 +361,7 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
return '' return ''
def encode(self, password, salt): def encode(self, password, salt):
return hashlib.md5(password).hexdigest() return hashlib.md5(smart_bytes(password)).hexdigest()
def verify(self, password, encoded): def verify(self, password, encoded):
encoded_2 = self.encode(password, '') encoded_2 = self.encode(password, '')

View File

@ -1,6 +1,8 @@
""" """
Creates permissions for all installed apps that need permissions. Creates permissions for all installed apps that need permissions.
""" """
from __future__ import unicode_literals
import getpass import getpass
import locale import locale
import unicodedata import unicodedata
@ -8,17 +10,18 @@ import unicodedata
from django.contrib.auth import models as auth_app, get_user_model from django.contrib.auth import models as auth_app, get_user_model
from django.core import exceptions from django.core import exceptions
from django.db.models import get_models, signals from django.db.models import get_models, signals
from django.utils.six.moves import input
def _get_permission_codename(action, opts): def _get_permission_codename(action, opts):
return u'%s_%s' % (action, opts.object_name.lower()) return '%s_%s' % (action, opts.object_name.lower())
def _get_all_permissions(opts): def _get_all_permissions(opts):
"Returns (codename, name) for all permissions in the given opts." "Returns (codename, name) for all permissions in the given opts."
perms = [] perms = []
for action in ('add', 'change', 'delete'): for action in ('add', 'change', 'delete'):
perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw))) perms.append((_get_permission_codename(action, opts), 'Can %s %s' % (action, opts.verbose_name_raw)))
return perms + list(opts.permissions) return perms + list(opts.permissions)
@ -67,10 +70,10 @@ def create_superuser(app, created_models, verbosity, db, **kwargs):
msg = ("\nYou just installed Django's auth system, which means you " msg = ("\nYou just installed Django's auth system, which means you "
"don't have any superusers defined.\nWould you like to create one " "don't have any superusers defined.\nWould you like to create one "
"now? (yes/no): ") "now? (yes/no): ")
confirm = raw_input(msg) confirm = input(msg)
while 1: while 1:
if confirm not in ('yes', 'no'): if confirm not in ('yes', 'no'):
confirm = raw_input('Please enter either "yes" or "no": ') confirm = input('Please enter either "yes" or "no": ')
continue continue
if confirm == 'yes': if confirm == 'yes':
call_command("createsuperuser", interactive=True, database=db) call_command("createsuperuser", interactive=True, database=db)
@ -84,14 +87,17 @@ def get_system_username():
:returns: The username as a unicode string, or an empty string if the :returns: The username as a unicode string, or an empty string if the
username could not be determined. username could not be determined.
""" """
try: default_locale = locale.getdefaultlocale()[1]
return getpass.getuser().decode(locale.getdefaultlocale()[1]) if default_locale:
except (ImportError, KeyError, UnicodeDecodeError): try:
# KeyError will be raised by os.getpwuid() (called by getuser()) return getpass.getuser().decode(default_locale)
# if there is no corresponding entry in the /etc/passwd file except (ImportError, KeyError, UnicodeDecodeError):
# (a very restricted chroot environment, for example). # KeyError will be raised by os.getpwuid() (called by getuser())
# UnicodeDecodeError - preventive treatment for non-latin Windows. # if there is no corresponding entry in the /etc/passwd file
return u'' # (a very restricted chroot environment, for example).
# UnicodeDecodeError - preventive treatment for non-latin Windows.
pass
return ''
def get_default_username(check_db=True): def get_default_username(check_db=True):
@ -111,7 +117,7 @@ def get_default_username(check_db=True):
default_username = get_system_username() default_username = get_system_username()
try: try:
default_username = unicodedata.normalize('NFKD', default_username)\ default_username = unicodedata.normalize('NFKD', default_username)\
.encode('ascii', 'ignore').replace(' ', '').lower() .encode('ascii', 'ignore').decode('ascii').replace(' ', '').lower()
except UnicodeDecodeError: except UnicodeDecodeError:
return '' return ''

View File

@ -11,6 +11,7 @@ from django.contrib.auth.management import get_default_username
from django.core import exceptions from django.core import exceptions
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS from django.db import DEFAULT_DB_ALIAS
from django.utils.six.moves import input
from django.utils.text import capfirst from django.utils.text import capfirst
@ -78,7 +79,7 @@ class Command(BaseCommand):
input_msg = capfirst(username_field.verbose_name) input_msg = capfirst(username_field.verbose_name)
if default_username: if default_username:
input_msg += ' (leave blank to use %r)' % default_username input_msg += ' (leave blank to use %r)' % default_username
raw_value = raw_input(input_msg + ': ') raw_value = input(input_msg + ': ')
if default_username and raw_value == '': if default_username and raw_value == '':
username = default_username username = default_username
try: try:
@ -99,9 +100,9 @@ class Command(BaseCommand):
for field_name in other_fields: for field_name in other_fields:
field = UserModel._meta.get_field(field_name) field = UserModel._meta.get_field(field_name)
other_data[field_name] = None other_data[field_name] = options.get(field_name)
while other_data[field_name] is None: while other_data[field_name] is None:
raw_value = raw_input(capfirst(field.verbose_name + ': ')) raw_value = input(capfirst(field.verbose_name + ': '))
try: try:
other_data[field_name] = field.clean(raw_value, None) other_data[field_name] = field.clean(raw_value, None)
except exceptions.ValidationError, e: except exceptions.ValidationError, e:

View File

@ -1,3 +1,4 @@
from __future__ import unicode_literals
import re import re
import urllib import urllib
@ -7,7 +8,8 @@ from django.core import validators
from django.db import models from django.db import models
from django.db.models.manager import EmptyManager from django.db.models.manager import EmptyManager
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.encoding import smart_str from django.utils.http import urlquote
from django.utils import six
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone from django.utils import timezone
@ -17,6 +19,7 @@ from django.contrib.auth.hashers import (
check_password, make_password, is_password_usable, UNUSABLE_PASSWORD) check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
from django.contrib.auth.signals import user_logged_in from django.contrib.auth.signals import user_logged_in
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import python_2_unicode_compatible
def update_last_login(sender, user, **kwargs): def update_last_login(sender, user, **kwargs):
@ -42,6 +45,7 @@ class PermissionManager(models.Manager):
) )
@python_2_unicode_compatible
class Permission(models.Model): class Permission(models.Model):
""" """
The permissions system provides a way to assign permissions to specific The permissions system provides a way to assign permissions to specific
@ -77,11 +81,11 @@ class Permission(models.Model):
ordering = ('content_type__app_label', 'content_type__model', ordering = ('content_type__app_label', 'content_type__model',
'codename') 'codename')
def __unicode__(self): def __str__(self):
return u"%s | %s | %s" % ( return "%s | %s | %s" % (
unicode(self.content_type.app_label), six.text_type(self.content_type.app_label),
unicode(self.content_type), six.text_type(self.content_type),
unicode(self.name)) six.text_type(self.name))
def natural_key(self): def natural_key(self):
return (self.codename,) + self.content_type.natural_key() return (self.codename,) + self.content_type.natural_key()
@ -96,6 +100,7 @@ class GroupManager(models.Manager):
return self.get(name=name) return self.get(name=name)
@python_2_unicode_compatible
class Group(models.Model): class Group(models.Model):
""" """
Groups are a generic way of categorizing users to apply permissions, or Groups are a generic way of categorizing users to apply permissions, or
@ -123,7 +128,7 @@ class Group(models.Model):
verbose_name = _('group') verbose_name = _('group')
verbose_name_plural = _('groups') verbose_name_plural = _('groups')
def __unicode__(self): def __str__(self):
return self.name return self.name
def natural_key(self): def natural_key(self):
@ -253,7 +258,7 @@ class AbstractBaseUser(models.Model):
""" """
def setter(raw_password): def setter(raw_password):
self.set_password(raw_password) self.set_password(raw_password)
self.save() self.save(update_fields=["password"])
return check_password(raw_password, self.password, setter) return check_password(raw_password, self.password, setter)
def set_unusable_password(self): def set_unusable_password(self):
@ -270,6 +275,7 @@ class AbstractBaseUser(models.Model):
raise NotImplementedError() raise NotImplementedError()
@python_2_unicode_compatible
class User(AbstractBaseUser): class User(AbstractBaseUser):
""" """
Users within the Django authentication system are represented by this Users within the Django authentication system are represented by this
@ -311,20 +317,20 @@ class User(AbstractBaseUser):
verbose_name_plural = _('users') verbose_name_plural = _('users')
swappable = 'AUTH_USER_MODEL' swappable = 'AUTH_USER_MODEL'
def __unicode__(self): def __str__(self):
return self.username return self.username
def natural_key(self): def natural_key(self):
return (self.username,) return (self.username,)
def get_absolute_url(self): def get_absolute_url(self):
return "/users/%s/" % urllib.quote(smart_str(self.username)) return "/users/%s/" % urlquote(self.username)
def get_full_name(self): def get_full_name(self):
""" """
Returns the first_name plus the last_name, with a space in between. Returns the first_name plus the last_name, with a space in between.
""" """
full_name = u'%s %s' % (self.first_name, self.last_name) full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip() return full_name.strip()
def get_short_name(self): def get_short_name(self):
@ -425,6 +431,7 @@ class User(AbstractBaseUser):
return self._profile_cache return self._profile_cache
@python_2_unicode_compatible
class AnonymousUser(object): class AnonymousUser(object):
id = None id = None
pk = None pk = None
@ -438,11 +445,8 @@ class AnonymousUser(object):
def __init__(self): def __init__(self):
pass pass
def __unicode__(self):
return 'AnonymousUser'
def __str__(self): def __str__(self):
return unicode(self).encode('utf-8') return 'AnonymousUser'
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, self.__class__) return isinstance(other, self.__class__)

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User, Group, Permission, AnonymousUser from django.contrib.auth.models import User, Group, Permission, AnonymousUser
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -51,7 +53,7 @@ class BackendTest(TestCase):
# reloading user to purge the _perm_cache # reloading user to purge the _perm_cache
user = User.objects.get(username='test') user = User.objects.get(username='test')
self.assertEqual(user.get_all_permissions() == set([u'auth.test']), True) self.assertEqual(user.get_all_permissions() == set(['auth.test']), True)
self.assertEqual(user.get_group_permissions(), set([])) self.assertEqual(user.get_group_permissions(), set([]))
self.assertEqual(user.has_module_perms('Group'), False) self.assertEqual(user.has_module_perms('Group'), False)
self.assertEqual(user.has_module_perms('auth'), True) self.assertEqual(user.has_module_perms('auth'), True)
@ -62,7 +64,7 @@ class BackendTest(TestCase):
user.user_permissions.add(perm) user.user_permissions.add(perm)
user.save() user.save()
user = User.objects.get(username='test') user = User.objects.get(username='test')
self.assertEqual(user.get_all_permissions(), set([u'auth.test2', u'auth.test', u'auth.test3'])) self.assertEqual(user.get_all_permissions(), set(['auth.test2', 'auth.test', 'auth.test3']))
self.assertEqual(user.has_perm('test'), False) self.assertEqual(user.has_perm('test'), False)
self.assertEqual(user.has_perm('auth.test'), True) self.assertEqual(user.has_perm('auth.test'), True)
self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), True) self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), True)
@ -72,9 +74,9 @@ class BackendTest(TestCase):
group.save() group.save()
user.groups.add(group) user.groups.add(group)
user = User.objects.get(username='test') user = User.objects.get(username='test')
exp = set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group']) exp = set(['auth.test2', 'auth.test', 'auth.test3', 'auth.test_group'])
self.assertEqual(user.get_all_permissions(), exp) self.assertEqual(user.get_all_permissions(), exp)
self.assertEqual(user.get_group_permissions(), set([u'auth.test_group'])) self.assertEqual(user.get_group_permissions(), set(['auth.test_group']))
self.assertEqual(user.has_perms(['auth.test3', 'auth.test_group']), True) self.assertEqual(user.has_perms(['auth.test3', 'auth.test_group']), True)
user = AnonymousUser() user = AnonymousUser()

View File

@ -1,7 +1,10 @@
from django.test import TestCase import locale
from django.contrib.auth.management.commands import createsuperuser
from django.contrib.auth.models import User, AnonymousUser from django.contrib.auth.models import User, AnonymousUser
from django.core.management import call_command from django.core.management import call_command
from StringIO import StringIO from django.test import TestCase
from django.utils.six import StringIO
class BasicTestCase(TestCase): class BasicTestCase(TestCase):
@ -60,3 +63,87 @@ class BasicTestCase(TestCase):
self.assertTrue(super.is_superuser) self.assertTrue(super.is_superuser)
self.assertTrue(super.is_active) self.assertTrue(super.is_active)
self.assertTrue(super.is_staff) self.assertTrue(super.is_staff)
def test_createsuperuser_management_command(self):
"Check the operation of the createsuperuser management command"
# We can use the management command to create a superuser
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe",
email="joe@somewhere.org",
stdout=new_io
)
command_output = new_io.getvalue().strip()
self.assertEqual(command_output, 'Superuser created successfully.')
u = User.objects.get(username="joe")
self.assertEqual(u.email, 'joe@somewhere.org')
# created password should be unusable
self.assertFalse(u.has_usable_password())
# We can supress output on the management command
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe2",
email="joe2@somewhere.org",
verbosity=0,
stdout=new_io
)
command_output = new_io.getvalue().strip()
self.assertEqual(command_output, '')
u = User.objects.get(username="joe2")
self.assertEqual(u.email, 'joe2@somewhere.org')
self.assertFalse(u.has_usable_password())
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
username="joe+admin@somewhere.org",
email="joe@somewhere.org",
stdout=new_io
)
u = User.objects.get(username="joe+admin@somewhere.org")
self.assertEqual(u.email, 'joe@somewhere.org')
self.assertFalse(u.has_usable_password())
def test_createsuperuser_nolocale(self):
"""
Check that createsuperuser does not break when no locale is set. See
ticket #16017.
"""
old_getdefaultlocale = locale.getdefaultlocale
old_getpass = createsuperuser.getpass
try:
# Temporarily remove locale information
locale.getdefaultlocale = lambda: (None, None)
# Temporarily replace getpass to allow interactive code to be used
# non-interactively
class mock_getpass:
pass
mock_getpass.getpass = staticmethod(lambda p=None: "nopasswd")
createsuperuser.getpass = mock_getpass
# Call the command in this new environment
new_io = StringIO()
call_command("createsuperuser",
interactive=True,
username="nolocale@somewhere.org",
email="nolocale@somewhere.org",
stdout=new_io
)
except TypeError:
self.fail("createsuperuser fails if the OS provides no information about the current locale")
finally:
# Re-apply locale and getpass information
createsuperuser.getpass = old_getpass
locale.getdefaultlocale = old_getdefaultlocale
# If we were successful, a user should have been created
u = User.objects.get(username="nolocale@somewhere.org")
self.assertEqual(u.email, 'nolocale@somewhere.org')

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import os import os
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm, from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
@ -6,7 +8,8 @@ from django.core import mail
from django.forms.fields import Field, EmailField from django.forms.fields import Field, EmailField
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from django.utils.encoding import force_unicode from django.utils.encoding import force_text
from django.utils import six
from django.utils import translation from django.utils import translation
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -25,7 +28,7 @@ class UserCreationFormTest(TestCase):
form = UserCreationForm(data) form = UserCreationForm(data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form["username"].errors, self.assertEqual(form["username"].errors,
[force_unicode(form.error_messages['duplicate_username'])]) [force_text(form.error_messages['duplicate_username'])])
def test_invalid_data(self): def test_invalid_data(self):
data = { data = {
@ -36,7 +39,7 @@ class UserCreationFormTest(TestCase):
form = UserCreationForm(data) form = UserCreationForm(data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form["username"].errors, self.assertEqual(form["username"].errors,
[force_unicode(form.fields['username'].error_messages['invalid'])]) [force_text(form.fields['username'].error_messages['invalid'])])
def test_password_verification(self): def test_password_verification(self):
# The verification password is incorrect. # The verification password is incorrect.
@ -48,13 +51,13 @@ class UserCreationFormTest(TestCase):
form = UserCreationForm(data) form = UserCreationForm(data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form["password2"].errors, self.assertEqual(form["password2"].errors,
[force_unicode(form.error_messages['password_mismatch'])]) [force_text(form.error_messages['password_mismatch'])])
def test_both_passwords(self): def test_both_passwords(self):
# One (or both) passwords weren't given # One (or both) passwords weren't given
data = {'username': 'jsmith'} data = {'username': 'jsmith'}
form = UserCreationForm(data) form = UserCreationForm(data)
required_error = [force_unicode(Field.default_error_messages['required'])] required_error = [force_text(Field.default_error_messages['required'])]
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form['password1'].errors, required_error) self.assertEqual(form['password1'].errors, required_error)
self.assertEqual(form['password2'].errors, required_error) self.assertEqual(form['password2'].errors, required_error)
@ -63,6 +66,7 @@ class UserCreationFormTest(TestCase):
form = UserCreationForm(data) form = UserCreationForm(data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form['password1'].errors, required_error) self.assertEqual(form['password1'].errors, required_error)
self.assertEqual(form['password2'].errors, [])
def test_success(self): def test_success(self):
# The success case. # The success case.
@ -92,7 +96,7 @@ class AuthenticationFormTest(TestCase):
form = AuthenticationForm(None, data) form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(), self.assertEqual(form.non_field_errors(),
[force_unicode(form.error_messages['invalid_login'])]) [force_text(form.error_messages['invalid_login'])])
def test_inactive_user(self): def test_inactive_user(self):
# The user is inactive. # The user is inactive.
@ -103,7 +107,7 @@ class AuthenticationFormTest(TestCase):
form = AuthenticationForm(None, data) form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(), self.assertEqual(form.non_field_errors(),
[force_unicode(form.error_messages['inactive'])]) [force_text(form.error_messages['inactive'])])
def test_inactive_user_i18n(self): def test_inactive_user_i18n(self):
with self.settings(USE_I18N=True): with self.settings(USE_I18N=True):
@ -116,7 +120,7 @@ class AuthenticationFormTest(TestCase):
form = AuthenticationForm(None, data) form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(), self.assertEqual(form.non_field_errors(),
[force_unicode(form.error_messages['inactive'])]) [force_text(form.error_messages['inactive'])])
def test_success(self): def test_success(self):
# The success case # The success case
@ -144,7 +148,7 @@ class SetPasswordFormTest(TestCase):
form = SetPasswordForm(user, data) form = SetPasswordForm(user, data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form["new_password2"].errors, self.assertEqual(form["new_password2"].errors,
[force_unicode(form.error_messages['password_mismatch'])]) [force_text(form.error_messages['password_mismatch'])])
def test_success(self): def test_success(self):
user = User.objects.get(username='testclient') user = User.objects.get(username='testclient')
@ -171,7 +175,7 @@ class PasswordChangeFormTest(TestCase):
form = PasswordChangeForm(user, data) form = PasswordChangeForm(user, data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form["old_password"].errors, self.assertEqual(form["old_password"].errors,
[force_unicode(form.error_messages['password_incorrect'])]) [force_text(form.error_messages['password_incorrect'])])
def test_password_verification(self): def test_password_verification(self):
# The two new passwords do not match. # The two new passwords do not match.
@ -184,7 +188,7 @@ class PasswordChangeFormTest(TestCase):
form = PasswordChangeForm(user, data) form = PasswordChangeForm(user, data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form["new_password2"].errors, self.assertEqual(form["new_password2"].errors,
[force_unicode(form.error_messages['password_mismatch'])]) [force_text(form.error_messages['password_mismatch'])])
def test_success(self): def test_success(self):
# The success case. # The success case.
@ -200,7 +204,7 @@ class PasswordChangeFormTest(TestCase):
def test_field_order(self): def test_field_order(self):
# Regression test - check the order of fields: # Regression test - check the order of fields:
user = User.objects.get(username='testclient') user = User.objects.get(username='testclient')
self.assertEqual(PasswordChangeForm(user, {}).fields.keys(), self.assertEqual(list(PasswordChangeForm(user, {}).fields),
['old_password', 'new_password1', 'new_password2']) ['old_password', 'new_password1', 'new_password2'])
@ -215,7 +219,7 @@ class UserChangeFormTest(TestCase):
form = UserChangeForm(data, instance=user) form = UserChangeForm(data, instance=user)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form['username'].errors, self.assertEqual(form['username'].errors,
[force_unicode(form.fields['username'].error_messages['invalid'])]) [force_text(form.fields['username'].error_messages['invalid'])])
def test_bug_14242(self): def test_bug_14242(self):
# A regression test, introduce by adding an optimization for the # A regression test, introduce by adding an optimization for the
@ -270,7 +274,7 @@ class PasswordResetFormTest(TestCase):
form = PasswordResetForm(data) form = PasswordResetForm(data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form['email'].errors, self.assertEqual(form['email'].errors,
[force_unicode(EmailField.default_error_messages['invalid'])]) [force_text(EmailField.default_error_messages['invalid'])])
def test_nonexistant_email(self): def test_nonexistant_email(self):
# Test nonexistant email address # Test nonexistant email address
@ -278,7 +282,7 @@ class PasswordResetFormTest(TestCase):
form = PasswordResetForm(data) form = PasswordResetForm(data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form.errors, self.assertEqual(form.errors,
{'email': [force_unicode(form.error_messages['unknown'])]}) {'email': [force_text(form.error_messages['unknown'])]})
def test_cleaned_data(self): def test_cleaned_data(self):
# Regression test # Regression test
@ -299,7 +303,7 @@ class PasswordResetFormTest(TestCase):
# potential case where contrib.sites is not installed. Refs #16412. # potential case where contrib.sites is not installed. Refs #16412.
form.save(domain_override='example.com') form.save(domain_override='example.com')
self.assertEqual(len(mail.outbox), 1) self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, u'Custom password reset on example.com') self.assertEqual(mail.outbox[0].subject, 'Custom password reset on example.com')
def test_bug_5605(self): def test_bug_5605(self):
# bug #5605, preserve the case of the user name (before the @ in the # bug #5605, preserve the case of the user name (before the @ in the
@ -328,4 +332,4 @@ class PasswordResetFormTest(TestCase):
form = PasswordResetForm(data) form = PasswordResetForm(data)
self.assertFalse(form.is_valid()) self.assertFalse(form.is_valid())
self.assertEqual(form["email"].errors, self.assertEqual(form["email"].errors,
[_(u"The user account associated with this e-mail address cannot reset the password.")]) [_("The user account associated with this e-mail address cannot reset the password.")])

View File

@ -1,7 +1,9 @@
from __future__ import unicode_literals
from django.conf.global_settings import PASSWORD_HASHERS as default_hashers from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
from django.contrib.auth.hashers import (is_password_usable, from django.contrib.auth.hashers import (is_password_usable,
check_password, make_password, PBKDF2PasswordHasher, load_hashers, check_password, make_password, PBKDF2PasswordHasher, load_hashers,
PBKDF2SHA1PasswordHasher, get_hasher, UNUSABLE_PASSWORD) PBKDF2SHA1PasswordHasher, get_hasher, identify_hasher, UNUSABLE_PASSWORD)
from django.utils import unittest from django.utils import unittest
from django.utils.unittest import skipUnless from django.utils.unittest import skipUnless
@ -26,7 +28,7 @@ class TestUtilsHashPass(unittest.TestCase):
encoded = make_password('letmein') encoded = make_password('letmein')
self.assertTrue(encoded.startswith('pbkdf2_sha256$')) self.assertTrue(encoded.startswith('pbkdf2_sha256$'))
self.assertTrue(is_password_usable(encoded)) self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded)) self.assertTrue(check_password('letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded)) self.assertFalse(check_password('letmeinz', encoded))
def test_pkbdf2(self): def test_pkbdf2(self):
@ -34,47 +36,53 @@ class TestUtilsHashPass(unittest.TestCase):
self.assertEqual(encoded, self.assertEqual(encoded,
'pbkdf2_sha256$10000$seasalt$FQCNpiZpTb0zub+HBsH6TOwyRxJ19FwvjbweatNmK/Y=') 'pbkdf2_sha256$10000$seasalt$FQCNpiZpTb0zub+HBsH6TOwyRxJ19FwvjbweatNmK/Y=')
self.assertTrue(is_password_usable(encoded)) self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded)) self.assertTrue(check_password('letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded)) self.assertFalse(check_password('letmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "pbkdf2_sha256")
def test_sha1(self): def test_sha1(self):
encoded = make_password('letmein', 'seasalt', 'sha1') encoded = make_password('letmein', 'seasalt', 'sha1')
self.assertEqual(encoded, self.assertEqual(encoded,
'sha1$seasalt$fec3530984afba6bade3347b7140d1a7da7da8c7') 'sha1$seasalt$fec3530984afba6bade3347b7140d1a7da7da8c7')
self.assertTrue(is_password_usable(encoded)) self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded)) self.assertTrue(check_password('letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded)) self.assertFalse(check_password('letmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "sha1")
def test_md5(self): def test_md5(self):
encoded = make_password('letmein', 'seasalt', 'md5') encoded = make_password('letmein', 'seasalt', 'md5')
self.assertEqual(encoded, self.assertEqual(encoded,
'md5$seasalt$f5531bef9f3687d0ccf0f617f0e25573') 'md5$seasalt$f5531bef9f3687d0ccf0f617f0e25573')
self.assertTrue(is_password_usable(encoded)) self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded)) self.assertTrue(check_password('letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded)) self.assertFalse(check_password('letmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "md5")
def test_unsalted_md5(self): def test_unsalted_md5(self):
encoded = make_password('letmein', 'seasalt', 'unsalted_md5') encoded = make_password('letmein', 'seasalt', 'unsalted_md5')
self.assertEqual(encoded, '0d107d09f5bbe40cade3de5c71e9e9b7') self.assertEqual(encoded, '0d107d09f5bbe40cade3de5c71e9e9b7')
self.assertTrue(is_password_usable(encoded)) self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded)) self.assertTrue(check_password('letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded)) self.assertFalse(check_password('letmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "unsalted_md5")
@skipUnless(crypt, "no crypt module to generate password.") @skipUnless(crypt, "no crypt module to generate password.")
def test_crypt(self): def test_crypt(self):
encoded = make_password('letmein', 'ab', 'crypt') encoded = make_password('letmein', 'ab', 'crypt')
self.assertEqual(encoded, 'crypt$$abN/qM.L/H8EQ') self.assertEqual(encoded, 'crypt$$abN/qM.L/H8EQ')
self.assertTrue(is_password_usable(encoded)) self.assertTrue(is_password_usable(encoded))
self.assertTrue(check_password(u'letmein', encoded)) self.assertTrue(check_password('letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded)) self.assertFalse(check_password('letmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "crypt")
@skipUnless(bcrypt, "py-bcrypt not installed") @skipUnless(bcrypt, "py-bcrypt not installed")
def test_bcrypt(self): def test_bcrypt(self):
encoded = make_password('letmein', hasher='bcrypt') encoded = make_password('letmein', hasher='bcrypt')
self.assertTrue(is_password_usable(encoded)) self.assertTrue(is_password_usable(encoded))
self.assertTrue(encoded.startswith('bcrypt$')) self.assertTrue(encoded.startswith('bcrypt$'))
self.assertTrue(check_password(u'letmein', encoded)) self.assertTrue(check_password('letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded)) self.assertFalse(check_password('letmeinz', encoded))
self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt")
def test_unusable(self): def test_unusable(self):
encoded = make_password(None) encoded = make_password(None)
@ -82,13 +90,15 @@ class TestUtilsHashPass(unittest.TestCase):
self.assertFalse(check_password(None, encoded)) self.assertFalse(check_password(None, encoded))
self.assertFalse(check_password(UNUSABLE_PASSWORD, encoded)) self.assertFalse(check_password(UNUSABLE_PASSWORD, encoded))
self.assertFalse(check_password('', encoded)) self.assertFalse(check_password('', encoded))
self.assertFalse(check_password(u'letmein', encoded)) self.assertFalse(check_password('letmein', encoded))
self.assertFalse(check_password('letmeinz', encoded)) self.assertFalse(check_password('letmeinz', encoded))
self.assertRaises(ValueError, identify_hasher, encoded)
def test_bad_algorithm(self): def test_bad_algorithm(self):
def doit(): def doit():
make_password('letmein', hasher='lolcat') make_password('letmein', hasher='lolcat')
self.assertRaises(ValueError, doit) self.assertRaises(ValueError, doit)
self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash")
def test_low_level_pkbdf2(self): def test_low_level_pkbdf2(self):
hasher = PBKDF2PasswordHasher() hasher = PBKDF2PasswordHasher()

View File

@ -1,5 +1,5 @@
from __future__ import unicode_literals
from datetime import date from datetime import date
from StringIO import StringIO
from django.contrib.auth import models, management from django.contrib.auth import models, management
from django.contrib.auth.management.commands import changepassword from django.contrib.auth.management.commands import changepassword
@ -9,6 +9,7 @@ from django.core.management import call_command
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from django.utils.six import StringIO
class GetDefaultUsernameTestCase(TestCase): class GetDefaultUsernameTestCase(TestCase):
@ -20,19 +21,19 @@ class GetDefaultUsernameTestCase(TestCase):
management.get_system_username = self._getpass_getuser management.get_system_username = self._getpass_getuser
def test_simple(self): def test_simple(self):
management.get_system_username = lambda: u'joe' management.get_system_username = lambda: 'joe'
self.assertEqual(management.get_default_username(), 'joe') self.assertEqual(management.get_default_username(), 'joe')
def test_existing(self): def test_existing(self):
models.User.objects.create(username='joe') models.User.objects.create(username='joe')
management.get_system_username = lambda: u'joe' management.get_system_username = lambda: 'joe'
self.assertEqual(management.get_default_username(), '') self.assertEqual(management.get_default_username(), '')
self.assertEqual( self.assertEqual(
management.get_default_username(check_db=False), 'joe') management.get_default_username(check_db=False), 'joe')
def test_i18n(self): def test_i18n(self):
# 'Julia' with accented 'u': # 'Julia' with accented 'u':
management.get_system_username = lambda: u'J\xfalia' management.get_system_username = lambda: 'J\xfalia'
self.assertEqual(management.get_default_username(), 'julia') self.assertEqual(management.get_default_username(), 'julia')

View File

@ -5,39 +5,29 @@ from django.contrib.auth.models import (Group, User,
SiteProfileNotAvailable, UserManager) SiteProfileNotAvailable, UserManager)
@override_settings(USE_TZ=False) @override_settings(USE_TZ=False, AUTH_PROFILE_MODULE='')
class ProfileTestCase(TestCase): class ProfileTestCase(TestCase):
fixtures = ['authtestdata.json']
def setUp(self):
"""Backs up the AUTH_PROFILE_MODULE"""
self.old_AUTH_PROFILE_MODULE = getattr(settings,
'AUTH_PROFILE_MODULE', None)
def tearDown(self):
"""Restores the AUTH_PROFILE_MODULE -- if it was not set it is deleted,
otherwise the old value is restored"""
if self.old_AUTH_PROFILE_MODULE is None and \
hasattr(settings, 'AUTH_PROFILE_MODULE'):
del settings.AUTH_PROFILE_MODULE
if self.old_AUTH_PROFILE_MODULE is not None:
settings.AUTH_PROFILE_MODULE = self.old_AUTH_PROFILE_MODULE
def test_site_profile_not_available(self): def test_site_profile_not_available(self):
user = User.objects.create(username='testclient')
# calling get_profile without AUTH_PROFILE_MODULE set # calling get_profile without AUTH_PROFILE_MODULE set
if hasattr(settings, 'AUTH_PROFILE_MODULE'): del settings.AUTH_PROFILE_MODULE
del settings.AUTH_PROFILE_MODULE with self.assertRaisesRegexp(SiteProfileNotAvailable,
user = User.objects.get(username='testclient') "You need to set AUTH_PROFILE_MODULE in your project"):
self.assertRaises(SiteProfileNotAvailable, user.get_profile) user.get_profile()
# Bad syntax in AUTH_PROFILE_MODULE: # Bad syntax in AUTH_PROFILE_MODULE:
settings.AUTH_PROFILE_MODULE = 'foobar' settings.AUTH_PROFILE_MODULE = 'foobar'
self.assertRaises(SiteProfileNotAvailable, user.get_profile) with self.assertRaisesRegexp(SiteProfileNotAvailable,
"app_label and model_name should be separated by a dot"):
user.get_profile()
# module that doesn't exist # module that doesn't exist
settings.AUTH_PROFILE_MODULE = 'foo.bar' settings.AUTH_PROFILE_MODULE = 'foo.bar'
self.assertRaises(SiteProfileNotAvailable, user.get_profile) with self.assertRaisesRegexp(SiteProfileNotAvailable,
"Unable to load the profile model"):
user.get_profile()
@override_settings(USE_TZ=False) @override_settings(USE_TZ=False)

View File

@ -1,9 +1,11 @@
import sys
from datetime import date, timedelta from datetime import date, timedelta
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.test import TestCase from django.test import TestCase
from django.utils import unittest
class TokenGeneratorTest(TestCase): class TokenGeneratorTest(TestCase):
@ -51,6 +53,7 @@ class TokenGeneratorTest(TestCase):
p2 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS + 1)) p2 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS + 1))
self.assertFalse(p2.check_token(user, tk1)) self.assertFalse(p2.check_token(user, tk1))
@unittest.skipIf(sys.version_info[:2] >= (3, 0), "Unnecessary test with Python 3")
def test_date_length(self): def test_date_length(self):
""" """
Make sure we don't allow overly long dates, causing a potential DoS. Make sure we don't allow overly long dates, causing a potential DoS.

View File

@ -1,6 +1,5 @@
import os import os
import re import re
import urllib
from django.conf import settings from django.conf import settings
from django.contrib.sites.models import Site, RequestSite from django.contrib.sites.models import Site, RequestSite
@ -8,8 +7,9 @@ from django.contrib.auth.models import User
from django.core import mail from django.core import mail
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse, NoReverseMatch
from django.http import QueryDict from django.http import QueryDict
from django.utils.encoding import force_unicode from django.utils.encoding import force_text
from django.utils.html import escape from django.utils.html import escape
from django.utils.http import urlquote
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
@ -46,7 +46,7 @@ class AuthViewsTestCase(TestCase):
self.assertTrue(SESSION_KEY in self.client.session) self.assertTrue(SESSION_KEY in self.client.session)
def assertContainsEscaped(self, response, text, **kwargs): def assertContainsEscaped(self, response, text, **kwargs):
return self.assertContains(response, escape(force_unicode(text)), **kwargs) return self.assertContains(response, escape(force_text(text)), **kwargs)
class AuthViewNamedURLTests(AuthViewsTestCase): class AuthViewNamedURLTests(AuthViewsTestCase):
@ -256,7 +256,7 @@ class LoginTest(AuthViewsTestCase):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % { nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': login_url, 'url': login_url,
'next': REDIRECT_FIELD_NAME, 'next': REDIRECT_FIELD_NAME,
'bad_url': urllib.quote(bad_url), 'bad_url': urlquote(bad_url),
} }
response = self.client.post(nasty_url, { response = self.client.post(nasty_url, {
'username': 'testclient', 'username': 'testclient',
@ -277,7 +277,7 @@ class LoginTest(AuthViewsTestCase):
safe_url = '%(url)s?%(next)s=%(good_url)s' % { safe_url = '%(url)s?%(next)s=%(good_url)s' % {
'url': login_url, 'url': login_url,
'next': REDIRECT_FIELD_NAME, 'next': REDIRECT_FIELD_NAME,
'good_url': urllib.quote(good_url), 'good_url': urlquote(good_url),
} }
response = self.client.post(safe_url, { response = self.client.post(safe_url, {
'username': 'testclient', 'username': 'testclient',
@ -412,7 +412,7 @@ class LogoutTest(AuthViewsTestCase):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % { nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': logout_url, 'url': logout_url,
'next': REDIRECT_FIELD_NAME, 'next': REDIRECT_FIELD_NAME,
'bad_url': urllib.quote(bad_url), 'bad_url': urlquote(bad_url),
} }
self.login() self.login()
response = self.client.get(nasty_url) response = self.client.get(nasty_url)
@ -432,7 +432,7 @@ class LogoutTest(AuthViewsTestCase):
safe_url = '%(url)s?%(next)s=%(good_url)s' % { safe_url = '%(url)s?%(next)s=%(good_url)s' % {
'url': logout_url, 'url': logout_url,
'next': REDIRECT_FIELD_NAME, 'next': REDIRECT_FIELD_NAME,
'good_url': urllib.quote(good_url), 'good_url': urlquote(good_url),
} }
self.login() self.login()
response = self.client.get(safe_url) response = self.client.get(safe_url)

View File

@ -2,6 +2,7 @@ from datetime import date
from django.conf import settings from django.conf import settings
from django.utils.http import int_to_base36, base36_to_int from django.utils.http import int_to_base36, base36_to_int
from django.utils.crypto import constant_time_compare, salted_hmac from django.utils.crypto import constant_time_compare, salted_hmac
from django.utils import six
class PasswordResetTokenGenerator(object): class PasswordResetTokenGenerator(object):
@ -57,8 +58,8 @@ class PasswordResetTokenGenerator(object):
# Ensure results are consistent across DB backends # Ensure results are consistent across DB backends
login_timestamp = user.last_login.replace(microsecond=0, tzinfo=None) login_timestamp = user.last_login.replace(microsecond=0, tzinfo=None)
value = (unicode(user.id) + user.password + value = (six.text_type(user.id) + user.password +
unicode(login_timestamp) + unicode(timestamp)) six.text_type(login_timestamp) + six.text_type(timestamp))
hash = salted_hmac(key_salt, value).hexdigest()[::2] hash = salted_hmac(key_salt, value).hexdigest()[::2]
return "%s-%s" % (ts_b36, hash) return "%s-%s" % (ts_b36, hash)

View File

@ -1,9 +1,13 @@
import urlparse try:
from urllib.parse import urlparse, urlunparse
except ImportError: # Python 2
from urlparse import urlparse, urlunparse
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, QueryDict from django.http import HttpResponseRedirect, QueryDict
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.encoding import force_str
from django.utils.http import base36_to_int from django.utils.http import base36_to_int
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.debug import sensitive_post_parameters from django.views.decorators.debug import sensitive_post_parameters
@ -34,7 +38,7 @@ def login(request, template_name='registration/login.html',
if request.method == "POST": if request.method == "POST":
form = authentication_form(data=request.POST) form = authentication_form(data=request.POST)
if form.is_valid(): if form.is_valid():
netloc = urlparse.urlparse(redirect_to)[1] netloc = urlparse(redirect_to)[1]
# Use default setting if redirect_to is empty # Use default setting if redirect_to is empty
if not redirect_to: if not redirect_to:
@ -81,7 +85,7 @@ def logout(request, next_page=None,
auth_logout(request) auth_logout(request)
redirect_to = request.REQUEST.get(redirect_field_name, '') redirect_to = request.REQUEST.get(redirect_field_name, '')
if redirect_to: if redirect_to:
netloc = urlparse.urlparse(redirect_to)[1] netloc = urlparse(redirect_to)[1]
# Security check -- don't allow redirection to a different host. # Security check -- don't allow redirection to a different host.
if not (netloc and netloc != request.get_host()): if not (netloc and netloc != request.get_host()):
return HttpResponseRedirect(redirect_to) return HttpResponseRedirect(redirect_to)
@ -116,16 +120,16 @@ def redirect_to_login(next, login_url=None,
""" """
Redirects the user to the login page, passing the given 'next' page Redirects the user to the login page, passing the given 'next' page
""" """
if not login_url: # urlparse chokes on lazy objects in Python 3
login_url = settings.LOGIN_URL login_url_as_str = force_str(login_url or settings.LOGIN_URL)
login_url_parts = list(urlparse.urlparse(login_url)) login_url_parts = list(urlparse(login_url_as_str))
if redirect_field_name: if redirect_field_name:
querystring = QueryDict(login_url_parts[4], mutable=True) querystring = QueryDict(login_url_parts[4], mutable=True)
querystring[redirect_field_name] = next querystring[redirect_field_name] = next
login_url_parts[4] = querystring.urlencode(safe='/') login_url_parts[4] = querystring.urlencode(safe='/')
return HttpResponseRedirect(urlparse.urlunparse(login_url_parts)) return HttpResponseRedirect(urlunparse(login_url_parts))
# 4 views for password reset: # 4 views for password reset:
@ -203,7 +207,7 @@ def password_reset_confirm(request, uidb36=None, token=None,
try: try:
uid_int = base36_to_int(uidb36) uid_int = base36_to_int(uidb36)
user = User.objects.get(id=uid_int) user = User.objects.get(id=uid_int)
except (ValueError, User.DoesNotExist): except (ValueError, OverflowError, User.DoesNotExist):
user = None user = None
if user is not None and token_generator.check_token(user, token): if user is not None and token_generator.check_token(user, token):

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from django.contrib import admin from django.contrib import admin
from django.contrib.comments.models import Comment from django.contrib.comments.models import Comment
from django.utils.translation import ugettext_lazy as _, ungettext from django.utils.translation import ugettext_lazy as _, ungettext
@ -62,8 +64,8 @@ class CommentsAdmin(admin.ModelAdmin):
action(request, comment) action(request, comment)
n_comments += 1 n_comments += 1
msg = ungettext(u'1 comment was successfully %(action)s.', msg = ungettext('1 comment was successfully %(action)s.',
u'%(count)s comments were successfully %(action)s.', '%(count)s comments were successfully %(action)s.',
n_comments) n_comments)
self.message_user(request, msg % {'count': n_comments, 'action': done_message(n_comments)}) self.message_user(request, msg % {'count': n_comments, 'action': done_message(n_comments)})

View File

@ -5,7 +5,7 @@ from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.comments.models import Comment from django.contrib.comments.models import Comment
from django.utils.crypto import salted_hmac, constant_time_compare from django.utils.crypto import salted_hmac, constant_time_compare
from django.utils.encoding import force_unicode from django.utils.encoding import force_text
from django.utils.text import get_text_list from django.utils.text import get_text_list
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ungettext, ugettext, ugettext_lazy as _ from django.utils.translation import ungettext, ugettext, ugettext_lazy as _
@ -133,7 +133,7 @@ class CommentDetailsForm(CommentSecurityForm):
""" """
return dict( return dict(
content_type = ContentType.objects.get_for_model(self.target_object), content_type = ContentType.objects.get_for_model(self.target_object),
object_pk = force_unicode(self.target_object._get_pk_val()), object_pk = force_text(self.target_object._get_pk_val()),
user_name = self.cleaned_data["name"], user_name = self.cleaned_data["name"],
user_email = self.cleaned_data["email"], user_email = self.cleaned_data["email"],
user_url = self.cleaned_data["url"], user_url = self.cleaned_data["url"],

View File

@ -1,6 +1,6 @@
from django.db import models from django.db import models
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import force_unicode from django.utils.encoding import force_text
class CommentManager(models.Manager): class CommentManager(models.Manager):
@ -18,5 +18,5 @@ class CommentManager(models.Manager):
ct = ContentType.objects.get_for_model(model) ct = ContentType.objects.get_for_model(model)
qs = self.get_query_set().filter(content_type=ct) qs = self.get_query_set().filter(content_type=ct)
if isinstance(model, models.Model): if isinstance(model, models.Model):
qs = qs.filter(object_pk=force_unicode(model._get_pk_val())) qs = qs.filter(object_pk=force_text(model._get_pk_val()))
return qs return qs

View File

@ -7,6 +7,7 @@ from django.core import urlresolvers
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
COMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000) COMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000)
@ -40,6 +41,7 @@ class BaseCommentAbstractModel(models.Model):
) )
@python_2_unicode_compatible
class Comment(BaseCommentAbstractModel): class Comment(BaseCommentAbstractModel):
""" """
A user comment about some object. A user comment about some object.
@ -77,7 +79,7 @@ class Comment(BaseCommentAbstractModel):
verbose_name = _('comment') verbose_name = _('comment')
verbose_name_plural = _('comments') verbose_name_plural = _('comments')
def __unicode__(self): def __str__(self):
return "%s: %s..." % (self.name, self.comment[:50]) return "%s: %s..." % (self.name, self.comment[:50])
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
@ -158,6 +160,7 @@ class Comment(BaseCommentAbstractModel):
return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d
@python_2_unicode_compatible
class CommentFlag(models.Model): class CommentFlag(models.Model):
""" """
Records a flag on a comment. This is intentionally flexible; right now, a Records a flag on a comment. This is intentionally flexible; right now, a
@ -187,7 +190,7 @@ class CommentFlag(models.Model):
verbose_name = _('comment flag') verbose_name = _('comment flag')
verbose_name_plural = _('comment flags') verbose_name_plural = _('comment flags')
def __unicode__(self): def __str__(self):
return "%s flag of comment ID %s by %s" % \ return "%s flag of comment ID %s by %s" % \
(self.flag, self.comment_id, self.user.username) (self.flag, self.comment_id, self.user.username)

View File

@ -3,7 +3,7 @@ from django.template.loader import render_to_string
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib import comments from django.contrib import comments
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_text
register = template.Library() register = template.Library()
@ -75,7 +75,7 @@ class BaseCommentNode(template.Node):
qs = self.comment_model.objects.filter( qs = self.comment_model.objects.filter(
content_type = ctype, content_type = ctype,
object_pk = smart_unicode(object_pk), object_pk = smart_text(object_pk),
site__pk = settings.SITE_ID, site__pk = settings.SITE_ID,
) )

View File

@ -17,7 +17,7 @@ def flag(request, comment_id, next=None):
""" """
Flags a comment. Confirmation on GET, action on POST. Flags a comment. Confirmation on GET, action on POST.
Templates: `comments/flag.html`, Templates: :template:`comments/flag.html`,
Context: Context:
comment comment
the flagged `comments.comment` object the flagged `comments.comment` object
@ -43,7 +43,7 @@ def delete(request, comment_id, next=None):
Deletes a comment. Confirmation on GET, action on POST. Requires the "can Deletes a comment. Confirmation on GET, action on POST. Requires the "can
moderate comments" permission. moderate comments" permission.
Templates: `comments/delete.html`, Templates: :template:`comments/delete.html`,
Context: Context:
comment comment
the flagged `comments.comment` object the flagged `comments.comment` object
@ -70,7 +70,7 @@ def approve(request, comment_id, next=None):
Approve a comment (that is, mark it as public and non-removed). Confirmation Approve a comment (that is, mark it as public and non-removed). Confirmation
on GET, action on POST. Requires the "can moderate comments" permission. on GET, action on POST. Requires the "can moderate comments" permission.
Templates: `comments/approve.html`, Templates: :template:`comments/approve.html`,
Context: Context:
comment comment
the `comments.comment` object for approval the `comments.comment` object for approval

View File

@ -2,8 +2,12 @@
A few bits of helper functions for comment views. A few bits of helper functions for comment views.
""" """
import urllib
import textwrap import textwrap
try:
from urllib.parse import urlencode
except ImportError: # Python 2
from urllib import urlencode
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.core import urlresolvers from django.core import urlresolvers
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
@ -33,7 +37,7 @@ def next_redirect(data, default, default_view, **get_kwargs):
anchor = '' anchor = ''
joiner = ('?' in next) and '&' or '?' joiner = ('?' in next) and '&' or '?'
next += joiner + urllib.urlencode(get_kwargs) + anchor next += joiner + urlencode(get_kwargs) + anchor
return HttpResponseRedirect(next) return HttpResponseRedirect(next)
def confirmation_view(template, doc="Display a confirmation view."): def confirmation_view(template, doc="Display a confirmation view."):
@ -56,7 +60,7 @@ def confirmation_view(template, doc="Display a confirmation view."):
confirmed.__doc__ = textwrap.dedent("""\ confirmed.__doc__ = textwrap.dedent("""\
%s %s
Templates: `%s`` Templates: :template:`%s``
Context: Context:
comment comment
The posted comment The posted comment

View File

@ -1,6 +1,7 @@
""" """
Classes allowing "generic" relations through ContentType and object-id fields. Classes allowing "generic" relations through ContentType and object-id fields.
""" """
from __future__ import unicode_literals
from collections import defaultdict from collections import defaultdict
from functools import partial from functools import partial
@ -16,7 +17,7 @@ from django.forms import ModelForm
from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance from django.forms.models import BaseModelFormSet, modelformset_factory, save_instance
from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_text
class GenericForeignKey(object): class GenericForeignKey(object):
""" """
@ -131,7 +132,7 @@ class GenericForeignKey(object):
def __set__(self, instance, value): def __set__(self, instance, value):
if instance is None: if instance is None:
raise AttributeError(u"%s must be accessed via instance" % self.related.opts.object_name) raise AttributeError("%s must be accessed via instance" % self.related.opts.object_name)
ct = None ct = None
fk = None fk = None
@ -168,7 +169,7 @@ class GenericRelation(RelatedField, Field):
def value_to_string(self, obj): def value_to_string(self, obj):
qs = getattr(obj, self.name).all() qs = getattr(obj, self.name).all()
return smart_unicode([instance._get_pk_val() for instance in qs]) return smart_text([instance._get_pk_val() for instance in qs])
def m2m_db_table(self): def m2m_db_table(self):
return self.rel.to._meta.db_table return self.rel.to._meta.db_table

View File

@ -1,6 +1,8 @@
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import get_apps, get_models, signals from django.db.models import get_apps, get_models, signals
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_text
from django.utils import six
from django.utils.six.moves import input
def update_contenttypes(app, created_models, verbosity=2, **kwargs): def update_contenttypes(app, created_models, verbosity=2, **kwargs):
""" """
@ -24,17 +26,17 @@ def update_contenttypes(app, created_models, verbosity=2, **kwargs):
) )
to_remove = [ to_remove = [
ct ct
for (model_name, ct) in content_types.iteritems() for (model_name, ct) in six.iteritems(content_types)
if model_name not in app_models if model_name not in app_models
] ]
cts = ContentType.objects.bulk_create([ cts = ContentType.objects.bulk_create([
ContentType( ContentType(
name=smart_unicode(model._meta.verbose_name_raw), name=smart_text(model._meta.verbose_name_raw),
app_label=app_label, app_label=app_label,
model=model_name, model=model_name,
) )
for (model_name, model) in app_models.iteritems() for (model_name, model) in six.iteritems(app_models)
if model_name not in content_types if model_name not in content_types
]) ])
if verbosity >= 2: if verbosity >= 2:
@ -48,7 +50,7 @@ def update_contenttypes(app, created_models, verbosity=2, **kwargs):
' %s | %s' % (ct.app_label, ct.model) ' %s | %s' % (ct.app_label, ct.model)
for ct in to_remove for ct in to_remove
]) ])
ok_to_delete = raw_input("""The following content types are stale and need to be deleted: ok_to_delete = input("""The following content types are stale and need to be deleted:
%s %s

View File

@ -1,6 +1,7 @@
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode, force_unicode from django.utils.encoding import smart_text, force_text
from django.utils.encoding import python_2_unicode_compatible
class ContentTypeManager(models.Manager): class ContentTypeManager(models.Manager):
@ -16,39 +17,44 @@ class ContentTypeManager(models.Manager):
self._add_to_cache(self.db, ct) self._add_to_cache(self.db, ct)
return ct return ct
def _get_opts(self, model): def _get_opts(self, model, for_concrete_model):
return model._meta.concrete_model._meta if for_concrete_model:
model = model._meta.concrete_model
elif model._deferred:
model = model._meta.proxy_for_model
return model._meta
def _get_from_cache(self, opts): def _get_from_cache(self, opts):
key = (opts.app_label, opts.object_name.lower()) key = (opts.app_label, opts.object_name.lower())
return self.__class__._cache[self.db][key] return self.__class__._cache[self.db][key]
def get_for_model(self, model): def get_for_model(self, model, for_concrete_model=True):
""" """
Returns the ContentType object for a given model, creating the Returns the ContentType object for a given model, creating the
ContentType if necessary. Lookups are cached so that subsequent lookups ContentType if necessary. Lookups are cached so that subsequent lookups
for the same model don't hit the database. for the same model don't hit the database.
""" """
opts = self._get_opts(model) opts = self._get_opts(model, for_concrete_model)
try: try:
ct = self._get_from_cache(opts) ct = self._get_from_cache(opts)
except KeyError: except KeyError:
# Load or create the ContentType entry. The smart_unicode() is # Load or create the ContentType entry. The smart_text() is
# needed around opts.verbose_name_raw because name_raw might be a # needed around opts.verbose_name_raw because name_raw might be a
# django.utils.functional.__proxy__ object. # django.utils.functional.__proxy__ object.
ct, created = self.get_or_create( ct, created = self.get_or_create(
app_label = opts.app_label, app_label = opts.app_label,
model = opts.object_name.lower(), model = opts.object_name.lower(),
defaults = {'name': smart_unicode(opts.verbose_name_raw)}, defaults = {'name': smart_text(opts.verbose_name_raw)},
) )
self._add_to_cache(self.db, ct) self._add_to_cache(self.db, ct)
return ct return ct
def get_for_models(self, *models): def get_for_models(self, *models, **kwargs):
""" """
Given *models, returns a dictionary mapping {model: content_type}. Given *models, returns a dictionary mapping {model: content_type}.
""" """
for_concrete_models = kwargs.pop('for_concrete_models', True)
# Final results # Final results
results = {} results = {}
# models that aren't already in the cache # models that aren't already in the cache
@ -56,7 +62,7 @@ class ContentTypeManager(models.Manager):
needed_models = set() needed_models = set()
needed_opts = set() needed_opts = set()
for model in models: for model in models:
opts = self._get_opts(model) opts = self._get_opts(model, for_concrete_models)
try: try:
ct = self._get_from_cache(opts) ct = self._get_from_cache(opts)
except KeyError: except KeyError:
@ -81,7 +87,7 @@ class ContentTypeManager(models.Manager):
ct = self.create( ct = self.create(
app_label=opts.app_label, app_label=opts.app_label,
model=opts.object_name.lower(), model=opts.object_name.lower(),
name=smart_unicode(opts.verbose_name_raw), name=smart_text(opts.verbose_name_raw),
) )
self._add_to_cache(self.db, ct) self._add_to_cache(self.db, ct)
results[ct.model_class()] = ct results[ct.model_class()] = ct
@ -117,6 +123,7 @@ class ContentTypeManager(models.Manager):
self.__class__._cache.setdefault(using, {})[key] = ct self.__class__._cache.setdefault(using, {})[key] = ct
self.__class__._cache.setdefault(using, {})[ct.id] = ct self.__class__._cache.setdefault(using, {})[ct.id] = ct
@python_2_unicode_compatible
class ContentType(models.Model): class ContentType(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
app_label = models.CharField(max_length=100) app_label = models.CharField(max_length=100)
@ -130,7 +137,7 @@ class ContentType(models.Model):
ordering = ('name',) ordering = ('name',)
unique_together = (('app_label', 'model'),) unique_together = (('app_label', 'model'),)
def __unicode__(self): def __str__(self):
# self.name is deprecated in favor of using model's verbose_name, which # self.name is deprecated in favor of using model's verbose_name, which
# can be translated. Formal deprecation is delayed until we have DB # can be translated. Formal deprecation is delayed until we have DB
# migration to be able to remove the field from the database along with # migration to be able to remove the field from the database along with
@ -142,7 +149,7 @@ class ContentType(models.Model):
if not model or self.name != model._meta.verbose_name_raw: if not model or self.name != model._meta.verbose_name_raw:
return self.name return self.name
else: else:
return force_unicode(model._meta.verbose_name) return force_text(model._meta.verbose_name)
def model_class(self): def model_class(self):
"Returns the Python model class for this type of content." "Returns the Python model class for this type of content."

View File

@ -1,4 +1,4 @@
import urllib from __future__ import unicode_literals
from django.db import models from django.db import models
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -6,16 +6,26 @@ from django.contrib.contenttypes.views import shortcut
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.http import HttpRequest, Http404 from django.http import HttpRequest, Http404
from django.test import TestCase from django.test import TestCase
from django.utils.encoding import smart_str from django.utils.http import urlquote
from django.utils import six
from django.utils.encoding import python_2_unicode_compatible
class ConcreteModel(models.Model):
name = models.CharField(max_length=10)
class ProxyModel(ConcreteModel):
class Meta:
proxy = True
@python_2_unicode_compatible
class FooWithoutUrl(models.Model): class FooWithoutUrl(models.Model):
""" """
Fake model not defining ``get_absolute_url`` for Fake model not defining ``get_absolute_url`` for
:meth:`ContentTypesTests.test_shortcut_view_without_get_absolute_url`""" :meth:`ContentTypesTests.test_shortcut_view_without_get_absolute_url`"""
name = models.CharField(max_length=30, unique=True) name = models.CharField(max_length=30, unique=True)
def __unicode__(self): def __str__(self):
return self.name return self.name
@ -26,7 +36,7 @@ class FooWithUrl(FooWithoutUrl):
""" """
def get_absolute_url(self): def get_absolute_url(self):
return "/users/%s/" % urllib.quote(smart_str(self.name)) return "/users/%s/" % urlquote(self.name)
class FooWithBrokenAbsoluteUrl(FooWithoutUrl): class FooWithBrokenAbsoluteUrl(FooWithoutUrl):
""" """
@ -112,6 +122,87 @@ class ContentTypesTests(TestCase):
FooWithUrl: ContentType.objects.get_for_model(FooWithUrl), FooWithUrl: ContentType.objects.get_for_model(FooWithUrl),
}) })
def test_get_for_concrete_model(self):
"""
Make sure the `for_concrete_model` kwarg correctly works
with concrete, proxy and deferred models
"""
concrete_model_ct = ContentType.objects.get_for_model(ConcreteModel)
self.assertEqual(concrete_model_ct,
ContentType.objects.get_for_model(ProxyModel))
self.assertEqual(concrete_model_ct,
ContentType.objects.get_for_model(ConcreteModel,
for_concrete_model=False))
proxy_model_ct = ContentType.objects.get_for_model(ProxyModel,
for_concrete_model=False)
self.assertNotEqual(concrete_model_ct, proxy_model_ct)
# Make sure deferred model are correctly handled
ConcreteModel.objects.create(name="Concrete")
DeferredConcreteModel = ConcreteModel.objects.only('pk').get().__class__
DeferredProxyModel = ProxyModel.objects.only('pk').get().__class__
self.assertEqual(concrete_model_ct,
ContentType.objects.get_for_model(DeferredConcreteModel))
self.assertEqual(concrete_model_ct,
ContentType.objects.get_for_model(DeferredConcreteModel,
for_concrete_model=False))
self.assertEqual(concrete_model_ct,
ContentType.objects.get_for_model(DeferredProxyModel))
self.assertEqual(proxy_model_ct,
ContentType.objects.get_for_model(DeferredProxyModel,
for_concrete_model=False))
def test_get_for_concrete_models(self):
"""
Make sure the `for_concrete_models` kwarg correctly works
with concrete, proxy and deferred models.
"""
concrete_model_ct = ContentType.objects.get_for_model(ConcreteModel)
cts = ContentType.objects.get_for_models(ConcreteModel, ProxyModel)
self.assertEqual(cts, {
ConcreteModel: concrete_model_ct,
ProxyModel: concrete_model_ct,
})
proxy_model_ct = ContentType.objects.get_for_model(ProxyModel,
for_concrete_model=False)
cts = ContentType.objects.get_for_models(ConcreteModel, ProxyModel,
for_concrete_models=False)
self.assertEqual(cts, {
ConcreteModel: concrete_model_ct,
ProxyModel: proxy_model_ct,
})
# Make sure deferred model are correctly handled
ConcreteModel.objects.create(name="Concrete")
DeferredConcreteModel = ConcreteModel.objects.only('pk').get().__class__
DeferredProxyModel = ProxyModel.objects.only('pk').get().__class__
cts = ContentType.objects.get_for_models(DeferredConcreteModel,
DeferredProxyModel)
self.assertEqual(cts, {
DeferredConcreteModel: concrete_model_ct,
DeferredProxyModel: concrete_model_ct,
})
cts = ContentType.objects.get_for_models(DeferredConcreteModel,
DeferredProxyModel,
for_concrete_models=False)
self.assertEqual(cts, {
DeferredConcreteModel: concrete_model_ct,
DeferredProxyModel: proxy_model_ct,
})
def test_shortcut_view(self): def test_shortcut_view(self):
""" """
Check that the shortcut view (used for the admin "view on site" Check that the shortcut view (used for the admin "view on site"
@ -181,4 +272,4 @@ class ContentTypesTests(TestCase):
app_label = 'contenttypes', app_label = 'contenttypes',
model = 'OldModel', model = 'OldModel',
) )
self.assertEqual(unicode(ct), u'Old model') self.assertEqual(six.text_type(ct), 'Old model')

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from django import http from django import http
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site, get_current_site from django.contrib.sites.models import Site, get_current_site
@ -12,11 +14,11 @@ def shortcut(request, content_type_id, object_id):
try: try:
content_type = ContentType.objects.get(pk=content_type_id) content_type = ContentType.objects.get(pk=content_type_id)
if not content_type.model_class(): if not content_type.model_class():
raise http.Http404(_(u"Content type %(ct_id)s object has no associated model") % raise http.Http404(_("Content type %(ct_id)s object has no associated model") %
{'ct_id': content_type_id}) {'ct_id': content_type_id})
obj = content_type.get_object_for_this_type(pk=object_id) obj = content_type.get_object_for_this_type(pk=object_id)
except (ObjectDoesNotExist, ValueError): except (ObjectDoesNotExist, ValueError):
raise http.Http404(_(u"Content type %(ct_id)s object %(obj_id)s doesn't exist") % raise http.Http404(_("Content type %(ct_id)s object %(obj_id)s doesn't exist") %
{'ct_id': content_type_id, 'obj_id': object_id}) {'ct_id': content_type_id, 'obj_id': object_id})
try: try:

View File

@ -2,13 +2,14 @@
These classes are light wrappers around Django's database API that provide These classes are light wrappers around Django's database API that provide
convenience functionality and permalink functions for the databrowse app. convenience functionality and permalink functions for the databrowse app.
""" """
from __future__ import unicode_literals
from django.db import models from django.db import models
from django.utils import formats from django.utils import formats
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.encoding import smart_unicode, smart_str, iri_to_uri from django.utils.encoding import smart_text, smart_str, iri_to_uri
from django.utils.safestring import mark_safe
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.utils.encoding import python_2_unicode_compatible
EMPTY_VALUE = '(None)' EMPTY_VALUE = '(None)'
DISPLAY_SIZE = 100 DISPLAY_SIZE = 100
@ -17,19 +18,19 @@ class EasyModel(object):
def __init__(self, site, model): def __init__(self, site, model):
self.site = site self.site = site
self.model = model self.model = model
self.model_list = site.registry.keys() self.model_list = list(site.registry.keys())
self.verbose_name = model._meta.verbose_name self.verbose_name = model._meta.verbose_name
self.verbose_name_plural = model._meta.verbose_name_plural self.verbose_name_plural = model._meta.verbose_name_plural
def __repr__(self): def __repr__(self):
return '<EasyModel for %s>' % smart_str(self.model._meta.object_name) return smart_str('<EasyModel for %s>' % self.model._meta.object_name)
def model_databrowse(self): def model_databrowse(self):
"Returns the ModelDatabrowse class for this model." "Returns the ModelDatabrowse class for this model."
return self.site.registry[self.model] return self.site.registry[self.model]
def url(self): def url(self):
return mark_safe('%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name)) return '%s%s/%s/' % (self.site.root_url, self.model._meta.app_label, self.model._meta.module_name)
def objects(self, **kwargs): def objects(self, **kwargs):
return self.get_query_set().filter(**kwargs) return self.get_query_set().filter(**kwargs)
@ -61,7 +62,7 @@ class EasyField(object):
self.model, self.field = easy_model, field self.model, self.field = easy_model, field
def __repr__(self): def __repr__(self):
return smart_str(u'<EasyField for %s.%s>' % (self.model.model._meta.object_name, self.field.name)) return smart_str('<EasyField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
def choices(self): def choices(self):
for value, label in self.field.choices: for value, label in self.field.choices:
@ -69,9 +70,9 @@ class EasyField(object):
def url(self): def url(self):
if self.field.choices: if self.field.choices:
return mark_safe('%s%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name)) return '%s%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name)
elif self.field.rel: elif self.field.rel:
return mark_safe('%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name)) return '%s%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name)
class EasyChoice(object): class EasyChoice(object):
def __init__(self, easy_model, field, value, label): def __init__(self, easy_model, field, value, label):
@ -79,32 +80,30 @@ class EasyChoice(object):
self.value, self.label = value, label self.value, self.label = value, label
def __repr__(self): def __repr__(self):
return smart_str(u'<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name)) return smart_str('<EasyChoice for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
def url(self): def url(self):
return mark_safe('%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, iri_to_uri(self.value))) return '%s%s/%s/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.field.name, iri_to_uri(self.value))
@python_2_unicode_compatible
class EasyInstance(object): class EasyInstance(object):
def __init__(self, easy_model, instance): def __init__(self, easy_model, instance):
self.model, self.instance = easy_model, instance self.model, self.instance = easy_model, instance
def __repr__(self): def __repr__(self):
return smart_str(u'<EasyInstance for %s (%s)>' % (self.model.model._meta.object_name, self.instance._get_pk_val())) return smart_str('<EasyInstance for %s (%s)>' % (self.model.model._meta.object_name, self.instance._get_pk_val()))
def __unicode__(self):
val = smart_unicode(self.instance)
if len(val) > DISPLAY_SIZE:
return val[:DISPLAY_SIZE] + u'...'
return val
def __str__(self): def __str__(self):
return self.__unicode__().encode('utf-8') val = smart_text(self.instance)
if len(val) > DISPLAY_SIZE:
return val[:DISPLAY_SIZE] + '...'
return val
def pk(self): def pk(self):
return self.instance._get_pk_val() return self.instance._get_pk_val()
def url(self): def url(self):
return mark_safe('%s%s/%s/objects/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, iri_to_uri(self.pk()))) return '%s%s/%s/objects/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, iri_to_uri(self.pk()))
def fields(self): def fields(self):
""" """
@ -136,7 +135,7 @@ class EasyInstanceField(object):
self.raw_value = getattr(instance.instance, field.name) self.raw_value = getattr(instance.instance, field.name)
def __repr__(self): def __repr__(self):
return smart_str(u'<EasyInstanceField for %s.%s>' % (self.model.model._meta.object_name, self.field.name)) return smart_str('<EasyInstanceField for %s.%s>' % (self.model.model._meta.object_name, self.field.name))
def values(self): def values(self):
""" """
@ -176,8 +175,6 @@ class EasyInstanceField(object):
for plugin_name, plugin in self.model.model_databrowse().plugins.items(): for plugin_name, plugin in self.model.model_databrowse().plugins.items():
urls = plugin.urls(plugin_name, self) urls = plugin.urls(plugin_name, self)
if urls is not None: if urls is not None:
#plugin_urls.append(urls)
values = self.values()
return zip(self.values(), urls) return zip(self.values(), urls)
if self.field.rel: if self.field.rel:
m = EasyModel(self.model.site, self.field.rel.to) m = EasyModel(self.model.site, self.field.rel.to)
@ -186,20 +183,20 @@ class EasyInstanceField(object):
for value in self.values(): for value in self.values():
if value is None: if value is None:
continue continue
url = mark_safe('%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, iri_to_uri(value._get_pk_val()))) url = '%s%s/%s/objects/%s/' % (self.model.site.root_url, m.model._meta.app_label, m.model._meta.module_name, iri_to_uri(value._get_pk_val()))
lst.append((smart_unicode(value), url)) lst.append((smart_text(value), url))
else: else:
lst = [(value, None) for value in self.values()] lst = [(value, None) for value in self.values()]
elif self.field.choices: elif self.field.choices:
lst = [] lst = []
for value in self.values(): for value in self.values():
url = mark_safe('%s%s/%s/fields/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name, iri_to_uri(self.raw_value))) url = '%s%s/%s/fields/%s/%s/' % (self.model.site.root_url, self.model.model._meta.app_label, self.model.model._meta.module_name, self.field.name, iri_to_uri(self.raw_value))
lst.append((value, url)) lst.append((value, url))
elif isinstance(self.field, models.URLField): elif isinstance(self.field, models.URLField):
val = self.values()[0] val = list(self.values())[0]
lst = [(val, iri_to_uri(val))] lst = [(val, iri_to_uri(val))]
else: else:
lst = [(self.values()[0], None)] lst = [(list(self.values())[0], None)]
return lst return lst
class EasyQuerySet(QuerySet): class EasyQuerySet(QuerySet):

View File

@ -1,11 +1,13 @@
from __future__ import unicode_literals
from django import http from django import http
from django.db import models from django.db import models
from django.contrib.databrowse.datastructures import EasyModel from django.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse.sites import DatabrowsePlugin from django.contrib.databrowse.sites import DatabrowsePlugin
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.utils.html import format_html, format_html_join
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.encoding import force_unicode from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.views.generic import dates from django.views.generic import dates
from django.utils import datetime_safe from django.utils import datetime_safe
@ -61,19 +63,20 @@ class CalendarPlugin(DatabrowsePlugin):
def model_index_html(self, request, model, site): def model_index_html(self, request, model, site):
fields = self.field_dict(model) fields = self.field_dict(model)
if not fields: if not fields:
return u'' return ''
return mark_safe(u'<p class="filter"><strong>View calendar by:</strong> %s</p>' % \ return format_html('<p class="filter"><strong>View calendar by:</strong> {0}</p>',
u', '.join(['<a href="calendars/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])) format_html_join(', ', '<a href="calendars/{0}/">{1}</a>',
((f.name, force_text(capfirst(f.verbose_name))) for f in fields.values())))
def urls(self, plugin_name, easy_instance_field): def urls(self, plugin_name, easy_instance_field):
if isinstance(easy_instance_field.field, models.DateField): if isinstance(easy_instance_field.field, models.DateField):
d = easy_instance_field.raw_value d = easy_instance_field.raw_value
return [mark_safe(u'%s%s/%s/%s/%s/%s/' % ( return ['%s%s/%s/%s/%s/%s/' % (
easy_instance_field.model.url(), easy_instance_field.model.url(),
plugin_name, easy_instance_field.field.name, plugin_name, easy_instance_field.field.name,
str(d.year), str(d.year),
datetime_safe.new_date(d).strftime('%b').lower(), datetime_safe.new_date(d).strftime('%b').lower(),
d.day))] d.day)]
def model_view(self, request, model_databrowse, url): def model_view(self, request, model_databrowse, url):
self.model, self.site = model_databrowse.model, model_databrowse.site self.model, self.site = model_databrowse.model, model_databrowse.site
@ -93,7 +96,7 @@ class CalendarPlugin(DatabrowsePlugin):
def homepage_view(self, request): def homepage_view(self, request):
easy_model = EasyModel(self.site, self.model) easy_model = EasyModel(self.site, self.model)
field_list = self.fields.values() field_list = list(self.fields.values())
field_list.sort(key=lambda k:k.verbose_name) field_list.sort(key=lambda k:k.verbose_name)
return render_to_response('databrowse/calendar_homepage.html', { return render_to_response('databrowse/calendar_homepage.html', {
'root_url': self.site.root_url, 'root_url': self.site.root_url,

View File

@ -1,12 +1,15 @@
from __future__ import unicode_literals
from django import http from django import http
from django.db import models from django.db import models
from django.contrib.databrowse.datastructures import EasyModel from django.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse.sites import DatabrowsePlugin from django.contrib.databrowse.sites import DatabrowsePlugin
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.utils.html import format_html, format_html_join
from django.utils.http import urlquote
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.encoding import smart_str, force_unicode from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
import urllib
class FieldChoicePlugin(DatabrowsePlugin): class FieldChoicePlugin(DatabrowsePlugin):
def __init__(self, field_filter=None): def __init__(self, field_filter=None):
@ -29,17 +32,17 @@ class FieldChoicePlugin(DatabrowsePlugin):
def model_index_html(self, request, model, site): def model_index_html(self, request, model, site):
fields = self.field_dict(model) fields = self.field_dict(model)
if not fields: if not fields:
return u'' return ''
return mark_safe(u'<p class="filter"><strong>View by:</strong> %s</p>' % \ return format_html('<p class="filter"><strong>View by:</strong> {0}</p>',
u', '.join(['<a href="fields/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()])) format_html_join(', ', '<a href="fields/{0}/">{1}</a>',
((f.name, force_text(capfirst(f.verbose_name))) for f in fields.values())))
def urls(self, plugin_name, easy_instance_field): def urls(self, plugin_name, easy_instance_field):
if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values(): if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values():
field_value = smart_str(easy_instance_field.raw_value) return ['%s%s/%s/%s/' % (
return [mark_safe(u'%s%s/%s/%s/' % (
easy_instance_field.model.url(), easy_instance_field.model.url(),
plugin_name, easy_instance_field.field.name, plugin_name, easy_instance_field.field.name,
urllib.quote(field_value, safe='')))] urlquote(easy_instance_field.raw_value, safe=''))]
def model_view(self, request, model_databrowse, url): def model_view(self, request, model_databrowse, url):
self.model, self.site = model_databrowse.model, model_databrowse.site self.model, self.site = model_databrowse.model, model_databrowse.site
@ -60,7 +63,7 @@ class FieldChoicePlugin(DatabrowsePlugin):
def homepage_view(self, request): def homepage_view(self, request):
easy_model = EasyModel(self.site, self.model) easy_model = EasyModel(self.site, self.model)
field_list = self.fields.values() field_list = list(self.fields.values())
field_list.sort(key=lambda k: k.verbose_name) field_list.sort(key=lambda k: k.verbose_name)
return render_to_response('databrowse/fieldchoice_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list}) return render_to_response('databrowse/fieldchoice_homepage.html', {'root_url': self.site.root_url, 'model': easy_model, 'field_list': field_list})

View File

@ -1,14 +1,18 @@
try:
from urllib.parse import urljoin
except ImportError: # Python 2
from urlparse import urljoin
from django import http from django import http
from django.contrib.databrowse.datastructures import EasyModel from django.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse.sites import DatabrowsePlugin from django.contrib.databrowse.sites import DatabrowsePlugin
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
import urlparse
class ObjectDetailPlugin(DatabrowsePlugin): class ObjectDetailPlugin(DatabrowsePlugin):
def model_view(self, request, model_databrowse, url): def model_view(self, request, model_databrowse, url):
# If the object ID wasn't provided, redirect to the model page, which is one level up. # If the object ID wasn't provided, redirect to the model page, which is one level up.
if url is None: if url is None:
return http.HttpResponseRedirect(urlparse.urljoin(request.path, '../')) return http.HttpResponseRedirect(urljoin(request.path, '../'))
easy_model = EasyModel(model_databrowse.site, model_databrowse.model) easy_model = EasyModel(model_databrowse.site, model_databrowse.model)
obj = easy_model.object_by_pk(url) obj = easy_model.object_by_pk(url)
return render_to_response('databrowse/object_detail.html', {'object': obj, 'root_url': model_databrowse.site.root_url}) return render_to_response('databrowse/object_detail.html', {'object': obj, 'root_url': model_databrowse.site.root_url})

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