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
* Aymeric Augustin
* Claude Paroz
* Anssi Kääriäinen
* Florian Apolloner
More information on the main contributors to Django can be found in
docs/internals/committers.txt.
@ -50,6 +52,7 @@ answer newbie questions, and generally made Django that much better:
Antoni Aloy
Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
AgarFu <heaven@croasanaso.sytes.net>
James Aylett
Dagur Páll Ammendrup <dagurp@gmail.com>
Collin Anderson <cmawebsite@gmail.com>
Jeff Anderson <jefferya@programmerq.net>
@ -59,7 +62,6 @@ answer newbie questions, and generally made Django that much better:
andy@jadedplanet.net
Fabrice Aneche <akh@nobugware.com>
ant9000@netwise.it
Florian Apolloner <florian@apolloner.eu>
arien <regexbot@gmail.com>
David Ascher <http://ascher.ca/>
atlithorn <atlithorn@gmail.com>
@ -84,6 +86,7 @@ answer newbie questions, and generally made Django that much better:
Esdras Beleza <linux@esdrasbeleza.com>
Chris Bennett <chrisrbennett@yahoo.com>
James Bennett
Danilo Bargen
Shai Berger <shai@platonix.com>
Julian Bez
Arvis Bickovskis <viestards.lists@gmail.com>
@ -136,9 +139,10 @@ answer newbie questions, and generally made Django that much better:
Robert Coup
Pete Crosier <pete.crosier@gmail.com>
Matt Croydon <http://www.postneo.com/>
Leah Culver <leah.culver@gmail.com>
flavio.curella@gmail.com
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>
dackze+django@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>
Sander Dijkhuis <sander.dijkhuis@gmail.com>
Jordan Dimov <s3x3y1@gmail.com>
Riccardo Di Virgilio
Nebojša Dorđević
dne@mayonnaise.net
dready <wil@mojipage.com>
@ -171,6 +176,7 @@ answer newbie questions, and generally made Django that much better:
Clint Ecker
Nick Efford <nick@efford.org>
eibaan@gmail.com
David Eklund
Julia Elman
enlight
Enrico <rico.bl@gmail.com>
@ -219,6 +225,7 @@ answer newbie questions, and generally made Django that much better:
pradeep.gowda@gmail.com
Collin Grady <collin@collingrady.com>
Gabriel Grant <g@briel.ca>
Daniel Greenfeld
Simon Greenhill <dev@simon.net.nz>
Owen Griffiths
Espen Grindhaug <http://grindhaug.org/>
@ -229,6 +236,7 @@ answer newbie questions, and generally made Django that much better:
Scot Hacker <shacker@birdhouse.org>
dAniel hAhler
hambaloney
Will Hardy <django@willhardy.com.au>
Brian Harring <ferringb@gmail.com>
Brant Harris
Ronny Haryanto <http://ronny.haryan.to/>
@ -273,7 +281,6 @@ answer newbie questions, and generally made Django that much better:
jpellerin@gmail.com
junzhang.jn@gmail.com
Xia Kai <http://blog.xiaket.org/>
Anssi Kääriäinen
Antti Kaihola <http://djangopeople.net/akaihola/>
Peter van Kampen
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
Paul McLanahan <paul@mclanahan.net>
Tobias McNulty <http://www.caktusgroup.com/blog>
Andrews Medina <andrewsmedina@gmail.com>
Zain Memon
Christian Metts
michal@plovarna.cz
@ -422,6 +430,7 @@ answer newbie questions, and generally made Django that much better:
polpak@yahoo.com
Ross Poulton <ross@rossp.org>
Mihai Preda <mihai_preda@yahoo.com>
Daniele Procida <daniele@vurt.org>
Matthias Pronk <django@masida.nl>
Jyrki Pulliainen <jyrki.pulliainen@gmail.com>
Thejaswi Puthraya <thejaswi.puthraya@gmail.com>
@ -450,6 +459,7 @@ answer newbie questions, and generally made Django that much better:
Armin Ronacher
Daniel Roseman <http://roseman.org.uk/>
Rozza <ross.lawley@gmail.com>
Audrey Roy <http://audreymroy.com/>
Oliver Rutherfurd <http://rutherfurd.net/>
ryankanno
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>
Bartolome Sanchez Salado <i42sasab@uco.es>
Kadesarin Sanjek
Tim Saylor <tim.saylor@gmail.com>
Massimo Scamarcia <massimo.scamarcia@gmail.com>
Paulo Scardine <paulo@scardine.com.br>
David Schein
@ -502,6 +513,7 @@ answer newbie questions, and generally made Django that much better:
Aaron Swartz <http://www.aaronsw.com/>
Ville Säävuori <http://www.unessa.net/>
Mart Sõmermaa <http://mrts.pri.ee/>
Marc Tamlyn
Christian Tanzer <tanzer@swing.co.at>
Tyler Tarabula <tyler.tarabula@gmail.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>
Frank Wierzbicki
charly.wilhelm@gmail.com
Simon Williams
Derek Willis <http://blog.thescoop.org/>
Rachel Willmer <http://www.willmer.com/kb/>
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.utils.functional import LazyObject, empty
from django.utils import importlib
from django.utils import six
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
@ -73,7 +74,7 @@ class BaseSettings(object):
elif name == "ADMIN_MEDIA_PREFIX":
warnings.warn("The ADMIN_MEDIA_PREFIX setting has been removed; "
"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 "
"to a tuple, not a string.")
object.__setattr__(self, name, value)
@ -102,7 +103,10 @@ class Settings(BaseSettings):
if setting == setting.upper():
setting_value = getattr(mod, setting)
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.
setattr(self, setting, setting_value)
@ -154,7 +158,7 @@ class UserSettingsHolder(BaseSettings):
return getattr(self.default_settings, name)
def __dir__(self):
return self.__dict__.keys() + dir(self.default_settings)
return list(self.__dict__) + dir(self.default_settings)
# For Python < 2.6:
__members__ = property(lambda self: self.__dir__())

View File

@ -270,19 +270,19 @@ SECRET_KEY = ''
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
# 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 = ''
# 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 = ''
# Absolute path to the directory that holds static files.
# Example: "/home/media/media.lawrence.com/static/"
# Absolute path to the directory static files should be collected to.
# Example: "/var/www/example.com/static/"
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
# 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_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_PATH = '/' # The path of the session cookie.
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 = {
'ar': {
'bidi': True,
'code': 'ar',
'name': 'Arabic',
'name_local': u'\u0627\u0644\u0639\u0631\u0628\u064a\u0651\u0629',
'name_local': '\u0627\u0644\u0639\u0631\u0628\u064a\u0651\u0629',
},
'az': {
'bidi': True,
'code': 'az',
'name': 'Azerbaijani',
'name_local': u'az\u0259rbaycan dili',
'name_local': 'az\u0259rbaycan dili',
},
'bg': {
'bidi': False,
'code': 'bg',
'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': {
'bidi': False,
'code': 'bn',
'name': 'Bengali',
'name_local': u'\u09ac\u09be\u0982\u09b2\u09be',
'name_local': '\u09ac\u09be\u0982\u09b2\u09be',
},
'bs': {
'bidi': False,
'code': 'bs',
'name': 'Bosnian',
'name_local': u'bosanski',
'name_local': 'bosanski',
},
'ca': {
'bidi': False,
'code': 'ca',
'name': 'Catalan',
'name_local': u'catal\xe0',
'name_local': 'catal\xe0',
},
'cs': {
'bidi': False,
'code': 'cs',
'name': 'Czech',
'name_local': u'\u010desky',
'name_local': '\u010desky',
},
'cy': {
'bidi': False,
'code': 'cy',
'name': 'Welsh',
'name_local': u'Cymraeg',
'name_local': 'Cymraeg',
},
'da': {
'bidi': False,
'code': 'da',
'name': 'Danish',
'name_local': u'Dansk',
'name_local': 'Dansk',
},
'de': {
'bidi': False,
'code': 'de',
'name': 'German',
'name_local': u'Deutsch',
'name_local': 'Deutsch',
},
'el': {
'bidi': False,
'code': 'el',
'name': 'Greek',
'name_local': u'\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac',
'name_local': '\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac',
},
'en': {
'bidi': False,
'code': 'en',
'name': 'English',
'name_local': u'English',
'name_local': 'English',
},
'en-gb': {
'bidi': False,
'code': 'en-gb',
'name': 'British English',
'name_local': u'British English',
'name_local': 'British English',
},
'eo': {
'bidi': False,
'code': 'eo',
'name': 'Esperanto',
'name_local': u'Esperanto',
'name_local': 'Esperanto',
},
'es': {
'bidi': False,
'code': 'es',
'name': 'Spanish',
'name_local': u'espa\xf1ol',
'name_local': 'espa\xf1ol',
},
'es-ar': {
'bidi': False,
'code': 'es-ar',
'name': 'Argentinian Spanish',
'name_local': u'espa\xf1ol de Argentina',
'name_local': 'espa\xf1ol de Argentina',
},
'es-mx': {
'bidi': False,
'code': 'es-mx',
'name': 'Mexican Spanish',
'name_local': u'espa\xf1ol de Mexico',
'name_local': 'espa\xf1ol de Mexico',
},
'es-ni': {
'bidi': False,
'code': 'es-ni',
'name': 'Nicaraguan Spanish',
'name_local': u'espa\xf1ol de Nicaragua',
'name_local': 'espa\xf1ol de Nicaragua',
},
'et': {
'bidi': False,
'code': 'et',
'name': 'Estonian',
'name_local': u'eesti',
'name_local': 'eesti',
},
'eu': {
'bidi': False,
'code': 'eu',
'name': 'Basque',
'name_local': u'Basque',
'name_local': 'Basque',
},
'fa': {
'bidi': True,
'code': 'fa',
'name': 'Persian',
'name_local': u'\u0641\u0627\u0631\u0633\u06cc',
'name_local': '\u0641\u0627\u0631\u0633\u06cc',
},
'fi': {
'bidi': False,
'code': 'fi',
'name': 'Finnish',
'name_local': u'suomi',
'name_local': 'suomi',
},
'fr': {
'bidi': False,
'code': 'fr',
'name': 'French',
'name_local': u'Fran\xe7ais',
'name_local': 'Fran\xe7ais',
},
'fy-nl': {
'bidi': False,
'code': 'fy-nl',
'name': 'Frisian',
'name_local': u'Frisian',
'name_local': 'Frisian',
},
'ga': {
'bidi': False,
'code': 'ga',
'name': 'Irish',
'name_local': u'Gaeilge',
'name_local': 'Gaeilge',
},
'gl': {
'bidi': False,
'code': 'gl',
'name': 'Galician',
'name_local': u'galego',
'name_local': 'galego',
},
'he': {
'bidi': True,
'code': 'he',
'name': 'Hebrew',
'name_local': u'\u05e2\u05d1\u05e8\u05d9\u05ea',
'name_local': '\u05e2\u05d1\u05e8\u05d9\u05ea',
},
'hi': {
'bidi': False,
'code': 'hi',
'name': 'Hindi',
'name_local': u'Hindi',
'name_local': 'Hindi',
},
'hr': {
'bidi': False,
'code': 'hr',
'name': 'Croatian',
'name_local': u'Hrvatski',
'name_local': 'Hrvatski',
},
'hu': {
'bidi': False,
'code': 'hu',
'name': 'Hungarian',
'name_local': u'Magyar',
'name_local': 'Magyar',
},
'id': {
'bidi': False,
'code': 'id',
'name': 'Indonesian',
'name_local': u'Bahasa Indonesia',
'name_local': 'Bahasa Indonesia',
},
'is': {
'bidi': False,
'code': 'is',
'name': 'Icelandic',
'name_local': u'\xcdslenska',
'name_local': '\xcdslenska',
},
'it': {
'bidi': False,
'code': 'it',
'name': 'Italian',
'name_local': u'italiano',
'name_local': 'italiano',
},
'ja': {
'bidi': False,
'code': 'ja',
'name': 'Japanese',
'name_local': u'\u65e5\u672c\u8a9e',
'name_local': '\u65e5\u672c\u8a9e',
},
'ka': {
'bidi': False,
'code': 'ka',
'name': 'Georgian',
'name_local': u'\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8',
'name_local': '\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8',
},
'kk': {
'bidi': False,
'code': 'kk',
'name': 'Kazakh',
'name_local': u'\u049a\u0430\u0437\u0430\u049b',
'name_local': '\u049a\u0430\u0437\u0430\u049b',
},
'km': {
'bidi': False,
'code': 'km',
'name': 'Khmer',
'name_local': u'Khmer',
'name_local': 'Khmer',
},
'kn': {
'bidi': False,
'code': 'kn',
'name': 'Kannada',
'name_local': u'Kannada',
'name_local': 'Kannada',
},
'ko': {
'bidi': False,
'code': 'ko',
'name': 'Korean',
'name_local': u'\ud55c\uad6d\uc5b4',
'name_local': '\ud55c\uad6d\uc5b4',
},
'lt': {
'bidi': False,
'code': 'lt',
'name': 'Lithuanian',
'name_local': u'Lithuanian',
'name_local': 'Lithuanian',
},
'lv': {
'bidi': False,
'code': 'lv',
'name': 'Latvian',
'name_local': u'latvie\u0161u',
'name_local': 'latvie\u0161u',
},
'mk': {
'bidi': False,
'code': 'mk',
'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': {
'bidi': False,
'code': 'ml',
'name': 'Malayalam',
'name_local': u'Malayalam',
'name_local': 'Malayalam',
},
'mn': {
'bidi': False,
'code': 'mn',
'name': 'Mongolian',
'name_local': u'Mongolian',
'name_local': 'Mongolian',
},
'nb': {
'bidi': False,
'code': 'nb',
'name': 'Norwegian Bokmal',
'name_local': u'Norsk (bokm\xe5l)',
'name_local': 'Norsk (bokm\xe5l)',
},
'ne': {
'bidi': False,
'code': 'ne',
'name': 'Nepali',
'name_local': u'\u0928\u0947\u092a\u093e\u0932\u0940',
'name_local': '\u0928\u0947\u092a\u093e\u0932\u0940',
},
'nl': {
'bidi': False,
'code': 'nl',
'name': 'Dutch',
'name_local': u'Nederlands',
'name_local': 'Nederlands',
},
'nn': {
'bidi': False,
'code': 'nn',
'name': 'Norwegian Nynorsk',
'name_local': u'Norsk (nynorsk)',
'name_local': 'Norsk (nynorsk)',
},
'no': {
'bidi': False,
'code': 'no',
'name': 'Norwegian',
'name_local': u'Norsk',
'name_local': 'Norsk',
},
'pa': {
'bidi': False,
'code': 'pa',
'name': 'Punjabi',
'name_local': u'Punjabi',
'name_local': 'Punjabi',
},
'pl': {
'bidi': False,
'code': 'pl',
'name': 'Polish',
'name_local': u'polski',
'name_local': 'polski',
},
'pt': {
'bidi': False,
'code': 'pt',
'name': 'Portuguese',
'name_local': u'Portugu\xeas',
'name_local': 'Portugu\xeas',
},
'pt-br': {
'bidi': False,
'code': 'pt-br',
'name': 'Brazilian Portuguese',
'name_local': u'Portugu\xeas Brasileiro',
'name_local': 'Portugu\xeas Brasileiro',
},
'ro': {
'bidi': False,
'code': 'ro',
'name': 'Romanian',
'name_local': u'Rom\xe2n\u0103',
'name_local': 'Rom\xe2n\u0103',
},
'ru': {
'bidi': False,
'code': 'ru',
'name': 'Russian',
'name_local': u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439',
'name_local': '\u0420\u0443\u0441\u0441\u043a\u0438\u0439',
},
'sk': {
'bidi': False,
'code': 'sk',
'name': 'Slovak',
'name_local': u'slovensk\xfd',
'name_local': 'slovensk\xfd',
},
'sl': {
'bidi': False,
'code': 'sl',
'name': 'Slovenian',
'name_local': u'Sloven\u0161\u010dina',
'name_local': 'Sloven\u0161\u010dina',
},
'sq': {
'bidi': False,
'code': 'sq',
'name': 'Albanian',
'name_local': u'Albanian',
'name_local': 'Albanian',
},
'sr': {
'bidi': False,
'code': 'sr',
'name': 'Serbian',
'name_local': u'\u0441\u0440\u043f\u0441\u043a\u0438',
'name_local': '\u0441\u0440\u043f\u0441\u043a\u0438',
},
'sr-latn': {
'bidi': False,
'code': 'sr-latn',
'name': 'Serbian Latin',
'name_local': u'srpski (latinica)',
'name_local': 'srpski (latinica)',
},
'sv': {
'bidi': False,
'code': 'sv',
'name': 'Swedish',
'name_local': u'Svenska',
'name_local': 'Svenska',
},
'sw': {
'bidi': False,
'code': 'sw',
'name': 'Swahili',
'name_local': u'Kiswahili',
'name_local': 'Kiswahili',
},
'ta': {
'bidi': False,
'code': 'ta',
'name': 'Tamil',
'name_local': u'\u0ba4\u0bae\u0bbf\u0bb4\u0bcd',
'name_local': '\u0ba4\u0bae\u0bbf\u0bb4\u0bcd',
},
'te': {
'bidi': False,
'code': 'te',
'name': 'Telugu',
'name_local': u'\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41',
'name_local': '\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41',
},
'th': {
'bidi': False,
'code': 'th',
'name': 'Thai',
'name_local': u'Thai',
'name_local': 'Thai',
},
'tr': {
'bidi': False,
'code': 'tr',
'name': 'Turkish',
'name_local': u'T\xfcrk\xe7e',
'name_local': 'T\xfcrk\xe7e',
},
'tt': {
'bidi': False,
'code': 'tt',
'name': 'Tatar',
'name_local': u'\u0422\u0430\u0442\u0430\u0440\u0447\u0430',
'name_local': '\u0422\u0430\u0442\u0430\u0440\u0447\u0430',
},
'uk': {
'bidi': False,
'code': 'uk',
'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': {
'bidi': False,
'code': 'ur',
'name': 'Urdu',
'name_local': u'\u0627\u0631\u062f\u0648',
'name_local': '\u0627\u0631\u062f\u0648',
},
'vi': {
'bidi': False,
'code': 'vi',
'name': 'Vietnamese',
'name_local': u'Vietnamese',
'name_local': 'Vietnamese',
},
'zh-cn': {
'bidi': False,
'code': 'zh-cn',
'name': 'Simplified Chinese',
'name_local': u'\u7b80\u4f53\u4e2d\u6587',
'name_local': '\u7b80\u4f53\u4e2d\u6587',
},
'zh-tw': {
'bidi': False,
'code': 'zh-tw',
'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 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -19,5 +20,5 @@ SHORT_DATE_FORMAT = 'd.m.Y'
# TIME_INPUT_FORMATS =
# DATETIME_INPUT_FORMATS =
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space
THOUSAND_SEPARATOR = ' ' # Non-breaking space
# NUMBER_GROUPING =

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -33,5 +34,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
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', # '10/25/06'
)
DECIMAL_SEPARATOR = u'.'
THOUSAND_SEPARATOR = u','
DECIMAL_SEPARATOR = '.'
THOUSAND_SEPARATOR = ','
NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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'
TIME_FORMAT = 'H:i:s'
@ -24,5 +25,5 @@ DATETIME_INPUT_FORMATS = (
'%d/%m/%y %H:%M',
)
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

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -19,5 +20,5 @@ SHORT_DATE_FORMAT = 'd.m.Y'
# TIME_INPUT_FORMATS =
# DATETIME_INPUT_FORMATS =
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space
THOUSAND_SEPARATOR = ' ' # Non-breaking space
# NUMBER_GROUPING =

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,12 +1,13 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = 'j. E Y'
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'
MONTH_DAY_FORMAT = 'j. F'
SHORT_DATE_FORMAT = 'j.n.Y'
@ -19,5 +20,5 @@ SHORT_DATE_FORMAT = 'j.n.Y'
# TIME_INPUT_FORMATS =
# DATETIME_INPUT_FORMATS =
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space
THOUSAND_SEPARATOR = ' ' # Non-breaking space
# NUMBER_GROUPING =

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -37,5 +38,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3

View File

@ -1,17 +1,18 @@
# -*- encoding: utf-8 -*-
# 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,
# 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'
# DATETIME_FORMAT =
YEAR_MONTH_FORMAT = 'F Y'
MONTH_DAY_FORMAT = 'j F'
SHORT_DATE_FORMAT = 'j M, Y'
# SHORT_DATETIME_FORMAT =
# FIRST_DAY_OF_WEEK =
DATETIME_FORMAT = r'j \d\e F \d\e Y \á\s H:i'
YEAR_MONTH_FORMAT = r'F \d\e Y'
MONTH_DAY_FORMAT = r'j \d\e F'
SHORT_DATE_FORMAT = 'd-m-Y'
SHORT_DATETIME_FORMAT = 'd-m-Y, H:i'
FIRST_DAY_OF_WEEK = 1 # Monday
# The *_INPUT_FORMATS strings use the Python strftime format syntax,
# see http://docs.python.org/library/datetime.html#strftime-strptime-behavior

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -28,5 +29,5 @@ DATETIME_INPUT_FORMATS = (
'%Y.%m.%d.', # '2006.10.25.'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space
THOUSAND_SEPARATOR = ' ' # Non-breaking space
NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -37,5 +38,5 @@ DATETIME_INPUT_FORMATS = (
'%d.%m.%y', # '25.10.06'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' ' # Non-breaking space
THOUSAND_SEPARATOR = ' ' # Non-breaking space
NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -39,5 +40,5 @@ DATETIME_INPUT_FORMATS = (
'%d.%m.%y', # '25.10.06'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -39,5 +40,5 @@ DATETIME_INPUT_FORMATS = (
'%d.%m.%y', # '25.10.06'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -33,5 +34,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' '
THOUSAND_SEPARATOR = ' '
NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -36,5 +37,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -33,5 +34,5 @@ DATETIME_INPUT_FORMATS = (
'%Y-%m-%d', # '2006-10-25'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
@ -36,5 +37,5 @@ DATETIME_INPUT_FORMATS = (
'%m/%d/%y', # '10/25/06'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u'\xa0' # non-breaking space
THOUSAND_SEPARATOR = '\xa0' # non-breaking space
NUMBER_GROUPING = 3

View File

@ -2,6 +2,8 @@
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = 'j E Y р.'
@ -19,5 +21,5 @@ SHORT_DATE_FORMAT = 'j M Y'
# TIME_INPUT_FORMATS =
# DATETIME_INPUT_FORMATS =
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = u' '
THOUSAND_SEPARATOR = ' '
# NUMBER_GROUPING =

View File

@ -1,6 +1,7 @@
# -*- encoding: utf-8 -*-
# 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,
# see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date

View File

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

View File

@ -2,6 +2,7 @@ from django.core.urlresolvers import (RegexURLPattern,
RegexURLResolver, LocaleRegexURLResolver)
from django.core.exceptions import ImproperlyConfigured
from django.utils.importlib import import_module
from django.utils import six
__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
urlconf_module = arg
if isinstance(urlconf_module, basestring):
if isinstance(urlconf_module, six.string_types):
urlconf_module = import_module(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
return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace)
else:
if isinstance(view, basestring):
if isinstance(view, six.string_types):
if not view:
raise ImproperlyConfigured('Empty URL pattern view name not permitted (for pattern %r)' % regex)
if prefix:

View File

@ -1,5 +1,5 @@
from django.conf import settings
from django.conf.urls import patterns
from django.conf.urls import patterns, url
from django.core.urlresolvers import LocaleRegexURLResolver
def i18n_patterns(prefix, *args):
@ -16,5 +16,5 @@ def i18n_patterns(prefix, *args):
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.db import router
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 _
def delete_selected(modeladmin, request, queryset):
@ -42,7 +42,7 @@ def delete_selected(modeladmin, request, queryset):
n = queryset.count()
if n:
for obj in queryset:
obj_display = force_unicode(obj)
obj_display = force_text(obj)
modeladmin.log_deletion(request, obj, obj_display)
queryset.delete()
modeladmin.message_user(request, _("Successfully deleted %(count)d %(items)s.") % {
@ -52,9 +52,9 @@ def delete_selected(modeladmin, request, queryset):
return None
if len(queryset) == 1:
objects_name = force_unicode(opts.verbose_name)
objects_name = force_text(opts.verbose_name)
else:
objects_name = force_unicode(opts.verbose_name_plural)
objects_name = force_text(opts.verbose_name_plural)
if perms_needed or protected:
title = _("Cannot delete %(name)s") % {"name": objects_name}

View File

@ -9,7 +9,7 @@ import datetime
from django.db import models
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 import timezone
@ -195,7 +195,7 @@ class RelatedFieldListFilter(FieldListFilter):
}
for pk_val, val in self.lookup_choices:
yield {
'selected': self.lookup_val == smart_unicode(pk_val),
'selected': self.lookup_val == smart_text(pk_val),
'query_string': cl.get_query_string({
self.lookup_kwarg: pk_val,
}, [self.lookup_kwarg_isnull]),
@ -272,7 +272,7 @@ class ChoicesFieldListFilter(FieldListFilter):
}
for lookup, title in self.field.flatchoices:
yield {
'selected': smart_unicode(lookup) == self.lookup_val,
'selected': smart_text(lookup) == self.lookup_val,
'query_string': cl.get_query_string({
self.lookup_kwarg: lookup}),
'display': title,
@ -381,7 +381,7 @@ class AllValuesFieldListFilter(FieldListFilter):
if val is None:
include_none = True
continue
val = smart_unicode(val)
val = smart_text(val)
yield {
'selected': self.lookup_val == val,
'query_string': cl.get_query_string({

View File

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

View File

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

View File

@ -1,10 +1,12 @@
from __future__ import unicode_literals
from django.db import models
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.util import quote
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode
from django.utils.safestring import mark_safe
from django.utils.encoding import smart_text
from django.utils.encoding import python_2_unicode_compatible
ADDITION = 1
CHANGE = 2
@ -13,10 +15,11 @@ DELETION = 3
class LogEntryManager(models.Manager):
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()
@python_2_unicode_compatible
class LogEntry(models.Model):
action_time = models.DateTimeField(_('action time'), auto_now=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
@ -35,9 +38,9 @@ class LogEntry(models.Model):
ordering = ('-action_time',)
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:
return _('Added "%(object)s".') % {'object': self.object_repr}
elif self.action_flag == CHANGE:
@ -66,5 +69,5 @@ class LogEntry(models.Model):
This is relative to the Django admin index page.
"""
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

View File

@ -24,10 +24,11 @@ from django.utils.decorators import method_decorator
from django.utils.datastructures import SortedDict
from django.utils.html import escape, escapejs
from django.utils.safestring import mark_safe
from django.utils import six
from django.utils.text import capfirst, get_text_list
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
from django.utils.encoding import force_unicode
from django.utils.encoding import force_text
HORIZONTAL, VERTICAL = 1, 2
# returns the <ul> class for a given radio_admin field
@ -49,7 +50,7 @@ FORMFIELD_FOR_DBFIELD_DEFAULTS = {
models.TextField: {'widget': widgets.AdminTextareaWidget},
models.URLField: {'widget': widgets.AdminURLFieldWidget},
models.IntegerField: {'widget': widgets.AdminIntegerFieldWidget},
models.BigIntegerField: {'widget': widgets.AdminIntegerFieldWidget},
models.BigIntegerField: {'widget': widgets.AdminBigIntegerFieldWidget},
models.CharField: {'widget': widgets.AdminTextInputWidget},
models.ImageField: {'widget': widgets.AdminFileWidget},
models.FileField: {'widget': widgets.AdminFileWidget},
@ -57,9 +58,8 @@ FORMFIELD_FOR_DBFIELD_DEFAULTS = {
csrf_protect_m = method_decorator(csrf_protect)
class BaseModelAdmin(object):
class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
"""Functionality common to both ModelAdmin and InlineAdmin."""
__metaclass__ = forms.MediaDefiningClass
raw_id_fields = ()
fields = None
@ -425,7 +425,7 @@ class ModelAdmin(BaseModelAdmin):
if self.declared_fieldsets:
return self.declared_fieldsets
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})]
def get_form(self, request, obj=None, **kwargs):
@ -520,7 +520,7 @@ class ModelAdmin(BaseModelAdmin):
user_id = request.user.pk,
content_type_id = ContentType.objects.get_for_model(object).pk,
object_id = object.pk,
object_repr = force_unicode(object),
object_repr = force_text(object),
action_flag = ADDITION
)
@ -535,7 +535,7 @@ class ModelAdmin(BaseModelAdmin):
user_id = request.user.pk,
content_type_id = ContentType.objects.get_for_model(object).pk,
object_id = object.pk,
object_repr = force_unicode(object),
object_repr = force_text(object),
action_flag = CHANGE,
change_message = message
)
@ -560,7 +560,7 @@ class ModelAdmin(BaseModelAdmin):
"""
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.allow_tags = True
@ -608,7 +608,7 @@ class ModelAdmin(BaseModelAdmin):
tuple (name, description).
"""
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))
choices.append(choice)
return choices
@ -674,17 +674,17 @@ class ModelAdmin(BaseModelAdmin):
for formset in formsets:
for added_object in formset.new_objects:
change_message.append(_('Added %(name)s "%(object)s".')
% {'name': force_unicode(added_object._meta.verbose_name),
'object': force_unicode(added_object)})
% {'name': force_text(added_object._meta.verbose_name),
'object': force_text(added_object)})
for changed_object, changed_fields in formset.changed_objects:
change_message.append(_('Changed %(list)s for %(name)s "%(object)s".')
% {'list': get_text_list(changed_fields, _('and')),
'name': force_unicode(changed_object._meta.verbose_name),
'object': force_unicode(changed_object)})
'name': force_text(changed_object._meta.verbose_name),
'object': force_text(changed_object)})
for deleted_object in formset.deleted_objects:
change_message.append(_('Deleted %(name)s "%(object)s".')
% {'name': force_unicode(deleted_object._meta.verbose_name),
'object': force_unicode(deleted_object)})
% {'name': force_text(deleted_object._meta.verbose_name),
'object': force_text(deleted_object)})
change_message = ' '.join(change_message)
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_absolute_url': hasattr(self.model, 'get_absolute_url'),
'ordered_objects': ordered_objects,
'form_url': mark_safe(form_url),
'form_url': form_url,
'opts': opts,
'content_type_id': ContentType.objects.get_for_model(self.model).id,
'save_as': self.save_as,
@ -769,7 +769,7 @@ class ModelAdmin(BaseModelAdmin):
opts = obj._meta
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
# the presence of keys in request.POST.
if "_continue" in request.POST:
@ -782,10 +782,10 @@ class ModelAdmin(BaseModelAdmin):
return HttpResponse(
'<!DOCTYPE html><html><head><title></title></head><body>'
'<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)))
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)
else:
self.message_user(request, msg)
@ -819,7 +819,7 @@ class ModelAdmin(BaseModelAdmin):
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:
self.message_user(request, msg + ' ' + _("You may edit it again below."))
if "_popup" in request.REQUEST:
@ -827,14 +827,14 @@ class ModelAdmin(BaseModelAdmin):
else:
return HttpResponseRedirect(request.path)
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)
return HttpResponseRedirect(reverse('admin:%s_%s_change' %
(opts.app_label, module_name),
args=(pk_value,),
current_app=self.admin_site.name))
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' %
(opts.app_label, module_name),
current_app=self.admin_site.name))
@ -995,10 +995,9 @@ class ModelAdmin(BaseModelAdmin):
media = media + inline_admin_formset.media
context = {
'title': _('Add %s') % force_unicode(opts.verbose_name),
'title': _('Add %s') % force_text(opts.verbose_name),
'adminform': adminForm,
'is_popup': "_popup" in request.REQUEST,
'show_delete': False,
'media': media,
'inline_admin_formsets': inline_admin_formsets,
'errors': helpers.AdminErrorList(form, formsets),
@ -1020,7 +1019,7 @@ class ModelAdmin(BaseModelAdmin):
raise PermissionDenied
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:
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
context = {
'title': _('Change %s') % force_unicode(opts.verbose_name),
'title': _('Change %s') % force_text(opts.verbose_name),
'adminform': adminForm,
'object_id': object_id,
'original': obj,
@ -1195,14 +1194,14 @@ class ModelAdmin(BaseModelAdmin):
if changecount:
if changecount == 1:
name = force_unicode(opts.verbose_name)
name = force_text(opts.verbose_name)
else:
name = force_unicode(opts.verbose_name_plural)
name = force_text(opts.verbose_name_plural)
msg = ungettext("%(count)s %(name)s was changed successfully.",
"%(count)s %(name)s were changed successfully.",
changecount) % {'count': changecount,
'name': name,
'obj': force_unicode(obj)}
'obj': force_text(obj)}
self.message_user(request, msg)
return HttpResponseRedirect(request.get_full_path())
@ -1229,7 +1228,7 @@ class ModelAdmin(BaseModelAdmin):
'All %(total_count)s selected', cl.result_count)
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_all': selection_note_all % {'total_count': cl.result_count},
'title': cl.title,
@ -1264,7 +1263,7 @@ class ModelAdmin(BaseModelAdmin):
raise PermissionDenied
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)
@ -1276,11 +1275,11 @@ class ModelAdmin(BaseModelAdmin):
if request.POST: # The user has already confirmed the deletion.
if perms_needed:
raise PermissionDenied
obj_display = force_unicode(obj)
obj_display = force_text(obj)
self.log_deletion(request, obj, obj_display)
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):
return HttpResponseRedirect(reverse('admin:index',
@ -1289,7 +1288,7 @@ class ModelAdmin(BaseModelAdmin):
(opts.app_label, opts.module_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:
title = _("Cannot delete %(name)s") % {"name": object_name}
@ -1321,15 +1320,15 @@ class ModelAdmin(BaseModelAdmin):
opts = model._meta
app_label = opts.app_label
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
).select_related().order_by('action_time')
# If no history was found, see whether this object even exists.
obj = get_object_or_404(model, pk=unquote(object_id))
context = {
'title': _('Change history: %s') % force_unicode(obj),
'title': _('Change history: %s') % force_text(obj),
'action_list': action_list,
'module_name': capfirst(force_unicode(opts.verbose_name_plural)),
'module_name': capfirst(force_text(opts.verbose_name_plural)),
'object': obj,
'app_label': app_label,
'opts': opts,
@ -1416,7 +1415,7 @@ class InlineModelAdmin(BaseModelAdmin):
if self.declared_fieldsets:
return self.declared_fieldsets
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})]
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.template.response import TemplateResponse
from django.utils.safestring import mark_safe
from django.utils import six
from django.utils.text import capfirst
from django.utils.translation import ugettext as _
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).
"""
return self._actions.iteritems()
return six.iteritems(self._actions)
def has_permission(self, request):
"""
@ -234,14 +235,15 @@ class AdminSite(object):
wrap(self.i18n_javascript, cacheable=True),
name='jsi18n'),
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+)/$',
wrap(self.app_index),
name='app_list')
)
# 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('',
url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
include(model_admin.urls))
@ -373,7 +375,7 @@ class AdminSite(object):
}
# Sort the apps alphabetically.
app_list = app_dict.values()
app_list = list(six.itervalues(app_dict))
app_list.sort(key=lambda x: x['name'])
# Sort the models alphabetically within each app.

View File

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

View File

@ -41,7 +41,8 @@
text-align: left;
}
.selector .selector-filter label {
.selector .selector-filter label,
.inline-group .aligned .selector .selector-filter label {
width: 16px;
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();
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);
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",
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."))});
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."))})};
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);
(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=
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();
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?
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=
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.")):
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",
[a(this).attr("id")]);return!1})})})(django.jQuery);
(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 false})})})(django.jQuery);

View File

@ -44,7 +44,7 @@
if ($(this).attr("tagName") == "TR") {
// If forms are laid out as table rows, insert the
// "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>");
addButton = $(this).parent().find("tr:last a");
} 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("."+
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();
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)">'+
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.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);
(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&&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");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>");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,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 %}
{% block extrahead %}{{ block.super }}
{% url 'admin:jsi18n' as jsi18nurl %}
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../../jsi18n/" }}"></script>
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{% 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 %}

View File

@ -3,8 +3,7 @@
{% load admin_urls %}
{% block extrahead %}{{ block.super }}
{% url 'admin:jsi18n' as jsi18nurl %}
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../jsi18n/" }}"></script>
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{{ media }}
{% endblock %}
@ -31,7 +30,7 @@
<ul class="object-tools">
{% block object-tools-items %}
<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 %}
</ul>
{% endif %}{% endif %}
@ -65,7 +64,7 @@
{% 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>
{% endif %}

View File

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

View File

@ -7,7 +7,7 @@
<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 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' %}
</div>
{% 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 %}">
<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 %}
</h3>
{% 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">
{% 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.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 %}
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
{{ inline_admin_form.fk_field.field }}

View File

@ -7,7 +7,7 @@
<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 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' %}
</div>
{% endblock %}

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import datetime
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.db import models
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 import six
from django.utils.text import capfirst
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.loader import get_template
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.
"""
if i == DOT:
return u'... '
return '... '
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:
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')
def pagination(cl):
@ -120,7 +126,7 @@ def result_headers(cl):
if i in ordering_field_columns:
sorted = True
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)
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_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}),
"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):
icon_url = static('admin/img/icon-%s.gif' %
{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):
"""
@ -179,8 +186,8 @@ def items_for_result(cl, result, form):
result_repr = EMPTY_CHANGELIST_VALUE
else:
if f is None:
if field_name == u'action_checkbox':
row_class = ' class="action-checkbox"'
if field_name == 'action_checkbox':
row_class = mark_safe(' class="action-checkbox"')
allow_tags = getattr(attr, 'allow_tags', False)
boolean = getattr(attr, 'boolean', False)
if boolean:
@ -188,24 +195,22 @@ def items_for_result(cl, result, form):
result_repr = display_for_value(value, boolean)
# Strip HTML tags in the resulting text, except if the
# function has an "allow_tags" attribute set to True.
if not allow_tags:
result_repr = escape(result_repr)
else:
if allow_tags:
result_repr = mark_safe(result_repr)
if isinstance(value, (datetime.date, datetime.time)):
row_class = ' class="nowrap"'
row_class = mark_safe(' class="nowrap"')
else:
if isinstance(f.rel, models.ManyToOneRel):
field_val = getattr(result, f.name)
if field_val is None:
result_repr = EMPTY_CHANGELIST_VALUE
else:
result_repr = escape(field_val)
result_repr = field_val
else:
result_repr = display_for_field(value, f)
if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)):
row_class = ' class="nowrap"'
if force_unicode(result_repr) == '':
row_class = mark_safe(' class="nowrap"')
if force_text(result_repr) == '':
result_repr = mark_safe('&nbsp;')
# 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:
@ -219,9 +224,15 @@ def items_for_result(cl, result, form):
else:
attr = pk
value = result.serializable_value(attr)
result_id = repr(force_unicode(value))[1:]
yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
(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))
result_id = repr(force_text(value))[1:]
yield format_html('<{0}{1}><a href="{2}"{3}>{4}</a></{5}>',
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:
# 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
@ -230,12 +241,10 @@ def items_for_result(cl, result, form):
field_name == cl.model._meta.pk.name and
form[cl.model._meta.pk.name].is_hidden)):
bf = form[field_name]
result_repr = mark_safe(force_unicode(bf.errors) + force_unicode(bf))
else:
result_repr = conditional_escape(result_repr)
yield mark_safe(u'<td%s>%s</td>' % (row_class, result_repr))
result_repr = mark_safe(force_text(bf.errors) + force_text(bf))
yield format_html('<td{0}>{1}</td>', row_class, result_repr)
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):
# Wrapper class used to return items in a list_editable
@ -258,7 +267,7 @@ def result_hidden_fields(cl):
if cl.formset:
for res, form in zip(cl.result_list, cl.formset.forms):
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")
def result_list(cl):

View File

@ -32,7 +32,7 @@ def submit_row(context):
'onclick_attrib': (opts.get_ordered_objects() and change
and 'onclick="submitOrderForm();"' or ''),
'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_and_add_another': context['has_add_permission'] and
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.contrib.admin.util import quote
register = template.Library()
@register.filter
def admin_urlname(value, 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
if not user_id.isdigit():
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 ''
@register.tag

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import datetime
import decimal
@ -7,11 +9,11 @@ from django.db.models.deletion import Collector
from django.db.models.related import RelatedObject
from django.forms.forms import pretty_name
from django.utils import formats
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.html import format_html
from django.utils.text import capfirst
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.core.urlresolvers import reverse
@ -50,7 +52,7 @@ def quote(s):
quoting is slightly different so that it doesn't get automatically
unquoted by the Web browser.
"""
if not isinstance(s, basestring):
if not isinstance(s, six.string_types):
return s
res = list(s)
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):
perms_needed.add(opts.verbose_name)
# Display a link to the admin page.
return mark_safe(u'%s: <a href="%s">%s</a>' %
(escape(capfirst(opts.verbose_name)),
admin_url,
escape(obj)))
return format_html('{0}: <a href="{1}">{2}</a>',
capfirst(opts.verbose_name),
admin_url,
obj)
else:
# Don't display link to edit, because it either has no
# admin or is edited inline.
return u'%s: %s' % (capfirst(opts.verbose_name),
force_unicode(obj))
return '%s: %s' % (capfirst(opts.verbose_name),
force_text(obj))
to_delete = collector.nested(format_callback)
@ -205,8 +207,8 @@ def model_format_dict(obj):
else:
opts = obj
return {
'verbose_name': force_unicode(opts.verbose_name),
'verbose_name_plural': force_unicode(opts.verbose_name_plural)
'verbose_name': force_text(opts.verbose_name),
'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
except models.FieldDoesNotExist:
if name == "__unicode__":
label = force_unicode(model._meta.verbose_name)
attr = unicode
label = force_text(model._meta.verbose_name)
attr = six.text_type
elif name == "__str__":
label = smart_str(model._meta.verbose_name)
attr = str
label = smart_bytes(model._meta.verbose_name)
attr = bytes
else:
if callable(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
except models.FieldDoesNotExist:
help_text = ""
return smart_unicode(help_text)
return smart_text(help_text)
def display_for_field(value, field):
@ -333,7 +335,7 @@ def display_for_field(value, field):
elif isinstance(field, models.FloatField):
return formats.number_format(value)
else:
return smart_unicode(value)
return smart_text(value)
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))
elif isinstance(value, (datetime.date, datetime.time)):
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)
else:
return smart_unicode(value)
return smart_text(value)
class NotRelationField(Exception):

View File

@ -6,7 +6,7 @@ from django.core.paginator import InvalidPage
from django.db import models
from django.db.models.fields import FieldDoesNotExist
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.http import urlencode
@ -75,7 +75,7 @@ class ChangeList(object):
title = ugettext('Select %s')
else:
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
def get_filters(self, request):
@ -94,7 +94,7 @@ class ChangeList(object):
# 'key' will be used as a keyword argument later, so Python
# requires it to be a string.
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):
raise SuspiciousOperation("Filtering by %s not allowed" % key)
@ -148,7 +148,7 @@ class ChangeList(object):
if remove is None: remove = []
p = self.params.copy()
for r in remove:
for k in p.keys():
for k in list(p):
if k.startswith(r):
del p[k]
for k, v in new_params.items():

View File

@ -1,18 +1,21 @@
"""
Form Widget classes specific to the Django admin site.
"""
from __future__ import unicode_literals
import copy
from django import forms
from django.contrib.admin.templatetags.admin_static import static
from django.core.urlresolvers import reverse
from django.forms.widgets import RadioFieldRenderer
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.translation import ugettext as _
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):
@ -39,12 +42,12 @@ class FilteredSelectMultiple(forms.SelectMultiple):
if self.is_stacked:
attrs['class'] += 'stacked'
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
# 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/')))
return mark_safe(u''.join(output))
return mark_safe(''.join(output))
class AdminDateWidget(forms.DateInput):
@ -83,24 +86,25 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget):
forms.MultiWidget.__init__(self, widgets, attrs)
def format_output(self, rendered_widgets):
return mark_safe(u'<p class="datetime">%s %s<br />%s %s</p>' % \
(_('Date:'), rendered_widgets[0], _('Time:'), rendered_widgets[1]))
return format_html('<p class="datetime">{0} {1}<br />{2} {3}</p>',
_('Date:'), rendered_widgets[0],
_('Time:'), rendered_widgets[1])
class AdminRadioFieldRenderer(RadioFieldRenderer):
def render(self):
"""Outputs a <ul> for this set of radio fields."""
return mark_safe(u'<ul%s>\n%s\n</ul>' % (
flatatt(self.attrs),
u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self]))
)
return format_html('<ul{0}>\n{1}\n</ul>',
flatatt(self.attrs),
format_html_join('\n', '<li>{0}</li>',
((force_text(w),) for w in self)))
class AdminRadioSelect(forms.RadioSelect):
renderer = AdminRadioFieldRenderer
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)
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)
def url_params_from_lookup_dict(lookups):
@ -113,12 +117,12 @@ def url_params_from_lookup_dict(lookups):
items = []
for k, v in lookups.items():
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):
# See django.db.fields.BooleanField.get_prep_lookup
v = ('0', '1')[v]
else:
v = unicode(v)
v = six.text_type(v)
items.append((k, v))
params.update(dict(items))
return params
@ -148,21 +152,21 @@ class ForeignKeyRawIdWidget(forms.TextInput):
params = self.url_parameters()
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:
url = u''
url = ''
if "class" not in attrs:
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
# TODO: "lookup_id_" is hard-coded here. This should instead use
# 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))
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')))
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
if value:
output.append(self.label_for_value(value))
return mark_safe(u''.join(output))
return mark_safe(''.join(output))
def base_url_parameters(self):
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
attrs['class'] = 'vManyToManyRawIdAdminField'
if value:
value = ','.join([force_unicode(v) for v in value])
value = ','.join([force_text(v) for v in value])
else:
value = ''
return super(ManyToManyRawIdWidget, self).render(name, value, attrs)
@ -217,7 +221,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
if len(initial) != len(data):
return True
for pk1, pk2 in zip(initial, data):
if force_unicode(pk1) != force_unicode(pk2):
if force_text(pk1) != force_text(pk2):
return True
return False
@ -261,11 +265,11 @@ class RelatedFieldWidgetWrapper(forms.Widget):
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
# 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))
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')))
return mark_safe(u''.join(output))
return mark_safe(''.join(output))
def build_attrs(self, extra_attrs=None, **kwargs):
"Helper function for building an attribute dictionary."
@ -303,12 +307,17 @@ class AdminURLFieldWidget(forms.TextInput):
super(AdminURLFieldWidget, self).__init__(attrs=final_attrs)
class AdminIntegerFieldWidget(forms.TextInput):
class_name = 'vIntegerField'
def __init__(self, attrs=None):
final_attrs = {'class': 'vIntegerField'}
final_attrs = {'class': self.class_name}
if attrs is not None:
final_attrs.update(attrs)
super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)
class AdminBigIntegerFieldWidget(AdminIntegerFieldWidget):
class_name = 'vBigIntegerField'
class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput):
def __init__(self, attrs=None):
final_attrs = {'class': 'vCommaSeparatedIntegerField'}

View File

@ -22,7 +22,7 @@ your computer is "internal").</p>
{% endblocktrans %}
<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>
<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.db.models import fields as builtin_fields
@ -20,17 +20,17 @@ class TestFieldType(unittest.TestCase):
def test_builtin_fields(self):
self.assertEqual(
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):
self.assertEqual(
views.get_readable_field_data_type(fields.CustomField()),
_(u'A custom field type')
_('A custom field type')
)
self.assertEqual(
views.get_readable_field_data_type(fields.DescriptionLackingField()),
_(u'Field of type: %(field_type)s') % {
_('Field of type: %(field_type)s') % {
'field_type': 'DescriptionLackingField'
}
)

View File

@ -6,7 +6,7 @@ from email.errors import HeaderParseError
from django.utils.safestring import mark_safe
from django.core.urlresolvers import reverse
from django.utils.encoding import smart_str
from django.utils.encoding import smart_bytes
try:
import docutils.core
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('/')
}
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,
destination_path=None, writer_name='html',
settings_overrides=overrides)

View File

@ -14,6 +14,7 @@ from django.core import urlresolvers
from django.contrib.admindocs import utils
from django.contrib.sites.models import Site
from django.utils.importlib import import_module
from django.utils import six
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
@ -37,7 +38,7 @@ def bookmarklets(request):
admin_root = urlresolvers.reverse('admin:index')
return render_to_response('admin_doc/bookmarklets.html', {
'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))
@staff_member_required
@ -48,7 +49,7 @@ def template_tag_index(request):
load_all_installed_template_libraries()
tags = []
app_libs = template.libraries.items()
app_libs = list(six.iteritems(template.libraries))
builtin_libs = [(None, lib) for lib in template.builtins]
for module_name, library in builtin_libs + app_libs:
for tag_name, tag_func in library.tags.items():
@ -83,7 +84,7 @@ def template_filter_index(request):
load_all_installed_template_libraries()
filters = []
app_libs = template.libraries.items()
app_libs = list(six.iteritems(template.libraries))
builtin_libs = [(None, lib) for lib in template.builtins]
for module_name, library in builtin_libs + app_libs:
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.decorators import method_decorator
from django.utils.safestring import mark_safe
from django.utils import six
from django.utils.translation import ugettext, ugettext_lazy as _
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters
@ -128,13 +129,13 @@ class UserAdmin(admin.ModelAdmin):
else:
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, {})
context = {
'title': _('Change password: %s') % escape(user.username),
'adminForm': adminForm,
'form_url': mark_safe(form_url),
'form_url': form_url,
'form': form,
'is_popup': '_popup' in request.REQUEST,
'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.models import Permission
@ -40,7 +41,7 @@ class ModelBackend(object):
if user_obj.is_anonymous() or obj is not None:
return set()
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))
return user_obj._perm_cache

View File

@ -11,8 +11,9 @@ class PermLookupDict(object):
def __getitem__(self, 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)
__nonzero__ = __bool__ # Python 2
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 django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import PermissionDenied
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):
@ -19,11 +23,12 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
if test_func(request.user):
return view_func(request, *args, **kwargs)
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
# use the path as the "next" url.
login_scheme, login_netloc = urlparse.urlparse(login_url or
settings.LOGIN_URL)[:2]
current_scheme, current_netloc = urlparse.urlparse(path)[:2]
login_scheme, login_netloc = urlparse(login_url_as_str)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()

View File

@ -1,17 +1,21 @@
from __future__ import unicode_literals
from django import forms
from django.forms.util import flatatt
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.safestring import mark_safe
from django.utils.translation import ugettext, ugettext_lazy as _
from django.contrib.auth import authenticate
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.sites.models import get_current_site
UNMASKED_DIGITS_TO_SHOW = 6
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)
encoded = smart_str(encoded)
if len(encoded) == 32 and '$' not in encoded:
algorithm = 'unsalted_md5'
else:
algorithm = encoded.split('$', 1)[0]
try:
hasher = get_hasher(algorithm)
hasher = identify_hasher(encoded)
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:
summary = ""
for key, value in hasher.safe_summary(encoded).iteritems():
summary += "<strong>%(key)s</strong>: %(value)s " % {"key": ugettext(key), "value": value}
summary = format_html_join('',
"<strong>{0}</strong>: {1} ",
((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):
@ -90,9 +89,9 @@ class UserCreationForm(forms.ModelForm):
raise forms.ValidationError(self.error_messages['duplicate_username'])
def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'])
return password2
@ -296,8 +295,11 @@ class PasswordChangeForm(SetPasswordForm):
raise forms.ValidationError(
self.error_messages['password_incorrect'])
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):

View File

@ -1,3 +1,6 @@
from __future__ import unicode_literals
import base64
import hashlib
from django.dispatch import receiver
@ -5,7 +8,7 @@ from django.conf import settings
from django.test.signals import setting_changed
from django.utils import importlib
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.utils.crypto import (
pbkdf2, constant_time_compare, get_random_string)
@ -40,20 +43,12 @@ def check_password(password, encoded, setter=None, preferred='default'):
return False
preferred = get_hasher(preferred)
raw_password = password
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)
hasher = identify_hasher(encoded)
must_update = hasher.algorithm != preferred.algorithm
is_correct = hasher.verify(password, encoded)
if setter and is_correct and must_update:
setter(raw_password)
setter(password)
return is_correct
@ -69,11 +64,9 @@ def make_password(password, salt=None, hasher='default'):
return UNUSABLE_PASSWORD
hasher = get_hasher(hasher)
password = smart_str(password)
if not salt:
salt = hasher.salt()
salt = smart_str(salt)
return hasher.encode(password, salt)
@ -125,6 +118,21 @@ def get_hasher(algorithm='default'):
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="*"):
"""
Returns the given hash, with only the first ``show`` number shown. The
@ -211,7 +219,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
if not iterations:
iterations = self.iterations
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)
def verify(self, password, encoded):
@ -291,7 +299,7 @@ class SHA1PasswordHasher(BasePasswordHasher):
def encode(self, password, salt):
assert password
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)
def verify(self, password, encoded):
@ -319,7 +327,7 @@ class MD5PasswordHasher(BasePasswordHasher):
def encode(self, password, salt):
assert password
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)
def verify(self, password, encoded):
@ -353,7 +361,7 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
return ''
def encode(self, password, salt):
return hashlib.md5(password).hexdigest()
return hashlib.md5(smart_bytes(password)).hexdigest()
def verify(self, password, encoded):
encoded_2 = self.encode(password, '')

View File

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

View File

@ -11,6 +11,7 @@ from django.contrib.auth.management import get_default_username
from django.core import exceptions
from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS
from django.utils.six.moves import input
from django.utils.text import capfirst
@ -78,7 +79,7 @@ class Command(BaseCommand):
input_msg = capfirst(username_field.verbose_name)
if 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 == '':
username = default_username
try:
@ -99,9 +100,9 @@ class Command(BaseCommand):
for field_name in other_fields:
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:
raw_value = raw_input(capfirst(field.verbose_name + ': '))
raw_value = input(capfirst(field.verbose_name + ': '))
try:
other_data[field_name] = field.clean(raw_value, None)
except exceptions.ValidationError, e:

View File

@ -1,3 +1,4 @@
from __future__ import unicode_literals
import re
import urllib
@ -7,7 +8,8 @@ from django.core import validators
from django.db import models
from django.db.models.manager import EmptyManager
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 import timezone
@ -17,6 +19,7 @@ from django.contrib.auth.hashers import (
check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
from django.contrib.auth.signals import user_logged_in
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import python_2_unicode_compatible
def update_last_login(sender, user, **kwargs):
@ -42,6 +45,7 @@ class PermissionManager(models.Manager):
)
@python_2_unicode_compatible
class Permission(models.Model):
"""
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',
'codename')
def __unicode__(self):
return u"%s | %s | %s" % (
unicode(self.content_type.app_label),
unicode(self.content_type),
unicode(self.name))
def __str__(self):
return "%s | %s | %s" % (
six.text_type(self.content_type.app_label),
six.text_type(self.content_type),
six.text_type(self.name))
def natural_key(self):
return (self.codename,) + self.content_type.natural_key()
@ -96,6 +100,7 @@ class GroupManager(models.Manager):
return self.get(name=name)
@python_2_unicode_compatible
class Group(models.Model):
"""
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_plural = _('groups')
def __unicode__(self):
def __str__(self):
return self.name
def natural_key(self):
@ -253,7 +258,7 @@ class AbstractBaseUser(models.Model):
"""
def setter(raw_password):
self.set_password(raw_password)
self.save()
self.save(update_fields=["password"])
return check_password(raw_password, self.password, setter)
def set_unusable_password(self):
@ -270,6 +275,7 @@ class AbstractBaseUser(models.Model):
raise NotImplementedError()
@python_2_unicode_compatible
class User(AbstractBaseUser):
"""
Users within the Django authentication system are represented by this
@ -311,20 +317,20 @@ class User(AbstractBaseUser):
verbose_name_plural = _('users')
swappable = 'AUTH_USER_MODEL'
def __unicode__(self):
def __str__(self):
return self.username
def natural_key(self):
return (self.username,)
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):
"""
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()
def get_short_name(self):
@ -425,6 +431,7 @@ class User(AbstractBaseUser):
return self._profile_cache
@python_2_unicode_compatible
class AnonymousUser(object):
id = None
pk = None
@ -438,11 +445,8 @@ class AnonymousUser(object):
def __init__(self):
pass
def __unicode__(self):
return 'AnonymousUser'
def __str__(self):
return unicode(self).encode('utf-8')
return 'AnonymousUser'
def __eq__(self, other):
return isinstance(other, self.__class__)

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
from django.conf import settings
from django.contrib.auth.models import User, Group, Permission, AnonymousUser
from django.contrib.contenttypes.models import ContentType
@ -51,7 +53,7 @@ class BackendTest(TestCase):
# reloading user to purge the _perm_cache
user = User.objects.get(username='test')
self.assertEqual(user.get_all_permissions() == set([u'auth.test']), True)
self.assertEqual(user.get_all_permissions() == set(['auth.test']), True)
self.assertEqual(user.get_group_permissions(), set([]))
self.assertEqual(user.has_module_perms('Group'), False)
self.assertEqual(user.has_module_perms('auth'), True)
@ -62,7 +64,7 @@ class BackendTest(TestCase):
user.user_permissions.add(perm)
user.save()
user = User.objects.get(username='test')
self.assertEqual(user.get_all_permissions(), set([u'auth.test2', u'auth.test', u'auth.test3']))
self.assertEqual(user.get_all_permissions(), set(['auth.test2', 'auth.test', 'auth.test3']))
self.assertEqual(user.has_perm('test'), False)
self.assertEqual(user.has_perm('auth.test'), True)
self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), True)
@ -72,9 +74,9 @@ class BackendTest(TestCase):
group.save()
user.groups.add(group)
user = User.objects.get(username='test')
exp = set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group'])
exp = set(['auth.test2', 'auth.test', 'auth.test3', 'auth.test_group'])
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)
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.core.management import call_command
from StringIO import StringIO
from django.test import TestCase
from django.utils.six import StringIO
class BasicTestCase(TestCase):
@ -60,3 +63,87 @@ class BasicTestCase(TestCase):
self.assertTrue(super.is_superuser)
self.assertTrue(super.is_active)
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
from django.contrib.auth.models import User
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.test import TestCase
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.translation import ugettext as _
@ -25,7 +28,7 @@ class UserCreationFormTest(TestCase):
form = UserCreationForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form["username"].errors,
[force_unicode(form.error_messages['duplicate_username'])])
[force_text(form.error_messages['duplicate_username'])])
def test_invalid_data(self):
data = {
@ -36,7 +39,7 @@ class UserCreationFormTest(TestCase):
form = UserCreationForm(data)
self.assertFalse(form.is_valid())
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):
# The verification password is incorrect.
@ -48,13 +51,13 @@ class UserCreationFormTest(TestCase):
form = UserCreationForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form["password2"].errors,
[force_unicode(form.error_messages['password_mismatch'])])
[force_text(form.error_messages['password_mismatch'])])
def test_both_passwords(self):
# One (or both) passwords weren't given
data = {'username': 'jsmith'}
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.assertEqual(form['password1'].errors, required_error)
self.assertEqual(form['password2'].errors, required_error)
@ -63,6 +66,7 @@ class UserCreationFormTest(TestCase):
form = UserCreationForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form['password1'].errors, required_error)
self.assertEqual(form['password2'].errors, [])
def test_success(self):
# The success case.
@ -92,7 +96,7 @@ class AuthenticationFormTest(TestCase):
form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid())
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):
# The user is inactive.
@ -103,7 +107,7 @@ class AuthenticationFormTest(TestCase):
form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(),
[force_unicode(form.error_messages['inactive'])])
[force_text(form.error_messages['inactive'])])
def test_inactive_user_i18n(self):
with self.settings(USE_I18N=True):
@ -116,7 +120,7 @@ class AuthenticationFormTest(TestCase):
form = AuthenticationForm(None, data)
self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(),
[force_unicode(form.error_messages['inactive'])])
[force_text(form.error_messages['inactive'])])
def test_success(self):
# The success case
@ -144,7 +148,7 @@ class SetPasswordFormTest(TestCase):
form = SetPasswordForm(user, data)
self.assertFalse(form.is_valid())
self.assertEqual(form["new_password2"].errors,
[force_unicode(form.error_messages['password_mismatch'])])
[force_text(form.error_messages['password_mismatch'])])
def test_success(self):
user = User.objects.get(username='testclient')
@ -171,7 +175,7 @@ class PasswordChangeFormTest(TestCase):
form = PasswordChangeForm(user, data)
self.assertFalse(form.is_valid())
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):
# The two new passwords do not match.
@ -184,7 +188,7 @@ class PasswordChangeFormTest(TestCase):
form = PasswordChangeForm(user, data)
self.assertFalse(form.is_valid())
self.assertEqual(form["new_password2"].errors,
[force_unicode(form.error_messages['password_mismatch'])])
[force_text(form.error_messages['password_mismatch'])])
def test_success(self):
# The success case.
@ -200,7 +204,7 @@ class PasswordChangeFormTest(TestCase):
def test_field_order(self):
# Regression test - check the order of fields:
user = User.objects.get(username='testclient')
self.assertEqual(PasswordChangeForm(user, {}).fields.keys(),
self.assertEqual(list(PasswordChangeForm(user, {}).fields),
['old_password', 'new_password1', 'new_password2'])
@ -215,7 +219,7 @@ class UserChangeFormTest(TestCase):
form = UserChangeForm(data, instance=user)
self.assertFalse(form.is_valid())
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):
# A regression test, introduce by adding an optimization for the
@ -270,7 +274,7 @@ class PasswordResetFormTest(TestCase):
form = PasswordResetForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form['email'].errors,
[force_unicode(EmailField.default_error_messages['invalid'])])
[force_text(EmailField.default_error_messages['invalid'])])
def test_nonexistant_email(self):
# Test nonexistant email address
@ -278,7 +282,7 @@ class PasswordResetFormTest(TestCase):
form = PasswordResetForm(data)
self.assertFalse(form.is_valid())
self.assertEqual(form.errors,
{'email': [force_unicode(form.error_messages['unknown'])]})
{'email': [force_text(form.error_messages['unknown'])]})
def test_cleaned_data(self):
# Regression test
@ -299,7 +303,7 @@ class PasswordResetFormTest(TestCase):
# potential case where contrib.sites is not installed. Refs #16412.
form.save(domain_override='example.com')
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):
# bug #5605, preserve the case of the user name (before the @ in the
@ -328,4 +332,4 @@ class PasswordResetFormTest(TestCase):
form = PasswordResetForm(data)
self.assertFalse(form.is_valid())
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.contrib.auth.hashers import (is_password_usable,
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.unittest import skipUnless
@ -26,7 +28,7 @@ class TestUtilsHashPass(unittest.TestCase):
encoded = make_password('letmein')
self.assertTrue(encoded.startswith('pbkdf2_sha256$'))
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))
def test_pkbdf2(self):
@ -34,47 +36,53 @@ class TestUtilsHashPass(unittest.TestCase):
self.assertEqual(encoded,
'pbkdf2_sha256$10000$seasalt$FQCNpiZpTb0zub+HBsH6TOwyRxJ19FwvjbweatNmK/Y=')
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.assertEqual(identify_hasher(encoded).algorithm, "pbkdf2_sha256")
def test_sha1(self):
encoded = make_password('letmein', 'seasalt', 'sha1')
self.assertEqual(encoded,
'sha1$seasalt$fec3530984afba6bade3347b7140d1a7da7da8c7')
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.assertEqual(identify_hasher(encoded).algorithm, "sha1")
def test_md5(self):
encoded = make_password('letmein', 'seasalt', 'md5')
self.assertEqual(encoded,
'md5$seasalt$f5531bef9f3687d0ccf0f617f0e25573')
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.assertEqual(identify_hasher(encoded).algorithm, "md5")
def test_unsalted_md5(self):
encoded = make_password('letmein', 'seasalt', 'unsalted_md5')
self.assertEqual(encoded, '0d107d09f5bbe40cade3de5c71e9e9b7')
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.assertEqual(identify_hasher(encoded).algorithm, "unsalted_md5")
@skipUnless(crypt, "no crypt module to generate password.")
def test_crypt(self):
encoded = make_password('letmein', 'ab', 'crypt')
self.assertEqual(encoded, 'crypt$$abN/qM.L/H8EQ')
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.assertEqual(identify_hasher(encoded).algorithm, "crypt")
@skipUnless(bcrypt, "py-bcrypt not installed")
def test_bcrypt(self):
encoded = make_password('letmein', hasher='bcrypt')
self.assertTrue(is_password_usable(encoded))
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.assertEqual(identify_hasher(encoded).algorithm, "bcrypt")
def test_unusable(self):
encoded = make_password(None)
@ -82,13 +90,15 @@ class TestUtilsHashPass(unittest.TestCase):
self.assertFalse(check_password(None, encoded))
self.assertFalse(check_password(UNUSABLE_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.assertRaises(ValueError, identify_hasher, encoded)
def test_bad_algorithm(self):
def doit():
make_password('letmein', hasher='lolcat')
self.assertRaises(ValueError, doit)
self.assertRaises(ValueError, identify_hasher, "lolcat$salt$hash")
def test_low_level_pkbdf2(self):
hasher = PBKDF2PasswordHasher()

View File

@ -1,5 +1,5 @@
from __future__ import unicode_literals
from datetime import date
from StringIO import StringIO
from django.contrib.auth import models, management
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.test import TestCase
from django.test.utils import override_settings
from django.utils.six import StringIO
class GetDefaultUsernameTestCase(TestCase):
@ -20,19 +21,19 @@ class GetDefaultUsernameTestCase(TestCase):
management.get_system_username = self._getpass_getuser
def test_simple(self):
management.get_system_username = lambda: u'joe'
management.get_system_username = lambda: 'joe'
self.assertEqual(management.get_default_username(), 'joe')
def test_existing(self):
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(check_db=False), 'joe')
def test_i18n(self):
# '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')

View File

@ -5,39 +5,29 @@ from django.contrib.auth.models import (Group, User,
SiteProfileNotAvailable, UserManager)
@override_settings(USE_TZ=False)
@override_settings(USE_TZ=False, AUTH_PROFILE_MODULE='')
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):
user = User.objects.create(username='testclient')
# calling get_profile without AUTH_PROFILE_MODULE set
if hasattr(settings, 'AUTH_PROFILE_MODULE'):
del settings.AUTH_PROFILE_MODULE
user = User.objects.get(username='testclient')
self.assertRaises(SiteProfileNotAvailable, user.get_profile)
del settings.AUTH_PROFILE_MODULE
with self.assertRaisesRegexp(SiteProfileNotAvailable,
"You need to set AUTH_PROFILE_MODULE in your project"):
user.get_profile()
# Bad syntax in AUTH_PROFILE_MODULE:
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
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)

View File

@ -1,9 +1,11 @@
import sys
from datetime import date, timedelta
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.test import TestCase
from django.utils import unittest
class TokenGeneratorTest(TestCase):
@ -51,6 +53,7 @@ class TokenGeneratorTest(TestCase):
p2 = Mocked(date.today() + timedelta(settings.PASSWORD_RESET_TIMEOUT_DAYS + 1))
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):
"""
Make sure we don't allow overly long dates, causing a potential DoS.

View File

@ -1,6 +1,5 @@
import os
import re
import urllib
from django.conf import settings
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.urlresolvers import reverse, NoReverseMatch
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.http import urlquote
from django.test import TestCase
from django.test.utils import override_settings
@ -46,7 +46,7 @@ class AuthViewsTestCase(TestCase):
self.assertTrue(SESSION_KEY in self.client.session)
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):
@ -256,7 +256,7 @@ class LoginTest(AuthViewsTestCase):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': login_url,
'next': REDIRECT_FIELD_NAME,
'bad_url': urllib.quote(bad_url),
'bad_url': urlquote(bad_url),
}
response = self.client.post(nasty_url, {
'username': 'testclient',
@ -277,7 +277,7 @@ class LoginTest(AuthViewsTestCase):
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
'url': login_url,
'next': REDIRECT_FIELD_NAME,
'good_url': urllib.quote(good_url),
'good_url': urlquote(good_url),
}
response = self.client.post(safe_url, {
'username': 'testclient',
@ -412,7 +412,7 @@ class LogoutTest(AuthViewsTestCase):
nasty_url = '%(url)s?%(next)s=%(bad_url)s' % {
'url': logout_url,
'next': REDIRECT_FIELD_NAME,
'bad_url': urllib.quote(bad_url),
'bad_url': urlquote(bad_url),
}
self.login()
response = self.client.get(nasty_url)
@ -432,7 +432,7 @@ class LogoutTest(AuthViewsTestCase):
safe_url = '%(url)s?%(next)s=%(good_url)s' % {
'url': logout_url,
'next': REDIRECT_FIELD_NAME,
'good_url': urllib.quote(good_url),
'good_url': urlquote(good_url),
}
self.login()
response = self.client.get(safe_url)

View File

@ -2,6 +2,7 @@ from datetime import date
from django.conf import settings
from django.utils.http import int_to_base36, base36_to_int
from django.utils.crypto import constant_time_compare, salted_hmac
from django.utils import six
class PasswordResetTokenGenerator(object):
@ -57,8 +58,8 @@ class PasswordResetTokenGenerator(object):
# Ensure results are consistent across DB backends
login_timestamp = user.last_login.replace(microsecond=0, tzinfo=None)
value = (unicode(user.id) + user.password +
unicode(login_timestamp) + unicode(timestamp))
value = (six.text_type(user.id) + user.password +
six.text_type(login_timestamp) + six.text_type(timestamp))
hash = salted_hmac(key_salt, value).hexdigest()[::2]
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.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, QueryDict
from django.template.response import TemplateResponse
from django.utils.encoding import force_str
from django.utils.http import base36_to_int
from django.utils.translation import ugettext as _
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":
form = authentication_form(data=request.POST)
if form.is_valid():
netloc = urlparse.urlparse(redirect_to)[1]
netloc = urlparse(redirect_to)[1]
# Use default setting if redirect_to is empty
if not redirect_to:
@ -81,7 +85,7 @@ def logout(request, next_page=None,
auth_logout(request)
redirect_to = request.REQUEST.get(redirect_field_name, '')
if redirect_to:
netloc = urlparse.urlparse(redirect_to)[1]
netloc = urlparse(redirect_to)[1]
# Security check -- don't allow redirection to a different host.
if not (netloc and netloc != request.get_host()):
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
"""
if not login_url:
login_url = settings.LOGIN_URL
# urlparse chokes on lazy objects in Python 3
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:
querystring = QueryDict(login_url_parts[4], mutable=True)
querystring[redirect_field_name] = next
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:
@ -203,7 +207,7 @@ def password_reset_confirm(request, uidb36=None, token=None,
try:
uid_int = base36_to_int(uidb36)
user = User.objects.get(id=uid_int)
except (ValueError, User.DoesNotExist):
except (ValueError, OverflowError, User.DoesNotExist):
user = None
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.comments.models import Comment
from django.utils.translation import ugettext_lazy as _, ungettext
@ -62,8 +64,8 @@ class CommentsAdmin(admin.ModelAdmin):
action(request, comment)
n_comments += 1
msg = ungettext(u'1 comment was successfully %(action)s.',
u'%(count)s comments were successfully %(action)s.',
msg = ungettext('1 comment was successfully %(action)s.',
'%(count)s comments were successfully %(action)s.',
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.comments.models import Comment
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 import timezone
from django.utils.translation import ungettext, ugettext, ugettext_lazy as _
@ -133,7 +133,7 @@ class CommentDetailsForm(CommentSecurityForm):
"""
return dict(
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_email = self.cleaned_data["email"],
user_url = self.cleaned_data["url"],

View File

@ -1,6 +1,6 @@
from django.db import models
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):
@ -18,5 +18,5 @@ class CommentManager(models.Manager):
ct = ContentType.objects.get_for_model(model)
qs = self.get_query_set().filter(content_type=ct)
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

View File

@ -7,6 +7,7 @@ from django.core import urlresolvers
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
COMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000)
@ -40,6 +41,7 @@ class BaseCommentAbstractModel(models.Model):
)
@python_2_unicode_compatible
class Comment(BaseCommentAbstractModel):
"""
A user comment about some object.
@ -77,7 +79,7 @@ class Comment(BaseCommentAbstractModel):
verbose_name = _('comment')
verbose_name_plural = _('comments')
def __unicode__(self):
def __str__(self):
return "%s: %s..." % (self.name, self.comment[:50])
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
@python_2_unicode_compatible
class CommentFlag(models.Model):
"""
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_plural = _('comment flags')
def __unicode__(self):
def __str__(self):
return "%s flag of comment ID %s by %s" % \
(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.contrib.contenttypes.models import ContentType
from django.contrib import comments
from django.utils.encoding import smart_unicode
from django.utils.encoding import smart_text
register = template.Library()
@ -75,7 +75,7 @@ class BaseCommentNode(template.Node):
qs = self.comment_model.objects.filter(
content_type = ctype,
object_pk = smart_unicode(object_pk),
object_pk = smart_text(object_pk),
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.
Templates: `comments/flag.html`,
Templates: :template:`comments/flag.html`,
Context:
comment
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
moderate comments" permission.
Templates: `comments/delete.html`,
Templates: :template:`comments/delete.html`,
Context:
comment
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
on GET, action on POST. Requires the "can moderate comments" permission.
Templates: `comments/approve.html`,
Templates: :template:`comments/approve.html`,
Context:
comment
the `comments.comment` object for approval

View File

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

View File

@ -1,6 +1,7 @@
"""
Classes allowing "generic" relations through ContentType and object-id fields.
"""
from __future__ import unicode_literals
from collections import defaultdict
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.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import smart_unicode
from django.utils.encoding import smart_text
class GenericForeignKey(object):
"""
@ -131,7 +132,7 @@ class GenericForeignKey(object):
def __set__(self, instance, value):
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
fk = None
@ -168,7 +169,7 @@ class GenericRelation(RelatedField, Field):
def value_to_string(self, obj):
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):
return self.rel.to._meta.db_table

View File

@ -1,6 +1,8 @@
from django.contrib.contenttypes.models import ContentType
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):
"""
@ -24,17 +26,17 @@ def update_contenttypes(app, created_models, verbosity=2, **kwargs):
)
to_remove = [
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
]
cts = ContentType.objects.bulk_create([
ContentType(
name=smart_unicode(model._meta.verbose_name_raw),
name=smart_text(model._meta.verbose_name_raw),
app_label=app_label,
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 verbosity >= 2:
@ -48,7 +50,7 @@ def update_contenttypes(app, created_models, verbosity=2, **kwargs):
' %s | %s' % (ct.app_label, ct.model)
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

View File

@ -1,6 +1,7 @@
from django.db import models
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):
@ -16,39 +17,44 @@ class ContentTypeManager(models.Manager):
self._add_to_cache(self.db, ct)
return ct
def _get_opts(self, model):
return model._meta.concrete_model._meta
def _get_opts(self, model, for_concrete_model):
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):
key = (opts.app_label, opts.object_name.lower())
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
ContentType if necessary. Lookups are cached so that subsequent lookups
for the same model don't hit the database.
"""
opts = self._get_opts(model)
opts = self._get_opts(model, for_concrete_model)
try:
ct = self._get_from_cache(opts)
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
# django.utils.functional.__proxy__ object.
ct, created = self.get_or_create(
app_label = opts.app_label,
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)
return ct
def get_for_models(self, *models):
def get_for_models(self, *models, **kwargs):
"""
Given *models, returns a dictionary mapping {model: content_type}.
"""
for_concrete_models = kwargs.pop('for_concrete_models', True)
# Final results
results = {}
# models that aren't already in the cache
@ -56,7 +62,7 @@ class ContentTypeManager(models.Manager):
needed_models = set()
needed_opts = set()
for model in models:
opts = self._get_opts(model)
opts = self._get_opts(model, for_concrete_models)
try:
ct = self._get_from_cache(opts)
except KeyError:
@ -81,7 +87,7 @@ class ContentTypeManager(models.Manager):
ct = self.create(
app_label=opts.app_label,
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)
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, {})[ct.id] = ct
@python_2_unicode_compatible
class ContentType(models.Model):
name = models.CharField(max_length=100)
app_label = models.CharField(max_length=100)
@ -130,7 +137,7 @@ class ContentType(models.Model):
ordering = ('name',)
unique_together = (('app_label', 'model'),)
def __unicode__(self):
def __str__(self):
# self.name is deprecated in favor of using model's verbose_name, which
# can be translated. Formal deprecation is delayed until we have DB
# 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:
return self.name
else:
return force_unicode(model._meta.verbose_name)
return force_text(model._meta.verbose_name)
def model_class(self):
"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.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.http import HttpRequest, Http404
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):
"""
Fake model not defining ``get_absolute_url`` for
:meth:`ContentTypesTests.test_shortcut_view_without_get_absolute_url`"""
name = models.CharField(max_length=30, unique=True)
def __unicode__(self):
def __str__(self):
return self.name
@ -26,7 +36,7 @@ class FooWithUrl(FooWithoutUrl):
"""
def get_absolute_url(self):
return "/users/%s/" % urllib.quote(smart_str(self.name))
return "/users/%s/" % urlquote(self.name)
class FooWithBrokenAbsoluteUrl(FooWithoutUrl):
"""
@ -112,6 +122,87 @@ class ContentTypesTests(TestCase):
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):
"""
Check that the shortcut view (used for the admin "view on site"
@ -181,4 +272,4 @@ class ContentTypesTests(TestCase):
app_label = 'contenttypes',
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.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site, get_current_site
@ -12,11 +14,11 @@ def shortcut(request, content_type_id, object_id):
try:
content_type = ContentType.objects.get(pk=content_type_id)
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})
obj = content_type.get_object_for_this_type(pk=object_id)
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})
try:

View File

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

View File

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

View File

@ -1,12 +1,15 @@
from __future__ import unicode_literals
from django import http
from django.db import models
from django.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse.sites import DatabrowsePlugin
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.encoding import smart_str, force_unicode
from django.utils.safestring import mark_safe
import urllib
from django.utils.encoding import force_text
class FieldChoicePlugin(DatabrowsePlugin):
def __init__(self, field_filter=None):
@ -29,17 +32,17 @@ class FieldChoicePlugin(DatabrowsePlugin):
def model_index_html(self, request, model, site):
fields = self.field_dict(model)
if not fields:
return u''
return mark_safe(u'<p class="filter"><strong>View by:</strong> %s</p>' % \
u', '.join(['<a href="fields/%s/">%s</a>' % (f.name, force_unicode(capfirst(f.verbose_name))) for f in fields.values()]))
return ''
return format_html('<p class="filter"><strong>View by:</strong> {0}</p>',
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):
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 [mark_safe(u'%s%s/%s/%s/' % (
return ['%s%s/%s/%s/' % (
easy_instance_field.model.url(),
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):
self.model, self.site = model_databrowse.model, model_databrowse.site
@ -60,7 +63,7 @@ class FieldChoicePlugin(DatabrowsePlugin):
def homepage_view(self, request):
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)
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.contrib.databrowse.datastructures import EasyModel
from django.contrib.databrowse.sites import DatabrowsePlugin
from django.shortcuts import render_to_response
import urlparse
class ObjectDetailPlugin(DatabrowsePlugin):
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 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)
obj = easy_model.object_by_pk(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