From 92b2dec918d7b73456899ecb5726cc03b86bd068 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Thu, 9 Aug 2012 20:08:47 +0200 Subject: [PATCH 001/253] [py3] Made signing infrastructure pass tests with Python 3 --- django/core/signing.py | 19 +++++++++---------- tests/regressiontests/signing/tests.py | 10 +++++++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/django/core/signing.py b/django/core/signing.py index 9ab8c5b8b0..6fc76bc201 100644 --- a/django/core/signing.py +++ b/django/core/signing.py @@ -32,6 +32,8 @@ start of the base64 JSON. There are 65 url-safe characters: the 64 used by url-safe base64 and the ':'. These functions make use of all of them. """ +from __future__ import unicode_literals + import base64 import json import time @@ -41,7 +43,7 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.utils import baseconv from django.utils.crypto import constant_time_compare, salted_hmac -from django.utils.encoding import force_text, smart_bytes +from django.utils.encoding import smart_bytes from django.utils.importlib import import_module @@ -60,12 +62,12 @@ class SignatureExpired(BadSignature): def b64_encode(s): - return base64.urlsafe_b64encode(s).strip('=') + return base64.urlsafe_b64encode(smart_bytes(s)).decode('ascii').strip('=') def b64_decode(s): pad = '=' * (-len(s) % 4) - return base64.urlsafe_b64decode(s + pad) + return base64.urlsafe_b64decode(smart_bytes(s + pad)).decode('ascii') def base64_hmac(salt, value, key): @@ -121,7 +123,7 @@ def dumps(obj, key=None, salt='django.core.signing', serializer=JSONSerializer, if compress: # Avoid zlib dependency unless compress is being used - compressed = zlib.compress(data) + compressed = zlib.compress(smart_bytes(data)) if len(compressed) < (len(data) - 1): data = compressed is_compressed = True @@ -135,8 +137,7 @@ def loads(s, key=None, salt='django.core.signing', serializer=JSONSerializer, ma """ Reverse of dumps(), raises BadSignature if signature fails """ - base64d = smart_bytes( - TimestampSigner(key, salt=salt).unsign(s, max_age=max_age)) + base64d = TimestampSigner(key, salt=salt).unsign(s, max_age=max_age) decompress = False if base64d[0] == '.': # It's compressed; uncompress it first @@ -159,16 +160,14 @@ class Signer(object): return base64_hmac(self.salt + 'signer', value, self.key) def sign(self, value): - value = smart_bytes(value) return '%s%s%s' % (value, self.sep, self.signature(value)) def unsign(self, signed_value): - signed_value = smart_bytes(signed_value) if not self.sep in signed_value: raise BadSignature('No "%s" found in value' % self.sep) value, sig = signed_value.rsplit(self.sep, 1) if constant_time_compare(sig, self.signature(value)): - return force_text(value) + return value raise BadSignature('Signature "%s" does not match' % sig) @@ -178,7 +177,7 @@ class TimestampSigner(Signer): return baseconv.base62.encode(int(time.time())) def sign(self, value): - value = smart_bytes('%s%s%s' % (value, self.sep, self.timestamp())) + value = '%s%s%s' % (value, self.sep, self.timestamp()) return '%s%s%s' % (value, self.sep, self.signature(value)) def unsign(self, value, max_age=None): diff --git a/tests/regressiontests/signing/tests.py b/tests/regressiontests/signing/tests.py index 2368405060..7145ec8b18 100644 --- a/tests/regressiontests/signing/tests.py +++ b/tests/regressiontests/signing/tests.py @@ -4,6 +4,7 @@ import time from django.core import signing from django.test import TestCase +from django.utils import six from django.utils.encoding import force_text @@ -69,15 +70,18 @@ class TestSigner(TestCase): def test_dumps_loads(self): "dumps and loads be reversible for any JSON serializable object" - objects = ( + objects = [ ['a', 'list'], - b'a string', 'a unicode string \u2019', {'a': 'dictionary'}, - ) + ] + if not six.PY3: + objects.append(b'a byte string') for o in objects: self.assertNotEqual(o, signing.dumps(o)) self.assertEqual(o, signing.loads(signing.dumps(o))) + self.assertNotEqual(o, signing.dumps(o, compress=True)) + self.assertEqual(o, signing.loads(signing.dumps(o, compress=True))) def test_decode_detects_tampering(self): "loads should raise exception for tampered objects" From eff6ba2f64ea5426502daadb04fbe1e85ace068a Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 10 Aug 2012 16:19:20 -0400 Subject: [PATCH 002/253] Fixed #17016 - Added examples for file uploads in views. Thanks Tim Saylor for the draft patch and Aymeric Augustin and Claude Paroz for feedback. --- AUTHORS | 1 + docs/topics/http/file-uploads.txt | 44 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/AUTHORS b/AUTHORS index 6ca8b297f4..0ea0ca6da7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -467,6 +467,7 @@ answer newbie questions, and generally made Django that much better: Vinay Sajip Bartolome Sanchez Salado Kadesarin Sanjek + Tim Saylor Massimo Scamarcia Paulo Scardine David Schein diff --git a/docs/topics/http/file-uploads.txt b/docs/topics/http/file-uploads.txt index 877d3b4100..0b0ef3b193 100644 --- a/docs/topics/http/file-uploads.txt +++ b/docs/topics/http/file-uploads.txt @@ -178,6 +178,50 @@ Three settings control Django's file upload behavior: Which means "try to upload to memory first, then fall back to temporary files." +Handling uploaded files with a model +------------------------------------ + +If you're saving a file on a :class:`~django.db.models.Model` with a +:class:`~django.db.models.FileField`, using a :class:`~django.forms.ModelForm` +makes this process much easier. The file object will be saved when calling +``form.save()``:: + + from django.http import HttpResponseRedirect + from django.shortcuts import render + from .forms import ModelFormWithFileField + + def upload_file(request): + if request.method == 'POST': + form = ModelFormWithFileField(request.POST, request.FILES) + if form.is_valid(): + # file is saved + form.save() + return HttpResponseRedirect('/success/url/') + else: + form = ModelFormWithFileField() + return render('upload.html', {'form': form}) + +If you are constructing an object manually, you can simply assign the file +object from :attr:`request.FILES ` to the file +field in the model:: + + from django.http import HttpResponseRedirect + from django.shortcuts import render + from .forms import UploadFileForm + from .models import ModelWithFileField + + def upload_file(request): + if request.method == 'POST': + form = UploadFileForm(request.POST, request.FILES) + if form.is_valid(): + instance = ModelWithFileField(file_field=request.FILES['file']) + instance.save() + return HttpResponseRedirect('/success/url/') + else: + form = UploadFileForm() + return render('upload.html', {'form': form}) + + ``UploadedFile`` objects ======================== From cb38fd9632f314bd78324083005dc8df55200390 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 10 Aug 2012 16:12:45 -0400 Subject: [PATCH 003/253] Fixed #17680 - Clarified when logging is configured. --- docs/topics/logging.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt index a878d42266..9bdd706355 100644 --- a/docs/topics/logging.txt +++ b/docs/topics/logging.txt @@ -218,10 +218,11 @@ handlers, filters and formatters that you want in your logging setup, and the log levels and other properties that you want those components to have. -Logging is configured immediately after settings have been loaded. -Since the loading of settings is one of the first things that Django -does, you can be certain that loggers are always ready for use in your -project code. +Logging is configured as soon as settings have been loaded +(either manually using :func:`~django.conf.settings.configure` or when at least +one setting is accessed). Since the loading of settings is one of the first +things that Django does, you can be certain that loggers are always ready for +use in your project code. .. _dictConfig format: http://docs.python.org/library/logging.config.html#configuration-dictionary-schema From 87e0a75c03747c5b7ed87311c2e1f34d9fb20bee Mon Sep 17 00:00:00 2001 From: Andrews Medina Date: Sat, 11 Aug 2012 00:18:33 +0200 Subject: [PATCH 004/253] [py3] Decoded base64-encoded hash in contrib.auth.hashers --- django/contrib/auth/hashers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py index 45c1f88ab2..cdaf75636f 100644 --- a/django/contrib/auth/hashers.py +++ b/django/contrib/auth/hashers.py @@ -219,7 +219,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher): if not iterations: iterations = self.iterations hash = pbkdf2(password, salt, iterations, digest=self.digest) - hash = base64.b64encode(hash).strip() + hash = base64.b64encode(hash).decode('ascii').strip() return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, hash) def verify(self, password, encoded): From 060ac8e7115b51374de7d5e34c784d9df5764d0b Mon Sep 17 00:00:00 2001 From: Issac Kelly Date: Fri, 10 Aug 2012 23:07:15 -0700 Subject: [PATCH 005/253] Create headings and expand CBV docs so that the "Built-In CBV" docs include a complete list. --- docs/ref/class-based-views/base.txt | 35 ++++++++++++------- .../class-based-views/generic-date-based.txt | 24 ++++++++++++- .../ref/class-based-views/generic-display.txt | 10 ++++-- .../ref/class-based-views/generic-editing.txt | 27 +++++++++----- docs/ref/class-based-views/index.txt | 2 +- .../class-based-views/mixins-date-based.txt | 18 ++++++++++ docs/ref/class-based-views/mixins-editing.txt | 14 ++++++-- .../mixins-multiple-object.txt | 6 ++++ docs/ref/class-based-views/mixins-simple.txt | 6 ++++ .../mixins-single-object.txt | 6 ++++ 10 files changed, 121 insertions(+), 27 deletions(-) diff --git a/docs/ref/class-based-views/base.txt b/docs/ref/class-based-views/base.txt index 5e0360c88f..3f82b44f46 100644 --- a/docs/ref/class-based-views/base.txt +++ b/docs/ref/class-based-views/base.txt @@ -8,6 +8,9 @@ themselves or inherited from. They may not provide all the capabilities required for projects, in which case there are Mixins and Generic class-based views. +View +---- + .. class:: django.views.generic.base.View The master class-based base view. All other class-based views inherit from @@ -31,13 +34,13 @@ views. **Example urls.py**:: from django.conf.urls import patterns, url - + from myapp.views import MyView - + urlpatterns = patterns('', url(r'^mine/$', MyView.as_view(), name='my-view'), ) - + **Methods** .. method:: dispatch(request, *args, **kwargs) @@ -61,13 +64,16 @@ views. The default implementation returns ``HttpResponseNotAllowed`` with list of allowed methods in plain text. - - .. note:: + + .. note:: Documentation on class-based views is a work in progress. As yet, only the methods defined directly on the class are documented here, not methods defined on superclasses. +TemplateView +------------ + .. class:: django.views.generic.base.TemplateView Renders a given template, passing it a ``{{ params }}`` template variable, @@ -84,22 +90,22 @@ views. 1. :meth:`dispatch()` 2. :meth:`http_method_not_allowed()` 3. :meth:`get_context_data()` - + **Example views.py**:: from django.views.generic.base import TemplateView - + from articles.models import Article class HomePageView(TemplateView): template_name = "home.html" - + def get_context_data(self, **kwargs): context = super(HomePageView, self).get_context_data(**kwargs) context['latest_articles'] = Article.objects.all()[:5] return context - + **Example urls.py**:: from django.conf.urls import patterns, url @@ -126,12 +132,15 @@ views. * ``params``: The dictionary of keyword arguments captured from the URL pattern that served the view. - .. note:: + .. note:: Documentation on class-based views is a work in progress. As yet, only the methods defined directly on the class are documented here, not methods defined on superclasses. +RedirectView +------------ + .. class:: django.views.generic.base.RedirectView Redirects to a given URL. @@ -159,7 +168,7 @@ views. from django.shortcuts import get_object_or_404 from django.views.generic.base import RedirectView - + from articles.models import Article class ArticleCounterRedirectView(RedirectView): @@ -176,7 +185,7 @@ views. from django.conf.urls import patterns, url from django.views.generic.base import RedirectView - + from article.views import ArticleCounterRedirectView urlpatterns = patterns('', @@ -217,7 +226,7 @@ views. behavior they wish, as long as the method returns a redirect-ready URL string. - .. note:: + .. note:: Documentation on class-based views is a work in progress. As yet, only the methods defined directly on the class are documented here, not methods diff --git a/docs/ref/class-based-views/generic-date-based.txt b/docs/ref/class-based-views/generic-date-based.txt index 69a98df77b..6730923d1e 100644 --- a/docs/ref/class-based-views/generic-date-based.txt +++ b/docs/ref/class-based-views/generic-date-based.txt @@ -5,6 +5,9 @@ Generic date views Date-based generic views (in the module :mod:`django.views.generic.dates`) are views for displaying drilldown pages for date-based data. +ArchiveIndexView +---------------- + .. class:: django.views.generic.dates.ArchiveIndexView A top-level index page showing the "latest" objects, by date. Objects with @@ -21,12 +24,15 @@ are views for displaying drilldown pages for date-based data. * :class:`django.views.generic.list.MultipleObjectMixin` * :class:`django.views.generic.dates.DateMixin` * :class:`django.views.generic.base.View` - + **Notes** * Uses a default ``context_object_name`` of ``latest``. * Uses a default ``template_name_suffix`` of ``_archive``. +YearArchiveView +--------------- + .. class:: django.views.generic.dates.YearArchiveView A yearly archive page showing all available months in a given year. Objects @@ -86,6 +92,9 @@ are views for displaying drilldown pages for date-based data. * Uses a default ``template_name_suffix`` of ``_archive_year``. +MonthArchiveView +---------------- + .. class:: django.views.generic.dates.MonthArchiveView A monthly archive page showing all objects in a given month. Objects with a @@ -134,6 +143,9 @@ are views for displaying drilldown pages for date-based data. * Uses a default ``template_name_suffix`` of ``_archive_month``. +WeekArchiveView +--------------- + .. class:: django.views.generic.dates.WeekArchiveView A weekly archive page showing all objects in a given week. Objects with a @@ -175,6 +187,9 @@ are views for displaying drilldown pages for date-based data. * Uses a default ``template_name_suffix`` of ``_archive_week``. +DayArchiveView +-------------- + .. class:: django.views.generic.dates.DayArchiveView A day archive page showing all objects in a given day. Days in the future @@ -225,6 +240,9 @@ are views for displaying drilldown pages for date-based data. * Uses a default ``template_name_suffix`` of ``_archive_day``. +TodayArchiveView +---------------- + .. class:: django.views.generic.dates.TodayArchiveView A day archive page showing all objects for *today*. This is exactly the @@ -246,6 +264,10 @@ are views for displaying drilldown pages for date-based data. * :class:`django.views.generic.dates.DateMixin` * :class:`django.views.generic.base.View` + +DateDetailView +-------------- + .. class:: django.views.generic.dates.DateDetailView A page representing an individual object. If the object has a date value in diff --git a/docs/ref/class-based-views/generic-display.txt b/docs/ref/class-based-views/generic-display.txt index bbf0d4f05a..ef3bc179ee 100644 --- a/docs/ref/class-based-views/generic-display.txt +++ b/docs/ref/class-based-views/generic-display.txt @@ -5,6 +5,9 @@ Generic display views The two following generic class-based views are designed to display data. On many projects they are typically the most commonly used views. +DetailView +---------- + .. class:: django.views.generic.detail.DetailView While this view is executing, ``self.object`` will contain the object that @@ -39,7 +42,7 @@ many projects they are typically the most commonly used views. from articles.models import Article class ArticleDetailView(DetailView): - + model = Article def get_context_data(self, **kwargs): @@ -55,7 +58,10 @@ many projects they are typically the most commonly used views. urlpatterns = patterns('', url(r'^(?P[-_\w]+)/$', ArticleDetailView.as_view(), name='article-detail'), - ) + ) + +ListView +-------- .. class:: django.views.generic.list.ListView diff --git a/docs/ref/class-based-views/generic-editing.txt b/docs/ref/class-based-views/generic-editing.txt index a65a59bc8b..2fac06ee02 100644 --- a/docs/ref/class-based-views/generic-editing.txt +++ b/docs/ref/class-based-views/generic-editing.txt @@ -2,7 +2,7 @@ Generic editing views ===================== -The following views are described on this page and provide a foundation for +The following views are described on this page and provide a foundation for editing content: * :class:`django.views.generic.edit.FormView` @@ -13,7 +13,7 @@ editing content: .. note:: Some of the examples on this page assume that a model titled 'Author' - has been defined. For these cases we assume the following has been defined + has been defined. For these cases we assume the following has been defined in `myapp/models.py`:: from django import models @@ -25,6 +25,9 @@ editing content: def get_absolute_url(self): return reverse('author-detail', kwargs={'pk': self.pk}) +FormView +-------- + .. class:: django.views.generic.edit.FormView A view that displays a form. On error, redisplays the form with validation @@ -69,6 +72,8 @@ editing content: form.send_email() return super(ContactView, self).form_valid(form) +CreateView +---------- .. class:: django.views.generic.edit.CreateView @@ -94,7 +99,7 @@ editing content: .. attribute:: template_name_suffix The CreateView page displayed to a GET request uses a - ``template_name_suffix`` of ``'_form.html'``. For + ``template_name_suffix`` of ``'_form.html'``. For example, changing this attribute to ``'_create_form.html'`` for a view creating objects for the the example `Author` model would cause the the default `template_name` to be ``'myapp/author_create_form.html'``. @@ -107,6 +112,9 @@ editing content: class AuthorCreate(CreateView): model = Author +UpdateView +---------- + .. class:: django.views.generic.edit.UpdateView A view that displays a form for editing an existing object, redisplaying @@ -133,10 +141,10 @@ editing content: .. attribute:: template_name_suffix The UpdateView page displayed to a GET request uses a - ``template_name_suffix`` of ``'_form.html'``. For + ``template_name_suffix`` of ``'_form.html'``. For example, changing this attribute to ``'_update_form.html'`` for a view updating objects for the the example `Author` model would cause the the - default `template_name` to be ``'myapp/author_update_form.html'``. + default `template_name` to be ``'myapp/author_update_form.html'``. **Example views.py**:: @@ -146,6 +154,9 @@ editing content: class AuthorUpdate(UpdateView): model = Author +DeleteView +---------- + .. class:: django.views.generic.edit.DeleteView A view that displays a confirmation page and deletes an existing object. @@ -171,10 +182,10 @@ editing content: .. attribute:: template_name_suffix The DeleteView page displayed to a GET request uses a - ``template_name_suffix`` of ``'_confirm_delete.html'``. For + ``template_name_suffix`` of ``'_confirm_delete.html'``. For example, changing this attribute to ``'_check_delete.html'`` for a view deleting objects for the the example `Author` model would cause the the - default `template_name` to be ``'myapp/author_check_delete.html'``. + default `template_name` to be ``'myapp/author_check_delete.html'``. **Example views.py**:: @@ -185,4 +196,4 @@ editing content: class AuthorDelete(DeleteView): model = Author - success_url = reverse_lazy('author-list') + success_url = reverse_lazy('author-list') diff --git a/docs/ref/class-based-views/index.txt b/docs/ref/class-based-views/index.txt index f2271d2506..9ed0762533 100644 --- a/docs/ref/class-based-views/index.txt +++ b/docs/ref/class-based-views/index.txt @@ -6,7 +6,7 @@ Class-based views API reference. For introductory material, see :doc:`/topics/class-based-views/index`. .. toctree:: - :maxdepth: 1 + :maxdepth: 3 base generic-display diff --git a/docs/ref/class-based-views/mixins-date-based.txt b/docs/ref/class-based-views/mixins-date-based.txt index a65471e68d..6bf6f10b5d 100644 --- a/docs/ref/class-based-views/mixins-date-based.txt +++ b/docs/ref/class-based-views/mixins-date-based.txt @@ -3,6 +3,9 @@ Date-based mixins ================= +YearMixin +--------- + .. class:: django.views.generic.dates.YearMixin A mixin that can be used to retrieve and provide parsing information for a @@ -36,6 +39,9 @@ Date-based mixins Raises a 404 if no valid year specification can be found. +MonthMixin +---------- + .. class:: django.views.generic.dates.MonthMixin A mixin that can be used to retrieve and provide parsing information for a @@ -82,6 +88,9 @@ Date-based mixins date provided. If ``allow_empty = False``, returns the previous month that contained data. +DayMixin +-------- + .. class:: django.views.generic.dates.DayMixin A mixin that can be used to retrieve and provide parsing information for a @@ -127,6 +136,9 @@ Date-based mixins Returns a date object containing the previous day. If ``allow_empty = False``, returns the previous day that contained data. +WeekMixin +--------- + .. class:: django.views.generic.dates.WeekMixin A mixin that can be used to retrieve and provide parsing information for a @@ -161,6 +173,9 @@ Date-based mixins Raises a 404 if no valid week specification can be found. +DateMixin +--------- + .. class:: django.views.generic.dates.DateMixin A mixin class providing common behavior for all date-based views. @@ -204,6 +219,9 @@ Date-based mixins is greater than the current date/time. Returns :attr:`DateMixin.allow_future` by default. +BaseDateListView +---------------- + .. class:: django.views.generic.dates.BaseDateListView A base class that provides common behavior for all date-based views. There diff --git a/docs/ref/class-based-views/mixins-editing.txt b/docs/ref/class-based-views/mixins-editing.txt index 89610889db..95dd24f442 100644 --- a/docs/ref/class-based-views/mixins-editing.txt +++ b/docs/ref/class-based-views/mixins-editing.txt @@ -14,6 +14,9 @@ The following mixins are used to construct Django's editing views: Examples of how these are combined into editing views can be found at the documentation on ``Generic editing views``. +FormMixin +--------- + .. class:: django.views.generic.edit.FormMixin A mixin class that provides facilities for creating and displaying forms. @@ -90,6 +93,9 @@ The following mixins are used to construct Django's editing views: :meth:`~django.views.generic.FormMixin.form_invalid`. +ModelFormMixin +-------------- + .. class:: django.views.generic.edit.ModelFormMixin A form mixin that works on ModelForms, rather than a standalone form. @@ -147,12 +153,16 @@ The following mixins are used to construct Django's editing views: .. method:: form_invalid() Renders a response, providing the invalid form as context. - + + +ProcessFormView +--------------- + .. class:: django.views.generic.edit.ProcessFormView A mixin that provides basic HTTP GET and POST workflow. - .. note:: + .. note:: This is named 'ProcessFormView' and inherits directly from :class:`django.views.generic.base.View`, but breaks if used diff --git a/docs/ref/class-based-views/mixins-multiple-object.txt b/docs/ref/class-based-views/mixins-multiple-object.txt index b414355c6b..8bc613b887 100644 --- a/docs/ref/class-based-views/mixins-multiple-object.txt +++ b/docs/ref/class-based-views/mixins-multiple-object.txt @@ -2,6 +2,9 @@ Multiple object mixins ====================== +MultipleObjectMixin +------------------- + .. class:: django.views.generic.list.MultipleObjectMixin A mixin that can be used to display a list of objects. @@ -148,6 +151,9 @@ Multiple object mixins this context variable will be ``None``. +MultipleObjectTemplateResponseMixin +----------------------------------- + .. class:: django.views.generic.list.MultipleObjectTemplateResponseMixin A mixin class that performs template-based response rendering for views diff --git a/docs/ref/class-based-views/mixins-simple.txt b/docs/ref/class-based-views/mixins-simple.txt index 33d0db3134..085c8a5fc7 100644 --- a/docs/ref/class-based-views/mixins-simple.txt +++ b/docs/ref/class-based-views/mixins-simple.txt @@ -2,6 +2,9 @@ Simple mixins ============= +ContextMixin +------------ + .. class:: django.views.generic.base.ContextMixin .. versionadded:: 1.5 @@ -17,6 +20,9 @@ Simple mixins Returns a dictionary representing the template context. The keyword arguments provided will make up the returned context. +TemplateResponseMixin +--------------------- + .. class:: django.views.generic.base.TemplateResponseMixin Provides a mechanism to construct a diff --git a/docs/ref/class-based-views/mixins-single-object.txt b/docs/ref/class-based-views/mixins-single-object.txt index a31608dbd4..77f52b96c6 100644 --- a/docs/ref/class-based-views/mixins-single-object.txt +++ b/docs/ref/class-based-views/mixins-single-object.txt @@ -2,6 +2,9 @@ Single object mixins ==================== +SingleObjectMixin +----------------- + .. class:: django.views.generic.detail.SingleObjectMixin Provides a mechanism for looking up an object associated with the @@ -86,6 +89,9 @@ Single object mixins ``context_object_name`` is specified, that variable will also be set in the context, with the same value as ``object``. +SingleObjectTemplateResponseMixin +--------------------------------- + .. class:: django.views.generic.detail.SingleObjectTemplateResponseMixin A mixin class that performs template-based response rendering for views From 97fe70d30bddfa24bf5ef5d2148a1a5ae6bf54b2 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 11 Aug 2012 11:11:20 +0200 Subject: [PATCH 006/253] [py3] Used BytesIO to test request streams --- tests/regressiontests/requests/tests.py | 56 ++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py index f192459246..f9e1112b2e 100644 --- a/tests/regressiontests/requests/tests.py +++ b/tests/regressiontests/requests/tests.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import time import warnings from datetime import datetime, timedelta +from io import BytesIO from django.conf import settings from django.core.handlers.wsgi import WSGIRequest, LimitedStream @@ -10,17 +11,16 @@ from django.http import HttpRequest, HttpResponse, parse_cookie, build_request_r from django.test.utils import str_prefix from django.utils import unittest from django.utils.http import cookie_date -from django.utils.six import StringIO from django.utils.timezone import utc class RequestsTests(unittest.TestCase): def test_httprequest(self): request = HttpRequest() - self.assertEqual(request.GET.keys(), []) - self.assertEqual(request.POST.keys(), []) - self.assertEqual(request.COOKIES.keys(), []) - self.assertEqual(request.META.keys(), []) + self.assertEqual(list(request.GET.keys()), []) + self.assertEqual(list(request.POST.keys()), []) + self.assertEqual(list(request.COOKIES.keys()), []) + self.assertEqual(list(request.META.keys()), []) def test_httprequest_repr(self): request = HttpRequest() @@ -35,17 +35,17 @@ class RequestsTests(unittest.TestCase): str_prefix("")) def test_wsgirequest(self): - request = WSGIRequest({'PATH_INFO': 'bogus', 'REQUEST_METHOD': 'bogus', 'wsgi.input': StringIO('')}) - self.assertEqual(request.GET.keys(), []) - self.assertEqual(request.POST.keys(), []) - self.assertEqual(request.COOKIES.keys(), []) + request = WSGIRequest({'PATH_INFO': 'bogus', 'REQUEST_METHOD': 'bogus', 'wsgi.input': BytesIO(b'')}) + self.assertEqual(list(request.GET.keys()), []) + self.assertEqual(list(request.POST.keys()), []) + self.assertEqual(list(request.COOKIES.keys()), []) self.assertEqual(set(request.META.keys()), set(['PATH_INFO', 'REQUEST_METHOD', 'SCRIPT_NAME', 'wsgi.input'])) self.assertEqual(request.META['PATH_INFO'], 'bogus') self.assertEqual(request.META['REQUEST_METHOD'], 'bogus') self.assertEqual(request.META['SCRIPT_NAME'], '') def test_wsgirequest_repr(self): - request = WSGIRequest({'PATH_INFO': '/somepath/', 'REQUEST_METHOD': 'get', 'wsgi.input': StringIO('')}) + request = WSGIRequest({'PATH_INFO': '/somepath/', 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')}) request.GET = {'get-key': 'get-value'} request.POST = {'post-key': 'post-value'} request.COOKIES = {'post-key': 'post-value'} @@ -207,26 +207,26 @@ class RequestsTests(unittest.TestCase): def test_limited_stream(self): # Read all of a limited stream - stream = LimitedStream(StringIO(b'test'), 2) + stream = LimitedStream(BytesIO(b'test'), 2) self.assertEqual(stream.read(), b'te') # Reading again returns nothing. self.assertEqual(stream.read(), b'') # Read a number of characters greater than the stream has to offer - stream = LimitedStream(StringIO(b'test'), 2) + stream = LimitedStream(BytesIO(b'test'), 2) self.assertEqual(stream.read(5), b'te') # Reading again returns nothing. self.assertEqual(stream.readline(5), b'') # Read sequentially from a stream - stream = LimitedStream(StringIO(b'12345678'), 8) + stream = LimitedStream(BytesIO(b'12345678'), 8) self.assertEqual(stream.read(5), b'12345') self.assertEqual(stream.read(5), b'678') # Reading again returns nothing. self.assertEqual(stream.readline(5), b'') # Read lines from a stream - stream = LimitedStream(StringIO(b'1234\n5678\nabcd\nefgh\nijkl'), 24) + stream = LimitedStream(BytesIO(b'1234\n5678\nabcd\nefgh\nijkl'), 24) # Read a full line, unconditionally self.assertEqual(stream.readline(), b'1234\n') # Read a number of characters less than a line @@ -246,7 +246,7 @@ class RequestsTests(unittest.TestCase): # If a stream contains a newline, but the provided length # is less than the number of provided characters, the newline # doesn't reset the available character count - stream = LimitedStream(StringIO(b'1234\nabcdef'), 9) + stream = LimitedStream(BytesIO(b'1234\nabcdef'), 9) self.assertEqual(stream.readline(10), b'1234\n') self.assertEqual(stream.readline(3), b'abc') # Now expire the available characters @@ -255,7 +255,7 @@ class RequestsTests(unittest.TestCase): self.assertEqual(stream.readline(2), b'') # Same test, but with read, not readline. - stream = LimitedStream(StringIO(b'1234\nabcdef'), 9) + stream = LimitedStream(BytesIO(b'1234\nabcdef'), 9) self.assertEqual(stream.read(6), b'1234\na') self.assertEqual(stream.read(2), b'bc') self.assertEqual(stream.read(2), b'd') @@ -266,7 +266,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.read(), b'name=value') def test_read_after_value(self): @@ -277,7 +277,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.POST, {'name': ['value']}) self.assertEqual(request.body, b'name=value') self.assertEqual(request.read(), b'name=value') @@ -290,7 +290,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.read(2), b'na') self.assertRaises(Exception, lambda: request.body) self.assertEqual(request.POST, {}) @@ -312,7 +312,7 @@ class RequestsTests(unittest.TestCase): request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.POST, {'name': ['value']}) self.assertRaises(Exception, lambda: request.body) @@ -334,14 +334,14 @@ class RequestsTests(unittest.TestCase): request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': 0, - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.POST, {}) def test_read_by_lines(self): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(list(request), [b'name=value']) def test_POST_after_body_read(self): @@ -351,7 +351,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) raw_data = request.body self.assertEqual(request.POST, {'name': ['value']}) @@ -363,7 +363,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) raw_data = request.body self.assertEqual(request.read(1), b'n') self.assertEqual(request.POST, {'name': ['value']}) @@ -383,7 +383,7 @@ class RequestsTests(unittest.TestCase): request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) raw_data = request.body # Consume enough data to mess up the parsing: self.assertEqual(request.read(13), b'--boundary\r\nC') @@ -397,7 +397,7 @@ class RequestsTests(unittest.TestCase): request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload) + 'wsgi.input': BytesIO(payload) }) with warnings.catch_warnings(record=True): @@ -408,14 +408,14 @@ class RequestsTests(unittest.TestCase): If wsgi.input.read() raises an exception while trying to read() the POST, the exception should be identifiable (not a generic IOError). """ - class ExplodingStringIO(StringIO): + class ExplodingBytesIO(BytesIO): def read(self, len=0): raise IOError("kaboom!") payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': ExplodingStringIO(payload)}) + 'wsgi.input': ExplodingBytesIO(payload)}) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") From 22527a821bbca73a4900906be8b01c81a414ad95 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 11 Aug 2012 13:34:35 +0200 Subject: [PATCH 007/253] [py3] Fixed str_prefix test utility --- django/test/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/test/utils.py b/django/test/utils.py index 4b121bdfb0..e2c439fd80 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -221,4 +221,4 @@ class override_settings(object): setting=key, value=new_value) def str_prefix(s): - return s % {'_': 'u'} + return s % {'_': '' if six.PY3 else 'u'} From f10a1b06416e59a66cc9c3c8437fe045cb0fb03c Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 11 Aug 2012 14:44:34 +0200 Subject: [PATCH 008/253] [py3] Fixed Python 3 compatibility of http handling * Using str() when Python 2 expects bytes and Python 3 Unicode * Fixed reraise-ing syntax * Fixed slicing of byte strings --- django/http/__init__.py | 8 ++++---- django/http/multipartparser.py | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/django/http/__init__.py b/django/http/__init__.py index 05824ad1d9..f2e1cda8ff 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -61,14 +61,14 @@ else: if not _cookie_allows_colon_in_names: def load(self, rawdata): self.bad_cookies = set() - super(SimpleCookie, self).load(smart_bytes(rawdata)) + super(SimpleCookie, self).load(str(rawdata)) for key in self.bad_cookies: del self[key] # override private __set() method: # (needed for using our Morsel, and for laxness with CookieError def _BaseCookie__set(self, key, real_value, coded_value): - key = smart_bytes(key) + key = str(key) try: M = self.get(key, Morsel()) M.set(key, real_value, coded_value) @@ -137,7 +137,7 @@ def build_request_repr(request, path_override=None, GET_override=None, except: meta = '' path = path_override if path_override is not None else request.path - return smart_bytes('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % + return str('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % (request.__class__.__name__, path, six.text_type(get), @@ -294,7 +294,7 @@ class HttpRequest(object): try: self._body = self.read() except IOError as e: - six.reraise(UnreadablePostError, e, sys.exc_traceback) + six.reraise(UnreadablePostError, UnreadablePostError(*tuple(e.args)), sys.exc_info()[2]) self._stream = BytesIO(self._body) return self._body diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py index 9c66827cbb..a324935d3d 100644 --- a/django/http/multipartparser.py +++ b/django/http/multipartparser.py @@ -507,9 +507,11 @@ class BoundaryIter(object): end = index next = index + len(self._boundary) # backup over CRLF - if data[max(0,end-1)] == b'\n': + last = max(0, end-1) + if data[last:last+1] == b'\n': end -= 1 - if data[max(0,end-1)] == b'\r': + last = max(0, end-1) + if data[last:last+1] == b'\r': end -= 1 return end, next @@ -613,7 +615,7 @@ def parse_header(line): if i >= 0: name = p[:i].strip().lower().decode('ascii') value = p[i+1:].strip() - if len(value) >= 2 and value[0] == value[-1] == b'"': + if len(value) >= 2 and value[:1] == value[-1:] == b'"': value = value[1:-1] value = value.replace(b'\\\\', b'\\').replace(b'\\"', b'"') pdict[name] = value From 09c589810d54ef14132084e34f4ef6133fdac3b9 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 11 Aug 2012 15:29:29 +0200 Subject: [PATCH 009/253] [py3] Used smart_str to prevent regressions in http handling --- django/http/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/django/http/__init__.py b/django/http/__init__.py index f2e1cda8ff..85d9b3b920 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -61,14 +61,14 @@ else: if not _cookie_allows_colon_in_names: def load(self, rawdata): self.bad_cookies = set() - super(SimpleCookie, self).load(str(rawdata)) + super(SimpleCookie, self).load(smart_str(rawdata)) for key in self.bad_cookies: del self[key] # override private __set() method: # (needed for using our Morsel, and for laxness with CookieError def _BaseCookie__set(self, key, real_value, coded_value): - key = str(key) + key = smart_str(key) try: M = self.get(key, Morsel()) M.set(key, real_value, coded_value) @@ -85,7 +85,7 @@ from django.core.files import uploadhandler from django.http.multipartparser import MultiPartParser from django.http.utils import * from django.utils.datastructures import MultiValueDict, ImmutableList -from django.utils.encoding import smart_bytes, iri_to_uri, force_text +from django.utils.encoding import smart_bytes, smart_str, iri_to_uri, force_text from django.utils.http import cookie_date from django.utils import six from django.utils import timezone @@ -137,7 +137,7 @@ def build_request_repr(request, path_override=None, GET_override=None, except: meta = '' path = path_override if path_override is not None else request.path - return str('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % + return smart_str('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % (request.__class__.__name__, path, six.text_type(get), From 3eb28d011927a657cad1bf4463611a72c6bb1325 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 11 Aug 2012 18:56:14 +0200 Subject: [PATCH 010/253] [py3] Used six.StringIO to simulate stdout buffer in tests --- tests/modeltests/invalid_models/tests.py | 4 ++-- tests/regressiontests/fixtures_regress/tests.py | 10 +++++----- tests/regressiontests/m2m_through_regress/tests.py | 9 ++++----- tests/regressiontests/staticfiles_tests/tests.py | 5 ++--- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/tests/modeltests/invalid_models/tests.py b/tests/modeltests/invalid_models/tests.py index 9d7adb045c..e1fc68743e 100644 --- a/tests/modeltests/invalid_models/tests.py +++ b/tests/modeltests/invalid_models/tests.py @@ -1,11 +1,11 @@ import copy import sys -from io import BytesIO from django.core.management.validation import get_validation_errors from django.db.models.loading import cache, load_app from django.utils import unittest +from django.utils.six import StringIO class InvalidModelTestCase(unittest.TestCase): @@ -16,7 +16,7 @@ class InvalidModelTestCase(unittest.TestCase): # coloring attached (makes matching the results easier). We restore # sys.stderr afterwards. self.old_stdout = sys.stdout - self.stdout = BytesIO() + self.stdout = StringIO() sys.stdout = self.stdout # This test adds dummy applications to the app cache. These diff --git a/tests/regressiontests/fixtures_regress/tests.py b/tests/regressiontests/fixtures_regress/tests.py index ab93341699..41be31ccd7 100644 --- a/tests/regressiontests/fixtures_regress/tests.py +++ b/tests/regressiontests/fixtures_regress/tests.py @@ -4,7 +4,6 @@ from __future__ import absolute_import, unicode_literals import os import re -from io import BytesIO from django.core import management from django.core.management.base import CommandError @@ -14,6 +13,7 @@ from django.db.models import signals from django.test import (TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature) from django.test.utils import override_settings +from django.utils.six import StringIO from .models import (Animal, Stuff, Absolute, Parent, Child, Article, Widget, Store, Person, Book, NKChild, RefToNKChild, Circle1, Circle2, Circle3, @@ -276,7 +276,7 @@ class TestFixtures(TestCase): ) animal.save() - stdout = BytesIO() + stdout = StringIO() management.call_command( 'dumpdata', 'fixtures_regress.animal', @@ -305,7 +305,7 @@ class TestFixtures(TestCase): """ Regression for #11428 - Proxy models aren't included when you dumpdata """ - stdout = BytesIO() + stdout = StringIO() # Create an instance of the concrete class widget = Widget.objects.create(name='grommet') management.call_command( @@ -380,7 +380,7 @@ class TestFixtures(TestCase): ) def test_loaddata_not_existant_fixture_file(self): - stdout_output = BytesIO() + stdout_output = StringIO() management.call_command( 'loaddata', 'this_fixture_doesnt_exist', @@ -465,7 +465,7 @@ class NaturalKeyFixtureTests(TestCase): commit=False ) - stdout = BytesIO() + stdout = StringIO() management.call_command( 'dumpdata', 'fixtures_regress.book', diff --git a/tests/regressiontests/m2m_through_regress/tests.py b/tests/regressiontests/m2m_through_regress/tests.py index 73a13654e7..458c194f89 100644 --- a/tests/regressiontests/m2m_through_regress/tests.py +++ b/tests/regressiontests/m2m_through_regress/tests.py @@ -1,10 +1,9 @@ from __future__ import absolute_import -from io import BytesIO - from django.core import management from django.contrib.auth.models import User from django.test import TestCase +from django.utils.six import StringIO from .models import (Person, Group, Membership, UserMembership, Car, Driver, CarDriver) @@ -70,11 +69,11 @@ class M2MThroughTestCase(TestCase): pks = {"p_pk": p.pk, "g_pk": g.pk, "m_pk": m.pk} - out = BytesIO() + out = StringIO() management.call_command("dumpdata", "m2m_through_regress", format="json", stdout=out) self.assertEqual(out.getvalue().strip(), """[{"pk": %(m_pk)s, "model": "m2m_through_regress.membership", "fields": {"person": %(p_pk)s, "price": 100, "group": %(g_pk)s}}, {"pk": %(p_pk)s, "model": "m2m_through_regress.person", "fields": {"name": "Bob"}}, {"pk": %(g_pk)s, "model": "m2m_through_regress.group", "fields": {"name": "Roll"}}]""" % pks) - out = BytesIO() + out = StringIO() management.call_command("dumpdata", "m2m_through_regress", format="xml", indent=2, stdout=out) self.assertEqual(out.getvalue().strip(), """ @@ -142,6 +141,6 @@ class ThroughLoadDataTestCase(TestCase): def test_sequence_creation(self): "Check that sequences on an m2m_through are created for the through model, not a phantom auto-generated m2m table. Refs #11107" - out = BytesIO() + out = StringIO() management.call_command("dumpdata", "m2m_through_regress", format="json", stdout=out) self.assertEqual(out.getvalue().strip(), """[{"pk": 1, "model": "m2m_through_regress.usermembership", "fields": {"price": 100, "group": 1, "user": 1}}, {"pk": 1, "model": "m2m_through_regress.person", "fields": {"name": "Guido"}}, {"pk": 1, "model": "m2m_through_regress.group", "fields": {"name": "Python Core Group"}}]""") diff --git a/tests/regressiontests/staticfiles_tests/tests.py b/tests/regressiontests/staticfiles_tests/tests.py index 19951f100b..d68f211535 100644 --- a/tests/regressiontests/staticfiles_tests/tests.py +++ b/tests/regressiontests/staticfiles_tests/tests.py @@ -7,7 +7,6 @@ import posixpath import shutil import sys import tempfile -from io import BytesIO from django.template import loader, Context from django.conf import settings @@ -194,7 +193,7 @@ class TestFindStatic(CollectionTestCase, TestDefaults): Test ``findstatic`` management command. """ def _get_file(self, filepath): - out = BytesIO() + out = six.StringIO() call_command('findstatic', filepath, all=False, verbosity=0, stdout=out) out.seek(0) lines = [l.strip() for l in out.readlines()] @@ -206,7 +205,7 @@ class TestFindStatic(CollectionTestCase, TestDefaults): """ Test that findstatic returns all candidate files if run without --first. """ - out = BytesIO() + out = six.StringIO() call_command('findstatic', 'test/file.txt', verbosity=0, stdout=out) out.seek(0) lines = [l.strip() for l in out.readlines()] From 4d9e4c64f1330fbdfe6a7931879deebf7154f403 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sat, 11 Aug 2012 21:46:36 +0200 Subject: [PATCH 011/253] =?UTF-8?q?Fixed=20#18698=20--=20Configure=20latex?= =?UTF-8?q?=20to=20support=20'=E2=89=A5'=20in=20the=20docs.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to simonb for the report and the initial patch. --- docs/conf.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 39a280e464..9ac93d7cbc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -190,11 +190,9 @@ modindex_common_prefix = ["django."] # -- Options for LaTeX output -------------------------------------------------- -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +latex_elements = { + 'preamble': '\\DeclareUnicodeCharacter{2265}{\\ensuremath{\\ge}}' +} # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). @@ -218,9 +216,6 @@ latex_documents = [ # If true, show URL addresses after external links. #latex_show_urls = False -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - # Documents to append as an appendix to all manuals. #latex_appendices = [] From b82eb10b2605477b3e583c9378257a9e6c8fd2ed Mon Sep 17 00:00:00 2001 From: Karen Tracey Date: Sat, 11 Aug 2012 17:05:26 -0400 Subject: [PATCH 012/253] Fixed #18754 -- cache keys created by post_process Corrected to always generate the cache keys from file names with forward slashes, not backslashes. --- django/contrib/staticfiles/storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py index 2ca54dde71..3b431edf94 100644 --- a/django/contrib/staticfiles/storage.py +++ b/django/contrib/staticfiles/storage.py @@ -261,7 +261,7 @@ class CachedFilesMixin(object): hashed_name = force_text(saved_name.replace('\\', '/')) # and then set the cache accordingly - hashed_paths[self.cache_key(name)] = hashed_name + hashed_paths[self.cache_key(name.replace('\\', '/'))] = hashed_name yield name, hashed_name, processed # Finally set the cache From 7d0f8831922535502569a5dda989dde339b4e491 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 11 Aug 2012 20:58:45 +0200 Subject: [PATCH 013/253] [py3] Fixed JSON deserialization --- django/core/serializers/json.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py index 3bac24d33a..4ba0d7fd79 100644 --- a/django/core/serializers/json.py +++ b/django/core/serializers/json.py @@ -61,13 +61,12 @@ def Deserializer(stream_or_string, **options): """ Deserialize a stream or string of JSON data. """ + if not isinstance(stream_or_string, (bytes, six.string_types)): + stream_or_string = stream_or_string.read() if isinstance(stream_or_string, bytes): stream_or_string = stream_or_string.decode('utf-8') try: - if isinstance(stream_or_string, six.string_types): - objects = json.loads(stream_or_string) - else: - objects = json.load(stream_or_string) + objects = json.loads(stream_or_string) for obj in PythonDeserializer(objects, **options): yield obj except GeneratorExit: From 66021033381f154ddf8a9ecf93fcbb41a7abda17 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 11 Aug 2012 22:29:18 +0200 Subject: [PATCH 014/253] [py3] Various minor syntax fixes in the test suite --- tests/modeltests/files/tests.py | 2 +- tests/regressiontests/decorators/tests.py | 2 +- tests/regressiontests/fixtures_regress/tests.py | 5 +++-- tests/regressiontests/forms/tests/models.py | 4 ++-- tests/regressiontests/generic_views/base.py | 2 +- tests/regressiontests/generic_views/dates.py | 2 +- tests/regressiontests/httpwrappers/tests.py | 16 ++++++++-------- tests/regressiontests/views/tests/shortcuts.py | 14 +++++++------- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/modeltests/files/tests.py b/tests/modeltests/files/tests.py index 565d4c8942..daf2209605 100644 --- a/tests/modeltests/files/tests.py +++ b/tests/modeltests/files/tests.py @@ -135,6 +135,6 @@ class FileTests(unittest.TestCase): def test_file_mode(self): # Should not set mode to None if it is not present. # See #14681, stdlib gzip module crashes if mode is set to None - file = SimpleUploadedFile("mode_test.txt", "content") + file = SimpleUploadedFile("mode_test.txt", b"content") self.assertFalse(hasattr(file, 'mode')) g = gzip.GzipFile(fileobj=file) diff --git a/tests/regressiontests/decorators/tests.py b/tests/regressiontests/decorators/tests.py index dceaa62abc..7dc36aa479 100644 --- a/tests/regressiontests/decorators/tests.py +++ b/tests/regressiontests/decorators/tests.py @@ -220,7 +220,7 @@ class MethodDecoratorTests(TestCase): self.assertEqual(getattr(Test.method, 'myattr2', False), True) self.assertEqual(Test.method.__doc__, 'A method') - self.assertEqual(Test.method.__func__.__name__, 'method') + self.assertEqual(Test.method.__name__, 'method') class XFrameOptionsDecoratorsTests(TestCase): diff --git a/tests/regressiontests/fixtures_regress/tests.py b/tests/regressiontests/fixtures_regress/tests.py index 41be31ccd7..89d754c58e 100644 --- a/tests/regressiontests/fixtures_regress/tests.py +++ b/tests/regressiontests/fixtures_regress/tests.py @@ -13,7 +13,7 @@ from django.db.models import signals from django.test import (TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature) from django.test.utils import override_settings -from django.utils.six import StringIO +from django.utils.six import PY3, StringIO from .models import (Animal, Stuff, Absolute, Parent, Child, Article, Widget, Store, Person, Book, NKChild, RefToNKChild, Circle1, Circle2, Circle3, @@ -244,7 +244,8 @@ class TestFixtures(TestCase): self.assertEqual( pre_save_checks, [ - ("Count = 42 ()", "Weight = 1.2 ()") + ("Count = 42 (<%s 'int'>)" % ('class' if PY3 else 'type'), + "Weight = 1.2 (<%s 'float'>)" % ('class' if PY3 else 'type')) ] ) finally: diff --git a/tests/regressiontests/forms/tests/models.py b/tests/regressiontests/forms/tests/models.py index 7687335b48..c351509cee 100644 --- a/tests/regressiontests/forms/tests/models.py +++ b/tests/regressiontests/forms/tests/models.py @@ -178,7 +178,7 @@ class RelatedModelFormTests(TestCase): class Meta: model=A - self.assertRaises(ValueError, ModelFormMetaclass, b'Form', (ModelForm,), {'Meta': Meta}) + self.assertRaises(ValueError, ModelFormMetaclass, str('Form'), (ModelForm,), {'Meta': Meta}) class B(models.Model): pass @@ -196,4 +196,4 @@ class RelatedModelFormTests(TestCase): class Meta: model=A - self.assertTrue(issubclass(ModelFormMetaclass(b'Form', (ModelForm,), {'Meta': Meta}), ModelForm)) + self.assertTrue(issubclass(ModelFormMetaclass(str('Form'), (ModelForm,), {'Meta': Meta}), ModelForm)) diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py index fc3dc05fd7..5296f6941a 100644 --- a/tests/regressiontests/generic_views/base.py +++ b/tests/regressiontests/generic_views/base.py @@ -69,7 +69,7 @@ class ViewTest(unittest.TestCase): def _assert_simple(self, response): self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'This is a simple view') + self.assertEqual(response.content, b'This is a simple view') def test_no_init_kwargs(self): """ diff --git a/tests/regressiontests/generic_views/dates.py b/tests/regressiontests/generic_views/dates.py index ad65fd9f10..808582a6a5 100644 --- a/tests/regressiontests/generic_views/dates.py +++ b/tests/regressiontests/generic_views/dates.py @@ -480,7 +480,7 @@ class DayArchiveViewTests(TestCase): def test_next_prev_context(self): res = self.client.get('/dates/books/2008/oct/01/') - self.assertEqual(res.content, "Archive for Oct. 1, 2008. Previous day is May 1, 2006") + self.assertEqual(res.content, b"Archive for Oct. 1, 2008. Previous day is May 1, 2006") def test_custom_month_format(self): res = self.client.get('/dates/books/2008/10/01/') diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py index 0f61c2d840..ad0ce4197e 100644 --- a/tests/regressiontests/httpwrappers/tests.py +++ b/tests/regressiontests/httpwrappers/tests.py @@ -260,29 +260,29 @@ class HttpResponseTests(unittest.TestCase): def test_non_string_content(self): #Bug 16494: HttpResponse should behave consistently with non-strings r = HttpResponse(12345) - self.assertEqual(r.content, '12345') + self.assertEqual(r.content, b'12345') #test content via property r = HttpResponse() r.content = 12345 - self.assertEqual(r.content, '12345') + self.assertEqual(r.content, b'12345') def test_iter_content(self): r = HttpResponse(['abc', 'def', 'ghi']) - self.assertEqual(r.content, 'abcdefghi') + self.assertEqual(r.content, b'abcdefghi') #test iter content via property r = HttpResponse() r.content = ['idan', 'alex', 'jacob'] - self.assertEqual(r.content, 'idanalexjacob') + self.assertEqual(r.content, b'idanalexjacob') r = HttpResponse() r.content = [1, 2, 3] - self.assertEqual(r.content, '123') + self.assertEqual(r.content, b'123') #test retrieval explicitly using iter and odd inputs r = HttpResponse() - r.content = ['1', '2', 3, unichr(1950)] + r.content = ['1', '2', 3, '\u079e'] result = [] my_iter = r.__iter__() while True: @@ -297,8 +297,8 @@ class HttpResponseTests(unittest.TestCase): #with Content-Encoding header r = HttpResponse([1,1,2,4,8]) r['Content-Encoding'] = 'winning' - self.assertEqual(r.content, '11248') - r.content = [unichr(1950),] + self.assertEqual(r.content, b'11248') + r.content = ['\u079e',] self.assertRaises(UnicodeEncodeError, getattr, r, 'content') diff --git a/tests/regressiontests/views/tests/shortcuts.py b/tests/regressiontests/views/tests/shortcuts.py index a48bb25d0b..62bd82f4a7 100644 --- a/tests/regressiontests/views/tests/shortcuts.py +++ b/tests/regressiontests/views/tests/shortcuts.py @@ -12,44 +12,44 @@ class ShortcutTests(TestCase): def test_render_to_response(self): response = self.client.get('/shortcuts/render_to_response/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR..\n') + self.assertEqual(response.content, b'FOO.BAR..\n') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_render_to_response_with_request_context(self): response = self.client.get('/shortcuts/render_to_response/request_context/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_render_to_response_with_mimetype(self): response = self.client.get('/shortcuts/render_to_response/mimetype/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR..\n') + self.assertEqual(response.content, b'FOO.BAR..\n') self.assertEqual(response['Content-Type'], 'application/x-rendertest') def test_render(self): response = self.client.get('/shortcuts/render/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response.context.current_app, None) def test_render_with_base_context(self): response = self.client.get('/shortcuts/render/base_context/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR..\n') + self.assertEqual(response.content, b'FOO.BAR..\n') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_render_with_content_type(self): response = self.client.get('/shortcuts/render/content_type/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') self.assertEqual(response['Content-Type'], 'application/x-rendertest') def test_render_with_status(self): response = self.client.get('/shortcuts/render/status/') self.assertEqual(response.status_code, 403) - self.assertEqual(response.content, 'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') def test_render_with_current_app(self): response = self.client.get('/shortcuts/render/current_app/') From 900816464dbd7b1fcc009177f9de03791edc6e88 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 11 Aug 2012 22:44:42 +0200 Subject: [PATCH 015/253] [py3] Re-decoded string after idna encoding --- django/core/validators.py | 4 ++-- django/utils/html.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/django/core/validators.py b/django/core/validators.py index 456fa3cec5..34c397506f 100644 --- a/django/core/validators.py +++ b/django/core/validators.py @@ -57,7 +57,7 @@ class URLValidator(RegexValidator): value = smart_text(value) scheme, netloc, path, query, fragment = urlsplit(value) try: - netloc = netloc.encode('idna') # IDN -> ACE + netloc = netloc.encode('idna').decode('ascii') # IDN -> ACE except UnicodeError: # invalid domain part raise e url = urlunsplit((scheme, netloc, path, query, fragment)) @@ -84,7 +84,7 @@ class EmailValidator(RegexValidator): if value and '@' in value: parts = value.split('@') try: - parts[-1] = parts[-1].encode('idna') + parts[-1] = parts[-1].encode('idna').decode('ascii') except UnicodeError: raise e super(EmailValidator, self).__call__('@'.join(parts)) diff --git a/django/utils/html.py b/django/utils/html.py index 7dd53a1353..647982a15f 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -143,7 +143,7 @@ def smart_urlquote(url): # Handle IDN before quoting. scheme, netloc, path, query, fragment = urlsplit(url) try: - netloc = netloc.encode('idna') # IDN -> ACE + netloc = netloc.encode('idna').decode('ascii') # IDN -> ACE except UnicodeError: # invalid domain part pass else: @@ -206,7 +206,7 @@ def urlize(text, trim_url_limit=None, nofollow=False, autoescape=False): elif not ':' in middle and simple_email_re.match(middle): local, domain = middle.rsplit('@', 1) try: - domain = domain.encode('idna') + domain = domain.encode('idna').decode('ascii') except UnicodeError: continue url = 'mailto:%s@%s' % (local, domain) From be7f1099c61004bda8c491fc4e7aab9f8b2c8512 Mon Sep 17 00:00:00 2001 From: Karen Tracey Date: Sat, 11 Aug 2012 17:32:24 -0400 Subject: [PATCH 016/253] Decorated some tests that require tz support. This allows the test suite to run without errors on Windows. --- tests/modeltests/timezones/tests.py | 1 + tests/regressiontests/generic_views/dates.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/tests/modeltests/timezones/tests.py b/tests/modeltests/timezones/tests.py index a38e4b3f75..4621f0a60c 100644 --- a/tests/modeltests/timezones/tests.py +++ b/tests/modeltests/timezones/tests.py @@ -467,6 +467,7 @@ class NewDatabaseTests(TestCase): [event], transform=lambda d: d) + @requires_tz_support def test_filter_date_field_with_aware_datetime(self): # Regression test for #17742 day = datetime.date(2011, 9, 1) diff --git a/tests/regressiontests/generic_views/dates.py b/tests/regressiontests/generic_views/dates.py index 808582a6a5..d18d10e603 100644 --- a/tests/regressiontests/generic_views/dates.py +++ b/tests/regressiontests/generic_views/dates.py @@ -1,14 +1,27 @@ from __future__ import absolute_import +import time import datetime from django.core.exceptions import ImproperlyConfigured from django.test import TestCase from django.test.utils import override_settings from django.utils import timezone +from django.utils.unittest import skipUnless from .models import Book, BookSigning +TZ_SUPPORT = hasattr(time, 'tzset') + +# On OSes that don't provide tzset (Windows), we can't set the timezone +# in which the program runs. As a consequence, we must skip tests that +# don't enforce a specific timezone (with timezone.override or equivalent), +# or attempt to interpret naive datetimes in the default timezone. + +requires_tz_support = skipUnless(TZ_SUPPORT, + "This test relies on the ability to run a program in an arbitrary " + "time zone, but your operating system isn't able to do that.") + class ArchiveIndexViewTests(TestCase): fixtures = ['generic-views-test-data.json'] @@ -100,6 +113,7 @@ class ArchiveIndexViewTests(TestCase): res = self.client.get('/dates/booksignings/') self.assertEqual(res.status_code, 200) + @requires_tz_support @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') def test_aware_datetime_archive_view(self): BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) @@ -502,6 +516,7 @@ class DayArchiveViewTests(TestCase): res = self.client.get('/dates/booksignings/2008/apr/2/') self.assertEqual(res.status_code, 200) + @requires_tz_support @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') def test_aware_datetime_day_view(self): bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) @@ -578,6 +593,7 @@ class DateDetailViewTests(TestCase): res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) self.assertEqual(res.status_code, 200) + @requires_tz_support @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') def test_aware_datetime_date_detail(self): bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) From 15fffcc7510ec2bf0fafdc254206619a8ab0923f Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 11 Aug 2012 23:43:45 +0200 Subject: [PATCH 017/253] [py3] Fixed reraising of exceptions reraise with second argument to None is not supported. --- django/core/handlers/base.py | 2 +- django/db/models/query.py | 2 +- django/test/client.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 5a6825f0a7..745b6ca3ad 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -225,7 +225,7 @@ class BaseHandler(object): # If Http500 handler is not installed, re-raise last exception if resolver.urlconf_module is None: - six.reraise(exc_info[1], None, exc_info[2]) + six.reraise(*exc_info) # Return an HttpResponse that displays a friendly error message. callback, param_dict = resolver.resolve500() return callback(request, **param_dict) diff --git a/django/db/models/query.py b/django/db/models/query.py index e8d6ae2a7b..3cf794924c 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -472,7 +472,7 @@ class QuerySet(object): return self.get(**lookup), False except self.model.DoesNotExist: # Re-raise the IntegrityError with its original traceback. - six.reraise(exc_info[1], None, exc_info[2]) + six.reraise(*exc_info) def latest(self, field_name=None): """ diff --git a/django/test/client.py b/django/test/client.py index a9ae7f5db1..771f7e0da5 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -385,7 +385,7 @@ class Client(RequestFactory): if self.exc_info: exc_info = self.exc_info self.exc_info = None - six.reraise(exc_info[1], None, exc_info[2]) + six.reraise(*exc_info) # Save the client and request that stimulated the response. response.client = self From 759ae3c2dafe149485a15d9a3bc97527c1ac1163 Mon Sep 17 00:00:00 2001 From: Karen Tracey Date: Sat, 11 Aug 2012 19:15:50 -0400 Subject: [PATCH 018/253] Fixed #18739 -- witdthratio behavior on None args Made behavior given None consistent with how non-numerics were handled. Thanks to ja.geb@me.com for the report. --- django/template/defaulttags.py | 4 ++-- tests/regressiontests/templates/tests.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 14391f08e1..cb2ecd26d8 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -448,7 +448,7 @@ class WidthRatioNode(Node): max_width = int(self.max_width.resolve(context)) except VariableDoesNotExist: return '' - except ValueError: + except (ValueError, TypeError): raise TemplateSyntaxError("widthratio final argument must be an number") try: value = float(value) @@ -456,7 +456,7 @@ class WidthRatioNode(Node): ratio = (value / max_value) * max_width except ZeroDivisionError: return '0' - except ValueError: + except (ValueError, TypeError): return '' return str(int(round(ratio))) diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index edbb21b6bd..a21b7ff323 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -1466,6 +1466,14 @@ class Templates(unittest.TestCase): # #10043: widthratio should allow max_width to be a variable 'widthratio11': ('{% widthratio a b c %}', {'a':50,'b':100, 'c': 100}, '50'), + # #18739: widthratio should handle None args consistently with non-numerics + 'widthratio12a': ('{% widthratio a b c %}', {'a':'a','b':100,'c':100}, ''), + 'widthratio12b': ('{% widthratio a b c %}', {'a':None,'b':100,'c':100}, ''), + 'widthratio13a': ('{% widthratio a b c %}', {'a':0,'b':'b','c':100}, ''), + 'widthratio13b': ('{% widthratio a b c %}', {'a':0,'b':None,'c':100}, ''), + 'widthratio14a': ('{% widthratio a b c %}', {'a':0,'b':100,'c':'c'}, template.TemplateSyntaxError), + 'widthratio14b': ('{% widthratio a b c %}', {'a':0,'b':100,'c':None}, template.TemplateSyntaxError), + ### WITH TAG ######################################################## 'with01': ('{% with key=dict.key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'), 'legacywith01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'), From 140179c77046f45136821db2f902f2da64ec4bb6 Mon Sep 17 00:00:00 2001 From: Martijn Vermaat Date: Sun, 12 Aug 2012 00:01:11 +0300 Subject: [PATCH 019/253] Fix link to Gunicorn website in deployment howto. --- docs/howto/deployment/wsgi/gunicorn.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/howto/deployment/wsgi/gunicorn.txt b/docs/howto/deployment/wsgi/gunicorn.txt index 13d4043e37..c4483291a3 100644 --- a/docs/howto/deployment/wsgi/gunicorn.txt +++ b/docs/howto/deployment/wsgi/gunicorn.txt @@ -13,7 +13,7 @@ There are two ways to use Gunicorn with Django. One is to have Gunicorn treat Django as just another WSGI application. The second is to use Gunicorn's special `integration with Django`_. -.. _integration with Django: http://gunicorn.org/run.html#django-manage-py_ +.. _integration with Django: http://gunicorn.org/run.html#django-manage-py Installing Gunicorn =================== From 9e7b5ba50fd9c23876b0a17a03e6875a6899b7c6 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sun, 12 Aug 2012 12:43:05 +0200 Subject: [PATCH 020/253] Removed missplaced label in the docs. --- docs/internals/contributing/bugs-and-features.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/internals/contributing/bugs-and-features.txt b/docs/internals/contributing/bugs-and-features.txt index 91a078cbc0..53b0e1007d 100644 --- a/docs/internals/contributing/bugs-and-features.txt +++ b/docs/internals/contributing/bugs-and-features.txt @@ -62,8 +62,6 @@ To understand the lifecycle of your ticket once you have created it, refer to .. _django-updates: http://groups.google.com/group/django-updates -.. _reporting-security-issues: - Reporting user interface bugs and features ------------------------------------------ From dbb63e56eaa6e2e2a574d4327464332b3ef9f970 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 11 Aug 2012 22:15:05 +0200 Subject: [PATCH 021/253] [py3] Avoided returning bytes in Model.__str__ on Python 3. --- django/db/models/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index 569b8e876c..3a569c805a 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -407,7 +407,7 @@ class Model(six.with_metaclass(ModelBase, object)): return smart_bytes('<%s: %s>' % (self.__class__.__name__, u)) def __str__(self): - if hasattr(self, '__unicode__'): + if not six.PY3 and hasattr(self, '__unicode__'): return force_text(self).encode('utf-8') return '%s object' % self.__class__.__name__ From 2bb2eecb6358ea4243ab7a6286048b86302f5128 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 11 Aug 2012 22:18:09 +0200 Subject: [PATCH 022/253] [py3] Removed redundant __str__ methods. These classes already have an identical __unicode__ method, which will be used after an upcoming refactoring. --- django/contrib/auth/models.py | 3 --- django/contrib/databrowse/datastructures.py | 3 --- django/core/files/base.py | 3 --- django/template/base.py | 3 --- tests/modeltests/field_subclassing/fields.py | 3 --- tests/modeltests/serializers/models.py | 3 --- 6 files changed, 18 deletions(-) diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 1099aa195b..a39165c92c 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -419,9 +419,6 @@ class AnonymousUser(object): def __unicode__(self): return 'AnonymousUser' - def __str__(self): - return six.text_type(self).encode('utf-8') - def __eq__(self, other): return isinstance(other, self.__class__) diff --git a/django/contrib/databrowse/datastructures.py b/django/contrib/databrowse/datastructures.py index 810e039894..e48383f438 100644 --- a/django/contrib/databrowse/datastructures.py +++ b/django/contrib/databrowse/datastructures.py @@ -97,9 +97,6 @@ class EasyInstance(object): return val[:DISPLAY_SIZE] + '...' return val - def __str__(self): - return self.__unicode__().encode('utf-8') - def pk(self): return self.instance._get_pk_val() diff --git a/django/core/files/base.py b/django/core/files/base.py index d0b25250a5..87fa8bc8ec 100644 --- a/django/core/files/base.py +++ b/django/core/files/base.py @@ -17,9 +17,6 @@ class File(FileProxyMixin): if hasattr(file, 'mode'): self.mode = file.mode - def __str__(self): - return smart_bytes(self.name or '') - def __unicode__(self): return smart_text(self.name or '') diff --git a/django/template/base.py b/django/template/base.py index 661d8c092a..4dcba6dd2b 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -85,9 +85,6 @@ class VariableDoesNotExist(Exception): self.msg = msg self.params = params - def __str__(self): - return six.text_type(self).encode('utf-8') - def __unicode__(self): return self.msg % tuple([force_text(p, errors='replace') for p in self.params]) diff --git a/tests/modeltests/field_subclassing/fields.py b/tests/modeltests/field_subclassing/fields.py index 0d4ff98aa7..47d710717b 100644 --- a/tests/modeltests/field_subclassing/fields.py +++ b/tests/modeltests/field_subclassing/fields.py @@ -18,9 +18,6 @@ class Small(object): def __unicode__(self): return '%s%s' % (force_text(self.first), force_text(self.second)) - def __str__(self): - return six.text_type(self).encode('utf-8') - class SmallField(models.Field): """ Turns the "Small" class into a Django field. Because of the similarities diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py index 9da099c027..ebae7605b4 100644 --- a/tests/modeltests/serializers/models.py +++ b/tests/modeltests/serializers/models.py @@ -87,9 +87,6 @@ class Team(object): def __unicode__(self): raise NotImplementedError("Not so simple") - def __str__(self): - raise NotImplementedError("Not so simple") - def to_string(self): return "%s" % self.title From e7e08fd48b3f88d4eb481f5b213b923fc1db3675 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 11 Aug 2012 22:20:59 +0200 Subject: [PATCH 023/253] [py3] Replaced some __str__ methods by __unicode__ These methods actually return unicode. --- django/core/files/base.py | 2 +- django/db/models/options.py | 8 +++++--- tests/modeltests/delete/models.py | 4 +++- tests/regressiontests/comment_tests/models.py | 8 +++++--- tests/regressiontests/datatypes/models.py | 2 +- tests/regressiontests/string_lookup/models.py | 4 +++- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/django/core/files/base.py b/django/core/files/base.py index 87fa8bc8ec..4ad0be4fd8 100644 --- a/django/core/files/base.py +++ b/django/core/files/base.py @@ -130,7 +130,7 @@ class ContentFile(File): super(ContentFile, self).__init__(BytesIO(content), name=name) self.size = len(content) - def __str__(self): + def __unicode__(self): return 'Raw content' def __bool__(self): diff --git a/django/db/models/options.py b/django/db/models/options.py index 2e8ccb49ce..9211a76b68 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import re from bisect import bisect @@ -8,7 +10,7 @@ from django.db.models.fields import AutoField, FieldDoesNotExist from django.db.models.fields.proxy import OrderWrt from django.db.models.loading import get_models, app_cache_ready from django.utils.translation import activate, deactivate_all, get_language, string_concat -from django.utils.encoding import force_text, smart_bytes +from django.utils.encoding import force_text, smart_text from django.utils.datastructures import SortedDict from django.utils import six @@ -198,8 +200,8 @@ class Options(object): def __repr__(self): return '' % self.object_name - def __str__(self): - return "%s.%s" % (smart_bytes(self.app_label), smart_bytes(self.module_name)) + def __unicode__(self): + return "%s.%s" % (smart_text(self.app_label), smart_text(self.module_name)) def verbose_name_raw(self): """ diff --git a/tests/modeltests/delete/models.py b/tests/modeltests/delete/models.py index f8b78eb7b7..7e7766e824 100644 --- a/tests/modeltests/delete/models.py +++ b/tests/modeltests/delete/models.py @@ -1,10 +1,12 @@ +from __future__ import unicode_literals + from django.db import models class R(models.Model): is_default = models.BooleanField(default=False) - def __str__(self): + def __unicode__(self): return "%s" % self.pk diff --git a/tests/regressiontests/comment_tests/models.py b/tests/regressiontests/comment_tests/models.py index 4093af14b5..7e16e48419 100644 --- a/tests/regressiontests/comment_tests/models.py +++ b/tests/regressiontests/comment_tests/models.py @@ -3,6 +3,8 @@ Comments may be attached to any object. See the comment documentation for more information. """ +from __future__ import unicode_literals + from django.db import models @@ -10,14 +12,14 @@ class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) - def __str__(self): + def __unicode__(self): return '%s %s' % (self.first_name, self.last_name) class Article(models.Model): author = models.ForeignKey(Author) headline = models.CharField(max_length=100) - def __str__(self): + def __unicode__(self): return self.headline class Entry(models.Model): @@ -26,7 +28,7 @@ class Entry(models.Model): pub_date = models.DateField() enable_comments = models.BooleanField() - def __str__(self): + def __unicode__(self): return self.title class Book(models.Model): diff --git a/tests/regressiontests/datatypes/models.py b/tests/regressiontests/datatypes/models.py index 332ce84307..d220d0893a 100644 --- a/tests/regressiontests/datatypes/models.py +++ b/tests/regressiontests/datatypes/models.py @@ -18,7 +18,7 @@ class Donut(models.Model): class Meta: ordering = ('consumed_at',) - def __str__(self): + def __unicode__(self): return self.name class RumBaba(models.Model): diff --git a/tests/regressiontests/string_lookup/models.py b/tests/regressiontests/string_lookup/models.py index 53687a22cb..0704890cd7 100644 --- a/tests/regressiontests/string_lookup/models.py +++ b/tests/regressiontests/string_lookup/models.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals + from django.db import models @@ -42,5 +44,5 @@ class Article(models.Model): text = models.TextField() submitted_from = models.IPAddressField(blank=True, null=True) - def __str__(self): + def __unicode__(self): return "Article %s" % self.name From a0a0203a392f67832ba7a8a2f099e70d7db2d19e Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 11 Aug 2012 15:34:51 +0200 Subject: [PATCH 024/253] [py3] Added python_2_unicode_compatible decorator. --- django/utils/encoding.py | 13 +++++++++++++ docs/ref/utils.txt | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/django/utils/encoding.py b/django/utils/encoding.py index eb60cfde8b..7030ab1db0 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -39,6 +39,19 @@ class StrAndUnicode(object): def __str__(self): return self.__unicode__().encode('utf-8') +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if not six.PY3: + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + def smart_text(s, encoding='utf-8', strings_only=False, errors='strict'): """ Returns a text object representing 's' -- unicode on Python 2 and str on diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index b6cb1035d3..97643c29e9 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -187,6 +187,14 @@ The functions defined in this module share the following properties: Useful as a mix-in. If you support Python 2 and 3 with a single code base, you can inherit this mix-in and just define ``__unicode__``. +.. function:: python_2_unicode_compatible + + A decorator that defines ``__unicode__`` and ``__str__`` methods under + Python 2. Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a ``__str__`` + method returning text and apply this decorator to the class. + .. function:: smart_text(s, encoding='utf-8', strings_only=False, errors='strict') .. versionadded:: 1.5 From 79d62a71751140315227891bbe175630f9d3edc3 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 11 Aug 2012 16:41:25 +0200 Subject: [PATCH 025/253] [py3] Added fixer for python_2_unicode_compatible. This doesn't deal with classes that define both __unicode__ and __str__; the definition of __str__ should be removed first. It doesn't guarantee that __str__ will return a str (rather than bytes) under Python 3 either. --- django/bin/django-2to3.py | 10 +++++++ django/utils/2to3_fixes/__init__.py | 0 django/utils/2to3_fixes/fix_unicode.py | 36 ++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100755 django/bin/django-2to3.py create mode 100644 django/utils/2to3_fixes/__init__.py create mode 100644 django/utils/2to3_fixes/fix_unicode.py diff --git a/django/bin/django-2to3.py b/django/bin/django-2to3.py new file mode 100755 index 0000000000..35566abb94 --- /dev/null +++ b/django/bin/django-2to3.py @@ -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")) + diff --git a/django/utils/2to3_fixes/__init__.py b/django/utils/2to3_fixes/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/utils/2to3_fixes/fix_unicode.py b/django/utils/2to3_fixes/fix_unicode.py new file mode 100644 index 0000000000..613734ca86 --- /dev/null +++ b/django/utils/2to3_fixes/fix_unicode.py @@ -0,0 +1,36 @@ +"""Fixer for __unicode__ methods. + +Uses the django.utils.encoding.python_2_unicode_compatible decorator. +""" + +from __future__ import unicode_literals + +from lib2to3 import fixer_base +from lib2to3.fixer_util import find_indentation, Name, syms, touch_import +from lib2to3.pgen2 import token +from lib2to3.pytree import Leaf, Node + + +class FixUnicode(fixer_base.BaseFix): + + BM_compatible = True + PATTERN = """ + classdef< 'class' any+ ':' + suite< any* + funcdef< 'def' unifunc='__unicode__' + parameters< '(' NAME ')' > any+ > + any* > > + """ + + def transform(self, node, results): + unifunc = results["unifunc"] + strfunc = Name("__str__", prefix=unifunc.prefix) + unifunc.replace(strfunc) + + klass = node.clone() + klass.prefix = '\n' + find_indentation(node) + decorator = Node(syms.decorator, [Leaf(token.AT, "@"), Name('python_2_unicode_compatible')]) + decorated = Node(syms.decorated, [decorator, klass], prefix=node.prefix) + node.replace(decorated) + + touch_import('django.utils.encoding', 'python_2_unicode_compatible', decorated) From d4a0b27838c815af87698920cc4db7d2afd6f05b Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 12 Aug 2012 12:32:08 +0200 Subject: [PATCH 026/253] [py3] Refactored __unicode__ to __str__. * Renamed the __unicode__ methods * Applied the python_2_unicode_compatible decorator * Removed the StrAndUnicode mix-in that is superseded by python_2_unicode_compatible * Kept the __unicode__ methods in classes that specifically test it under Python 2 --- django/contrib/admin/models.py | 4 +- django/contrib/auth/models.py | 13 ++- django/contrib/comments/models.py | 7 +- django/contrib/contenttypes/models.py | 4 +- django/contrib/contenttypes/tests.py | 4 +- django/contrib/databrowse/datastructures.py | 4 +- django/contrib/databrowse/tests.py | 10 +- django/contrib/flatpages/models.py | 4 +- django/contrib/gis/db/backends/base.py | 4 +- .../contrib/gis/db/backends/oracle/models.py | 4 +- .../contrib/gis/db/backends/postgis/models.py | 4 +- .../gis/db/backends/spatialite/models.py | 4 +- django/contrib/gis/maps/google/overlays.py | 7 +- django/contrib/gis/tests/distapp/models.py | 22 +++-- django/contrib/gis/tests/geo3d/models.py | 26 ++++-- django/contrib/gis/tests/geoadmin/models.py | 4 +- django/contrib/gis/tests/geoapp/models.py | 16 +++- django/contrib/gis/tests/geogapp/models.py | 10 +- django/contrib/gis/tests/relatedapp/models.py | 14 ++- django/contrib/gis/utils/ogrinspect.py | 2 +- django/contrib/messages/storage/base.py | 7 +- django/contrib/redirects/models.py | 6 +- django/contrib/sites/models.py | 7 +- django/core/files/base.py | 7 +- django/core/files/uploadhandler.py | 4 +- django/db/models/options.py | 4 +- django/forms/forms.py | 12 ++- django/forms/formsets.py | 7 +- django/forms/util.py | 12 ++- django/forms/widgets.py | 20 ++-- django/template/base.py | 4 +- django/test/html.py | 7 +- tests/modeltests/aggregation/models.py | 13 ++- tests/modeltests/basic/models.py | 4 +- tests/modeltests/choices/models.py | 4 +- tests/modeltests/custom_columns/models.py | 7 +- tests/modeltests/custom_managers/models.py | 10 +- tests/modeltests/custom_methods/models.py | 4 +- tests/modeltests/custom_pk/fields.py | 4 +- tests/modeltests/custom_pk/models.py | 10 +- tests/modeltests/defer/models.py | 4 +- tests/modeltests/delete/models.py | 4 +- tests/modeltests/distinct_on_fields/models.py | 13 ++- tests/modeltests/expressions/models.py | 7 +- tests/modeltests/field_defaults/models.py | 4 +- tests/modeltests/field_subclassing/fields.py | 4 +- tests/modeltests/field_subclassing/models.py | 4 +- tests/modeltests/fixtures/models.py | 22 +++-- .../fixtures_model_package/models/__init__.py | 4 +- tests/modeltests/generic_relations/models.py | 16 +++- tests/modeltests/get_latest/models.py | 7 +- tests/modeltests/get_object_or_404/models.py | 7 +- tests/modeltests/get_or_create/models.py | 4 +- tests/modeltests/lookup/models.py | 13 ++- tests/modeltests/m2m_and_m2o/models.py | 4 +- tests/modeltests/m2m_intermediary/models.py | 10 +- tests/modeltests/m2m_multiple/models.py | 7 +- tests/modeltests/m2m_recursive/models.py | 4 +- tests/modeltests/m2m_signals/models.py | 10 +- tests/modeltests/m2m_through/models.py | 16 +++- tests/modeltests/m2o_recursive/models.py | 7 +- tests/modeltests/many_to_many/models.py | 7 +- tests/modeltests/many_to_one/models.py | 7 +- tests/modeltests/many_to_one_null/models.py | 7 +- tests/modeltests/model_forms/models.py | 46 +++++++--- tests/modeltests/model_formsets/models.py | 55 +++++++---- tests/modeltests/model_inheritance/models.py | 28 ++++-- .../models.py | 4 +- tests/modeltests/one_to_one/models.py | 13 ++- tests/modeltests/or_lookups/models.py | 4 +- .../order_with_respect_to/models.py | 7 +- tests/modeltests/ordering/models.py | 7 +- tests/modeltests/pagination/models.py | 4 +- tests/modeltests/prefetch_related/models.py | 22 +++-- tests/modeltests/proxy_models/models.py | 13 ++- tests/modeltests/reserved_names/models.py | 6 +- tests/modeltests/reverse_lookup/models.py | 10 +- tests/modeltests/save_delete_hooks/models.py | 4 +- tests/modeltests/select_related/models.py | 27 ++++-- tests/modeltests/serializers/models.py | 25 +++-- tests/modeltests/signals/models.py | 7 +- tests/modeltests/str/models.py | 6 +- tests/modeltests/transactions/models.py | 4 +- tests/modeltests/unmanaged_models/models.py | 19 ++-- tests/modeltests/update/models.py | 7 +- tests/modeltests/update_only_fields/models.py | 7 +- tests/modeltests/validation/models.py | 4 +- .../admin_changelist/models.py | 7 +- .../admin_custom_urls/models.py | 4 +- tests/regressiontests/admin_filters/models.py | 10 +- tests/regressiontests/admin_inlines/models.py | 10 +- tests/regressiontests/admin_scripts/models.py | 4 +- tests/regressiontests/admin_util/models.py | 4 +- .../admin_validation/models.py | 4 +- tests/regressiontests/admin_views/models.py | 91 +++++++++++++------ tests/regressiontests/admin_widgets/models.py | 24 +++-- .../aggregation_regress/models.py | 16 +++- tests/regressiontests/backends/models.py | 13 ++- tests/regressiontests/comment_tests/models.py | 10 +- .../custom_columns_regress/models.py | 7 +- .../custom_managers_regress/models.py | 10 +- tests/regressiontests/datatypes/models.py | 4 +- tests/regressiontests/dates/models.py | 7 +- tests/regressiontests/defaultfilters/tests.py | 4 +- tests/regressiontests/defer_regress/models.py | 10 +- .../expressions_regress/models.py | 4 +- tests/regressiontests/extra_regress/models.py | 7 +- .../fixtures_regress/models.py | 25 +++-- tests/regressiontests/forms/models.py | 7 +- .../forms/tests/error_messages.py | 4 +- tests/regressiontests/forms/tests/extra.py | 28 ++++-- tests/regressiontests/forms/tests/util.py | 4 +- tests/regressiontests/forms/tests/widgets.py | 7 +- .../generic_inline_admin/models.py | 4 +- .../generic_relations_regress/models.py | 16 +++- tests/regressiontests/generic_views/models.py | 10 +- .../regressiontests/inline_formsets/models.py | 7 +- tests/regressiontests/introspection/models.py | 7 +- tests/regressiontests/m2m_regress/models.py | 13 ++- .../m2m_through_regress/models.py | 13 ++- .../managers_regress/models.py | 19 ++-- .../many_to_one_regress/models.py | 7 +- .../model_forms_regress/models.py | 7 +- .../model_formsets_regress/models.py | 4 +- .../model_inheritance_regress/models.py | 28 ++++-- .../models.py | 10 +- tests/regressiontests/model_regress/models.py | 13 ++- tests/regressiontests/modeladmin/models.py | 4 +- .../multiple_database/models.py | 13 ++- tests/regressiontests/null_fk/models.py | 7 +- .../null_fk_ordering/models.py | 10 +- tests/regressiontests/null_queries/models.py | 7 +- .../one_to_one_regress/models.py | 13 ++- tests/regressiontests/queries/models.py | 79 ++++++++++------ .../select_related_onetoone/models.py | 16 +++- .../select_related_regress/models.py | 19 ++-- .../regressiontests/signals_regress/models.py | 7 +- .../regressiontests/sites_framework/models.py | 4 +- tests/regressiontests/string_lookup/models.py | 19 ++-- tests/regressiontests/syndication/models.py | 7 +- tests/regressiontests/templates/filters.py | 17 ++-- tests/regressiontests/views/models.py | 7 +- 142 files changed, 1072 insertions(+), 481 deletions(-) diff --git a/django/contrib/admin/models.py b/django/contrib/admin/models.py index e31c6d84ed..2b12edd4e2 100644 --- a/django/contrib/admin/models.py +++ b/django/contrib/admin/models.py @@ -6,6 +6,7 @@ from django.contrib.auth.models import User from django.contrib.admin.util import quote from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_text +from django.utils.encoding import python_2_unicode_compatible ADDITION = 1 CHANGE = 2 @@ -16,6 +17,7 @@ class LogEntryManager(models.Manager): 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(User) @@ -36,7 +38,7 @@ class LogEntry(models.Model): def __repr__(self): 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: diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index a39165c92c..1c21917a8c 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -16,6 +16,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): @@ -41,6 +42,7 @@ class PermissionManager(models.Manager): ) +@python_2_unicode_compatible class Permission(models.Model): """ The permissions system provides a way to assign permissions to specific @@ -76,7 +78,7 @@ class Permission(models.Model): ordering = ('content_type__app_label', 'content_type__model', 'codename') - def __unicode__(self): + def __str__(self): return "%s | %s | %s" % ( six.text_type(self.content_type.app_label), six.text_type(self.content_type), @@ -94,6 +96,7 @@ class GroupManager(models.Manager): def get_by_natural_key(self, name): 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 @@ -121,7 +124,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): @@ -221,6 +224,7 @@ def _user_has_module_perms(user, app_label): return False +@python_2_unicode_compatible class User(models.Model): """ Users within the Django authentication system are represented by this @@ -259,7 +263,7 @@ class User(models.Model): verbose_name = _('user') verbose_name_plural = _('users') - def __unicode__(self): + def __str__(self): return self.username def natural_key(self): @@ -403,6 +407,7 @@ class User(models.Model): return self._profile_cache +@python_2_unicode_compatible class AnonymousUser(object): id = None pk = None @@ -416,7 +421,7 @@ class AnonymousUser(object): def __init__(self): pass - def __unicode__(self): + def __str__(self): return 'AnonymousUser' def __eq__(self, other): diff --git a/django/contrib/comments/models.py b/django/contrib/comments/models.py index 475b3c8dea..b043b4187a 100644 --- a/django/contrib/comments/models.py +++ b/django/contrib/comments/models.py @@ -8,6 +8,7 @@ from django.core import urlresolvers from django.utils.translation import ugettext_lazy as _ from django.utils import timezone from django.conf import settings +from django.utils.encoding import python_2_unicode_compatible COMMENT_MAX_LENGTH = getattr(settings,'COMMENT_MAX_LENGTH',3000) @@ -39,6 +40,7 @@ class BaseCommentAbstractModel(models.Model): args=(self.content_type_id, self.object_pk) ) +@python_2_unicode_compatible class Comment(BaseCommentAbstractModel): """ A user comment about some object. @@ -76,7 +78,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): @@ -153,6 +155,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 @@ -182,7 +185,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) diff --git a/django/contrib/contenttypes/models.py b/django/contrib/contenttypes/models.py index e6d547a491..b658655bbb 100644 --- a/django/contrib/contenttypes/models.py +++ b/django/contrib/contenttypes/models.py @@ -1,6 +1,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_text, force_text +from django.utils.encoding import python_2_unicode_compatible class ContentTypeManager(models.Manager): @@ -122,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) @@ -135,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 diff --git a/django/contrib/contenttypes/tests.py b/django/contrib/contenttypes/tests.py index cfd7e6ff32..2f92a34581 100644 --- a/django/contrib/contenttypes/tests.py +++ b/django/contrib/contenttypes/tests.py @@ -8,6 +8,7 @@ from django.http import HttpRequest, Http404 from django.test import TestCase from django.utils.http import urlquote from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class ConcreteModel(models.Model): @@ -17,13 +18,14 @@ 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 diff --git a/django/contrib/databrowse/datastructures.py b/django/contrib/databrowse/datastructures.py index e48383f438..13e24167f5 100644 --- a/django/contrib/databrowse/datastructures.py +++ b/django/contrib/databrowse/datastructures.py @@ -9,6 +9,7 @@ from django.utils import formats from django.utils.text import capfirst from django.utils.encoding import smart_text, smart_bytes, 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 @@ -84,6 +85,7 @@ class EasyChoice(object): def url(self): 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 @@ -91,7 +93,7 @@ class EasyInstance(object): def __repr__(self): return smart_bytes('' % (self.model.model._meta.object_name, self.instance._get_pk_val())) - def __unicode__(self): + def __str__(self): val = smart_text(self.instance) if len(val) > DISPLAY_SIZE: return val[:DISPLAY_SIZE] + '...' diff --git a/django/contrib/databrowse/tests.py b/django/contrib/databrowse/tests.py index 149383cf72..d649b4af67 100644 --- a/django/contrib/databrowse/tests.py +++ b/django/contrib/databrowse/tests.py @@ -1,26 +1,30 @@ from django.contrib import databrowse from django.db import models from django.test import TestCase +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class SomeModel(models.Model): some_field = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.some_field +@python_2_unicode_compatible class SomeOtherModel(models.Model): some_other_field = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.some_other_field +@python_2_unicode_compatible class YetAnotherModel(models.Model): yet_another_field = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.yet_another_field diff --git a/django/contrib/flatpages/models.py b/django/contrib/flatpages/models.py index 42ec155f34..3a5b4d6135 100644 --- a/django/contrib/flatpages/models.py +++ b/django/contrib/flatpages/models.py @@ -3,8 +3,10 @@ from __future__ import unicode_literals from django.db import models from django.contrib.sites.models import Site from django.utils.translation import ugettext_lazy as _ +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class FlatPage(models.Model): url = models.CharField(_('URL'), max_length=100, db_index=True) title = models.CharField(_('title'), max_length=200) @@ -21,7 +23,7 @@ class FlatPage(models.Model): verbose_name_plural = _('flat pages') ordering = ('url',) - def __unicode__(self): + def __str__(self): return "%s -- %s" % (self.url, self.title) def get_absolute_url(self): diff --git a/django/contrib/gis/db/backends/base.py b/django/contrib/gis/db/backends/base.py index d9f3546cff..f7af420a8d 100644 --- a/django/contrib/gis/db/backends/base.py +++ b/django/contrib/gis/db/backends/base.py @@ -5,6 +5,7 @@ Base/mixin classes for the spatial backend database operations and the import re from django.contrib.gis import gdal from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class BaseSpatialOperations(object): """ @@ -131,6 +132,7 @@ class BaseSpatialOperations(object): def spatial_ref_sys(self): raise NotImplementedError +@python_2_unicode_compatible class SpatialRefSysMixin(object): """ The SpatialRefSysMixin is a class used by the database-dependent @@ -325,7 +327,7 @@ class SpatialRefSysMixin(object): radius, flattening = sphere_params return 'SPHEROID["%s",%s,%s]' % (sphere_name, radius, flattening) - def __unicode__(self): + def __str__(self): """ Returns the string representation. If GDAL is installed, it will be 'pretty' OGC WKT. diff --git a/django/contrib/gis/db/backends/oracle/models.py b/django/contrib/gis/db/backends/oracle/models.py index ed29f7bb38..b7deb3a946 100644 --- a/django/contrib/gis/db/backends/oracle/models.py +++ b/django/contrib/gis/db/backends/oracle/models.py @@ -9,7 +9,9 @@ """ from django.contrib.gis.db import models from django.contrib.gis.db.backends.base import SpatialRefSysMixin +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class GeometryColumns(models.Model): "Maps to the Oracle USER_SDO_GEOM_METADATA table." table_name = models.CharField(max_length=32) @@ -36,7 +38,7 @@ class GeometryColumns(models.Model): """ return 'column_name' - def __unicode__(self): + def __str__(self): return '%s - %s (SRID: %s)' % (self.table_name, self.column_name, self.srid) class SpatialRefSys(models.Model, SpatialRefSysMixin): diff --git a/django/contrib/gis/db/backends/postgis/models.py b/django/contrib/gis/db/backends/postgis/models.py index a38598343c..e8052594c6 100644 --- a/django/contrib/gis/db/backends/postgis/models.py +++ b/django/contrib/gis/db/backends/postgis/models.py @@ -3,7 +3,9 @@ """ from django.db import models from django.contrib.gis.db.backends.base import SpatialRefSysMixin +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class GeometryColumns(models.Model): """ The 'geometry_columns' table from the PostGIS. See the PostGIS @@ -37,7 +39,7 @@ class GeometryColumns(models.Model): """ return 'f_geometry_column' - def __unicode__(self): + def __str__(self): return "%s.%s - %dD %s field (SRID: %d)" % \ (self.f_table_name, self.f_geometry_column, self.coord_dimension, self.type, self.srid) diff --git a/django/contrib/gis/db/backends/spatialite/models.py b/django/contrib/gis/db/backends/spatialite/models.py index 684c5d8fc7..b281f0bc62 100644 --- a/django/contrib/gis/db/backends/spatialite/models.py +++ b/django/contrib/gis/db/backends/spatialite/models.py @@ -3,7 +3,9 @@ """ from django.db import models from django.contrib.gis.db.backends.base import SpatialRefSysMixin +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class GeometryColumns(models.Model): """ The 'geometry_columns' table from SpatiaLite. @@ -35,7 +37,7 @@ class GeometryColumns(models.Model): """ return 'f_geometry_column' - def __unicode__(self): + def __str__(self): return "%s.%s - %dD %s field (SRID: %d)" % \ (self.f_table_name, self.f_geometry_column, self.coord_dimension, self.type, self.srid) diff --git a/django/contrib/gis/maps/google/overlays.py b/django/contrib/gis/maps/google/overlays.py index 28603ac422..b82d967da6 100644 --- a/django/contrib/gis/maps/google/overlays.py +++ b/django/contrib/gis/maps/google/overlays.py @@ -2,8 +2,10 @@ from django.contrib.gis.geos import fromstr, Point, LineString, LinearRing, Poly from django.utils.functional import total_ordering from django.utils.safestring import mark_safe from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class GEvent(object): """ A Python wrapper for the Google GEvent object. @@ -48,10 +50,11 @@ class GEvent(object): self.event = event self.action = action - def __unicode__(self): + def __str__(self): "Returns the parameter part of a GEvent." return mark_safe('"%s", %s' %(self.event, self.action)) +@python_2_unicode_compatible class GOverlayBase(object): def __init__(self): self.events = [] @@ -64,7 +67,7 @@ class GOverlayBase(object): "Attaches a GEvent to the overlay object." self.events.append(event) - def __unicode__(self): + def __str__(self): "The string representation is the JavaScript API call." return mark_safe('%s(%s)' % (self.__class__.__name__, self.js_params)) diff --git a/django/contrib/gis/tests/distapp/models.py b/django/contrib/gis/tests/distapp/models.py index 76e7d3a03f..bf08829eae 100644 --- a/django/contrib/gis/tests/distapp/models.py +++ b/django/contrib/gis/tests/distapp/models.py @@ -1,50 +1,58 @@ from django.contrib.gis.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class SouthTexasCity(models.Model): "City model on projected coordinate system for South Texas." name = models.CharField(max_length=30) point = models.PointField(srid=32140) objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name +@python_2_unicode_compatible class SouthTexasCityFt(models.Model): "Same City model as above, but U.S. survey feet are the units." name = models.CharField(max_length=30) point = models.PointField(srid=2278) objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name +@python_2_unicode_compatible class AustraliaCity(models.Model): "City model for Australia, using WGS84." name = models.CharField(max_length=30) point = models.PointField() objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name +@python_2_unicode_compatible class CensusZipcode(models.Model): "Model for a few South Texas ZIP codes (in original Census NAD83)." name = models.CharField(max_length=5) poly = models.PolygonField(srid=4269) objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name +@python_2_unicode_compatible class SouthTexasZipcode(models.Model): "Model for a few South Texas ZIP codes." name = models.CharField(max_length=5) poly = models.PolygonField(srid=32140, null=True) objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name +@python_2_unicode_compatible class Interstate(models.Model): "Geodetic model for U.S. Interstates." name = models.CharField(max_length=10) path = models.LineStringField() objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name +@python_2_unicode_compatible class SouthTexasInterstate(models.Model): "Projected model for South Texas Interstates." name = models.CharField(max_length=10) path = models.LineStringField(srid=32140) objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name diff --git a/django/contrib/gis/tests/geo3d/models.py b/django/contrib/gis/tests/geo3d/models.py index 3c4f77ee05..81e5f55f78 100644 --- a/django/contrib/gis/tests/geo3d/models.py +++ b/django/contrib/gis/tests/geo3d/models.py @@ -1,59 +1,67 @@ from django.contrib.gis.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class City3D(models.Model): name = models.CharField(max_length=30) point = models.PointField(dim=3) objects = models.GeoManager() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Interstate2D(models.Model): name = models.CharField(max_length=30) line = models.LineStringField(srid=4269) objects = models.GeoManager() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Interstate3D(models.Model): name = models.CharField(max_length=30) line = models.LineStringField(dim=3, srid=4269) objects = models.GeoManager() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class InterstateProj2D(models.Model): name = models.CharField(max_length=30) line = models.LineStringField(srid=32140) objects = models.GeoManager() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class InterstateProj3D(models.Model): name = models.CharField(max_length=30) line = models.LineStringField(dim=3, srid=32140) objects = models.GeoManager() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Polygon2D(models.Model): name = models.CharField(max_length=30) poly = models.PolygonField(srid=32140) objects = models.GeoManager() - - def __unicode__(self): + + def __str__(self): return self.name +@python_2_unicode_compatible class Polygon3D(models.Model): name = models.CharField(max_length=30) poly = models.PolygonField(dim=3, srid=32140) objects = models.GeoManager() - - def __unicode__(self): + + def __str__(self): return self.name class Point2D(models.Model): diff --git a/django/contrib/gis/tests/geoadmin/models.py b/django/contrib/gis/tests/geoadmin/models.py index 51a76d1a0e..af0898823d 100644 --- a/django/contrib/gis/tests/geoadmin/models.py +++ b/django/contrib/gis/tests/geoadmin/models.py @@ -1,10 +1,12 @@ from django.contrib.gis.db import models from django.contrib.gis import admin +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class City(models.Model): name = models.CharField(max_length=30) point = models.PointField() objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name admin.site.register(City, admin.OSMGeoAdmin) diff --git a/django/contrib/gis/tests/geoapp/models.py b/django/contrib/gis/tests/geoapp/models.py index 79061e1cfc..abde509c8b 100644 --- a/django/contrib/gis/tests/geoapp/models.py +++ b/django/contrib/gis/tests/geoapp/models.py @@ -1,20 +1,23 @@ from django.contrib.gis.db import models from django.contrib.gis.tests.utils import mysql, spatialite +from django.utils.encoding import python_2_unicode_compatible # MySQL spatial indices can't handle NULL geometries. null_flag = not mysql +@python_2_unicode_compatible class Country(models.Model): name = models.CharField(max_length=30) mpoly = models.MultiPolygonField() # SRID, by default, is 4326 objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name +@python_2_unicode_compatible class City(models.Model): name = models.CharField(max_length=30) point = models.PointField() objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name # This is an inherited model from City class PennsylvaniaCity(City): @@ -22,28 +25,31 @@ class PennsylvaniaCity(City): founded = models.DateTimeField(null=True) objects = models.GeoManager() # TODO: This should be implicitly inherited. +@python_2_unicode_compatible class State(models.Model): name = models.CharField(max_length=30) poly = models.PolygonField(null=null_flag) # Allowing NULL geometries here. objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name +@python_2_unicode_compatible class Track(models.Model): name = models.CharField(max_length=30) line = models.LineStringField() objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name class Truth(models.Model): val = models.BooleanField() objects = models.GeoManager() if not spatialite: + @python_2_unicode_compatible class Feature(models.Model): name = models.CharField(max_length=20) geom = models.GeometryField() objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name class MinusOneSRID(models.Model): geom = models.PointField(srid=-1) # Minus one SRID. diff --git a/django/contrib/gis/tests/geogapp/models.py b/django/contrib/gis/tests/geogapp/models.py index 3696ba2ff4..7e802f9321 100644 --- a/django/contrib/gis/tests/geogapp/models.py +++ b/django/contrib/gis/tests/geogapp/models.py @@ -1,20 +1,24 @@ from django.contrib.gis.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class City(models.Model): name = models.CharField(max_length=30) point = models.PointField(geography=True) objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name +@python_2_unicode_compatible class Zipcode(models.Model): code = models.CharField(max_length=10) poly = models.PolygonField(geography=True) objects = models.GeoManager() - def __unicode__(self): return self.code + def __str__(self): return self.code +@python_2_unicode_compatible class County(models.Model): name = models.CharField(max_length=25) state = models.CharField(max_length=20) mpoly = models.MultiPolygonField(geography=True) objects = models.GeoManager() - def __unicode__(self): return ' County, '.join([self.name, self.state]) + def __str__(self): return ' County, '.join([self.name, self.state]) diff --git a/django/contrib/gis/tests/relatedapp/models.py b/django/contrib/gis/tests/relatedapp/models.py index aec4e15749..659fef7a93 100644 --- a/django/contrib/gis/tests/relatedapp/models.py +++ b/django/contrib/gis/tests/relatedapp/models.py @@ -1,37 +1,41 @@ from django.contrib.gis.db import models from django.contrib.localflavor.us.models import USStateField +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Location(models.Model): point = models.PointField() objects = models.GeoManager() - def __unicode__(self): return self.point.wkt + def __str__(self): return self.point.wkt +@python_2_unicode_compatible class City(models.Model): name = models.CharField(max_length=50) state = USStateField() location = models.ForeignKey(Location) objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name class AugmentedLocation(Location): extra_text = models.TextField(blank=True) objects = models.GeoManager() - + class DirectoryEntry(models.Model): listing_text = models.CharField(max_length=50) location = models.ForeignKey(AugmentedLocation) objects = models.GeoManager() +@python_2_unicode_compatible class Parcel(models.Model): name = models.CharField(max_length=30) city = models.ForeignKey(City) center1 = models.PointField() # Throwing a curveball w/`db_column` here. - center2 = models.PointField(srid=2276, db_column='mycenter') + center2 = models.PointField(srid=2276, db_column='mycenter') border1 = models.PolygonField() border2 = models.PolygonField(srid=2276) objects = models.GeoManager() - def __unicode__(self): return self.name + def __str__(self): return self.name # These use the GeoManager but do not have any geographic fields. class Author(models.Model): diff --git a/django/contrib/gis/utils/ogrinspect.py b/django/contrib/gis/utils/ogrinspect.py index f8977059d9..4266ee4b4c 100644 --- a/django/contrib/gis/utils/ogrinspect.py +++ b/django/contrib/gis/utils/ogrinspect.py @@ -223,4 +223,4 @@ def _ogrinspect(data_source, model_name, geom_name='geom', layer_key=0, srid=Non if name_field: yield '' - yield ' def __unicode__(self): return self.%s' % name_field + yield ' def __str__(self): return self.%s' % name_field diff --git a/django/contrib/messages/storage/base.py b/django/contrib/messages/storage/base.py index 5433bbff28..7fe8a077ed 100644 --- a/django/contrib/messages/storage/base.py +++ b/django/contrib/messages/storage/base.py @@ -1,14 +1,15 @@ from __future__ import unicode_literals from django.conf import settings -from django.utils.encoding import force_text, StrAndUnicode +from django.utils.encoding import force_text, python_2_unicode_compatible from django.contrib.messages import constants, utils LEVEL_TAGS = utils.get_level_tags() -class Message(StrAndUnicode): +@python_2_unicode_compatible +class Message(object): """ Represents an actual message that can be stored in any of the supported storage classes (typically session- or cookie-based) and rendered in a view @@ -35,7 +36,7 @@ class Message(StrAndUnicode): return isinstance(other, Message) and self.level == other.level and \ self.message == other.message - def __unicode__(self): + def __str__(self): return force_text(self.message) def _get_tags(self): diff --git a/django/contrib/redirects/models.py b/django/contrib/redirects/models.py index 4233d55793..a0376b5578 100644 --- a/django/contrib/redirects/models.py +++ b/django/contrib/redirects/models.py @@ -1,7 +1,9 @@ from django.db import models from django.contrib.sites.models import Site from django.utils.translation import ugettext_lazy as _ +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Redirect(models.Model): site = models.ForeignKey(Site) old_path = models.CharField(_('redirect from'), max_length=200, db_index=True, @@ -15,6 +17,6 @@ class Redirect(models.Model): db_table = 'django_redirect' unique_together=(('site', 'old_path'),) ordering = ('old_path',) - - def __unicode__(self): + + def __str__(self): return "%s ---> %s" % (self.old_path, self.new_path) diff --git a/django/contrib/sites/models.py b/django/contrib/sites/models.py index fecbff79d8..8590740658 100644 --- a/django/contrib/sites/models.py +++ b/django/contrib/sites/models.py @@ -1,5 +1,6 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ +from django.utils.encoding import python_2_unicode_compatible SITE_CACHE = {} @@ -32,6 +33,7 @@ class SiteManager(models.Manager): SITE_CACHE = {} +@python_2_unicode_compatible class Site(models.Model): domain = models.CharField(_('domain name'), max_length=100) @@ -44,7 +46,7 @@ class Site(models.Model): verbose_name_plural = _('sites') ordering = ('domain',) - def __unicode__(self): + def __str__(self): return self.domain def save(self, *args, **kwargs): @@ -62,6 +64,7 @@ class Site(models.Model): pass +@python_2_unicode_compatible class RequestSite(object): """ A class that shares the primary interface of Site (i.e., it has @@ -73,7 +76,7 @@ class RequestSite(object): def __init__(self, request): self.domain = self.name = request.get_host() - def __unicode__(self): + def __str__(self): return self.domain def save(self, force_insert=False, force_update=False): diff --git a/django/core/files/base.py b/django/core/files/base.py index 4ad0be4fd8..d7a8cb8539 100644 --- a/django/core/files/base.py +++ b/django/core/files/base.py @@ -5,7 +5,9 @@ from io import BytesIO from django.utils.encoding import smart_bytes, smart_text from django.core.files.utils import FileProxyMixin +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class File(FileProxyMixin): DEFAULT_CHUNK_SIZE = 64 * 2**10 @@ -17,7 +19,7 @@ class File(FileProxyMixin): if hasattr(file, 'mode'): self.mode = file.mode - def __unicode__(self): + def __str__(self): return smart_text(self.name or '') def __repr__(self): @@ -121,6 +123,7 @@ class File(FileProxyMixin): def close(self): self.file.close() +@python_2_unicode_compatible class ContentFile(File): """ A File-like object that takes just raw content, rather than an actual file. @@ -130,7 +133,7 @@ class ContentFile(File): super(ContentFile, self).__init__(BytesIO(content), name=name) self.size = len(content) - def __unicode__(self): + def __str__(self): return 'Raw content' def __bool__(self): diff --git a/django/core/files/uploadhandler.py b/django/core/files/uploadhandler.py index 68d540e595..c422945d6f 100644 --- a/django/core/files/uploadhandler.py +++ b/django/core/files/uploadhandler.py @@ -10,6 +10,7 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile from django.utils import importlib +from django.utils.encoding import python_2_unicode_compatible __all__ = ['UploadFileException','StopUpload', 'SkipFile', 'FileUploadHandler', 'TemporaryFileUploadHandler', 'MemoryFileUploadHandler', @@ -21,6 +22,7 @@ class UploadFileException(Exception): """ pass +@python_2_unicode_compatible class StopUpload(UploadFileException): """ This exception is raised when an upload must abort. @@ -33,7 +35,7 @@ class StopUpload(UploadFileException): """ self.connection_reset = connection_reset - def __unicode__(self): + def __str__(self): if self.connection_reset: return 'StopUpload: Halt current upload.' else: diff --git a/django/db/models/options.py b/django/db/models/options.py index 9211a76b68..014b8bf359 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -13,6 +13,7 @@ from django.utils.translation import activate, deactivate_all, get_language, str from django.utils.encoding import force_text, smart_text from django.utils.datastructures import SortedDict from django.utils import six +from django.utils.encoding import python_2_unicode_compatible # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces". get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip() @@ -22,6 +23,7 @@ DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering', 'order_with_respect_to', 'app_label', 'db_tablespace', 'abstract', 'managed', 'proxy', 'auto_created') +@python_2_unicode_compatible class Options(object): def __init__(self, meta, app_label=None): self.local_fields, self.local_many_to_many = [], [] @@ -200,7 +202,7 @@ class Options(object): def __repr__(self): return '' % self.object_name - def __unicode__(self): + def __str__(self): return "%s.%s" % (smart_text(self.app_label), smart_text(self.module_name)) def verbose_name_raw(self): diff --git a/django/forms/forms.py b/django/forms/forms.py index 45b758202a..3299c2becc 100644 --- a/django/forms/forms.py +++ b/django/forms/forms.py @@ -12,7 +12,7 @@ from django.forms.util import flatatt, ErrorDict, ErrorList from django.forms.widgets import Media, media_property, TextInput, Textarea from django.utils.datastructures import SortedDict from django.utils.html import conditional_escape, format_html -from django.utils.encoding import StrAndUnicode, smart_text, force_text +from django.utils.encoding import smart_text, force_text, python_2_unicode_compatible from django.utils.safestring import mark_safe from django.utils import six @@ -68,7 +68,8 @@ class DeclarativeFieldsMetaclass(type): new_class.media = media_property(new_class) return new_class -class BaseForm(StrAndUnicode): +@python_2_unicode_compatible +class BaseForm(object): # This is the main implementation of all the Form logic. Note that this # class is different than Form. See the comments by the Form class for more # information. Any improvements to the form API should be made to *this* @@ -95,7 +96,7 @@ class BaseForm(StrAndUnicode): # self.base_fields. self.fields = copy.deepcopy(self.base_fields) - def __unicode__(self): + def __str__(self): return self.as_table() def __iter__(self): @@ -387,7 +388,8 @@ class Form(six.with_metaclass(DeclarativeFieldsMetaclass, BaseForm)): # to define a form using declarative syntax. # BaseForm itself has no way of designating self.fields. -class BoundField(StrAndUnicode): +@python_2_unicode_compatible +class BoundField(object): "A Field plus data" def __init__(self, form, field, name): self.form = form @@ -402,7 +404,7 @@ class BoundField(StrAndUnicode): self.label = self.field.label self.help_text = field.help_text or '' - def __unicode__(self): + def __str__(self): """Renders this field as an HTML widget.""" if self.field.show_hidden_initial: return self.as_widget() + self.as_hidden(only_initial=True) diff --git a/django/forms/formsets.py b/django/forms/formsets.py index 4ea8dc4ca9..258c673da8 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -5,7 +5,7 @@ from django.forms import Form from django.forms.fields import IntegerField, BooleanField from django.forms.util import ErrorList from django.forms.widgets import Media, HiddenInput -from django.utils.encoding import StrAndUnicode +from django.utils.encoding import python_2_unicode_compatible from django.utils.safestring import mark_safe from django.utils import six from django.utils.six.moves import xrange @@ -33,7 +33,8 @@ class ManagementForm(Form): self.base_fields[MAX_NUM_FORM_COUNT] = IntegerField(required=False, widget=HiddenInput) super(ManagementForm, self).__init__(*args, **kwargs) -class BaseFormSet(StrAndUnicode): +@python_2_unicode_compatible +class BaseFormSet(object): """ A collection of instances of the same Form class. """ @@ -51,7 +52,7 @@ class BaseFormSet(StrAndUnicode): # construct the forms in the formset self._construct_forms() - def __unicode__(self): + def __str__(self): return self.as_table() def __iter__(self): diff --git a/django/forms/util.py b/django/forms/util.py index cd6b52df6f..9b1bcebe33 100644 --- a/django/forms/util.py +++ b/django/forms/util.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from django.conf import settings from django.utils.html import format_html, format_html_join -from django.utils.encoding import StrAndUnicode, force_text +from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.safestring import mark_safe from django.utils import timezone from django.utils.translation import ugettext_lazy as _ @@ -22,13 +22,14 @@ def flatatt(attrs): """ return format_html_join('', ' {0}="{1}"', attrs.items()) -class ErrorDict(dict, StrAndUnicode): +@python_2_unicode_compatible +class ErrorDict(dict): """ A collection of errors that knows how to display itself in various formats. The dictionary keys are the field names, and the values are the errors. """ - def __unicode__(self): + def __str__(self): return self.as_ul() def as_ul(self): @@ -42,11 +43,12 @@ class ErrorDict(dict, StrAndUnicode): def as_text(self): return '\n'.join(['* %s\n%s' % (k, '\n'.join([' * %s' % force_text(i) for i in v])) for k, v in self.items()]) -class ErrorList(list, StrAndUnicode): +@python_2_unicode_compatible +class ErrorList(list): """ A collection of errors that knows how to display itself in various formats. """ - def __unicode__(self): + def __str__(self): return self.as_ul() def as_ul(self): diff --git a/django/forms/widgets.py b/django/forms/widgets.py index be9ac8eb8f..fe7c06ff34 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -17,7 +17,7 @@ from django.forms.util import flatatt, to_current_timezone from django.utils.datastructures import MultiValueDict, MergeDict from django.utils.html import conditional_escape, format_html, format_html_join from django.utils.translation import ugettext, ugettext_lazy -from django.utils.encoding import StrAndUnicode, force_text +from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.safestring import mark_safe from django.utils import six from django.utils import datetime_safe, formats @@ -34,7 +34,8 @@ __all__ = ( MEDIA_TYPES = ('css','js') -class Media(StrAndUnicode): +@python_2_unicode_compatible +class Media(object): def __init__(self, media=None, **kwargs): if media: media_attrs = media.__dict__ @@ -51,7 +52,7 @@ class Media(StrAndUnicode): # if media_attrs != {}: # raise TypeError("'class Media' has invalid attribute(s): %s" % ','.join(media_attrs.keys())) - def __unicode__(self): + def __str__(self): return self.render() def render(self): @@ -142,7 +143,8 @@ class MediaDefiningClass(type): new_class.media = media_property(new_class) return new_class -class SubWidget(StrAndUnicode): +@python_2_unicode_compatible +class SubWidget(object): """ Some widgets are made of multiple HTML elements -- namely, RadioSelect. This is a class that represents the "inner" HTML element of a widget. @@ -152,7 +154,7 @@ class SubWidget(StrAndUnicode): self.name, self.value = name, value self.attrs, self.choices = attrs, choices - def __unicode__(self): + def __str__(self): args = [self.name, self.value, self.attrs] if self.choices: args.append(self.choices) @@ -647,6 +649,7 @@ class SelectMultiple(Select): data_set = set([force_text(value) for value in data]) return data_set != initial_set +@python_2_unicode_compatible class RadioInput(SubWidget): """ An object used by RadioFieldRenderer that represents a single @@ -660,7 +663,7 @@ class RadioInput(SubWidget): self.choice_label = force_text(choice[1]) self.index = index - def __unicode__(self): + def __str__(self): return self.render() def render(self, name=None, value=None, attrs=None, choices=()): @@ -685,7 +688,8 @@ class RadioInput(SubWidget): final_attrs['checked'] = 'checked' return format_html('', flatatt(final_attrs)) -class RadioFieldRenderer(StrAndUnicode): +@python_2_unicode_compatible +class RadioFieldRenderer(object): """ An object used by RadioSelect to enable customization of radio widgets. """ @@ -702,7 +706,7 @@ class RadioFieldRenderer(StrAndUnicode): choice = self.choices[idx] # Let the IndexError propogate return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx) - def __unicode__(self): + def __str__(self): return self.render() def render(self): diff --git a/django/template/base.py b/django/template/base.py index 4dcba6dd2b..24ad9320e0 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -20,6 +20,7 @@ from django.utils.html import escape from django.utils.module_loading import module_has_submodule from django.utils import six from django.utils.timezone import template_localtime +from django.utils.encoding import python_2_unicode_compatible TOKEN_TEXT = 0 @@ -79,13 +80,14 @@ class TemplateDoesNotExist(Exception): class TemplateEncodingError(Exception): pass +@python_2_unicode_compatible class VariableDoesNotExist(Exception): def __init__(self, msg, params=()): self.msg = msg self.params = params - def __unicode__(self): + def __str__(self): return self.msg % tuple([force_text(p, errors='replace') for p in self.params]) diff --git a/django/test/html.py b/django/test/html.py index acdb4ffd14..274810cab4 100644 --- a/django/test/html.py +++ b/django/test/html.py @@ -8,6 +8,7 @@ import re from django.utils.encoding import force_text from django.utils.html_parser import HTMLParser, HTMLParseError from django.utils import six +from django.utils.encoding import python_2_unicode_compatible WHITESPACE = re.compile('\s+') @@ -17,6 +18,7 @@ def normalize_whitespace(string): return WHITESPACE.sub(' ', string) +@python_2_unicode_compatible class Element(object): def __init__(self, name, attributes): self.name = name @@ -117,7 +119,7 @@ class Element(object): def __getitem__(self, key): return self.children[key] - def __unicode__(self): + def __str__(self): output = '<%s' % self.name for key, value in self.attributes: if value: @@ -136,11 +138,12 @@ class Element(object): return six.text_type(self) +@python_2_unicode_compatible class RootElement(Element): def __init__(self): super(RootElement, self).__init__(None, ()) - def __unicode__(self): + def __str__(self): return ''.join(six.text_type(c) for c in self.children) diff --git a/tests/modeltests/aggregation/models.py b/tests/modeltests/aggregation/models.py index ccc12898b7..b4f797ee03 100644 --- a/tests/modeltests/aggregation/models.py +++ b/tests/modeltests/aggregation/models.py @@ -1,22 +1,26 @@ # coding: utf-8 from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() friends = models.ManyToManyField('self', blank=True) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Publisher(models.Model): name = models.CharField(max_length=255) num_awards = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Book(models.Model): isbn = models.CharField(max_length=9) name = models.CharField(max_length=255) @@ -28,15 +32,16 @@ class Book(models.Model): publisher = models.ForeignKey(Publisher) pubdate = models.DateField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Store(models.Model): name = models.CharField(max_length=255) books = models.ManyToManyField(Book) original_opening = models.DateTimeField() friday_night_closing = models.TimeField() - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py index 06aa9cf3c3..660beddf49 100644 --- a/tests/modeltests/basic/models.py +++ b/tests/modeltests/basic/models.py @@ -5,8 +5,10 @@ This is a basic model with only two non-primary-key fields. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() @@ -14,5 +16,5 @@ class Article(models.Model): class Meta: ordering = ('pub_date','headline') - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/choices/models.py b/tests/modeltests/choices/models.py index ee01911573..2fa33a9680 100644 --- a/tests/modeltests/choices/models.py +++ b/tests/modeltests/choices/models.py @@ -10,6 +10,7 @@ field. This method returns the "human-readable" value of the field. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible GENDER_CHOICES = ( @@ -17,9 +18,10 @@ GENDER_CHOICES = ( ('F', 'Female'), ) +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=20) gender = models.CharField(max_length=1, choices=GENDER_CHOICES) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/custom_columns/models.py b/tests/modeltests/custom_columns/models.py index 39f1274a8f..16f0563d6b 100644 --- a/tests/modeltests/custom_columns/models.py +++ b/tests/modeltests/custom_columns/models.py @@ -18,24 +18,27 @@ from the default generated name, use the ``db_table`` parameter on the from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): first_name = models.CharField(max_length=30, db_column='firstname') last_name = models.CharField(max_length=30, db_column='last') - def __unicode__(self): + def __str__(self): return '%s %s' % (self.first_name, self.last_name) class Meta: db_table = 'my_author_table' ordering = ('last_name','first_name') +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) authors = models.ManyToManyField(Author, db_table='my_m2m_table') - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py index a9845ad414..de7c1772ed 100644 --- a/tests/modeltests/custom_managers/models.py +++ b/tests/modeltests/custom_managers/models.py @@ -12,6 +12,7 @@ returns. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible # An example of a custom manager called "objects". @@ -19,13 +20,14 @@ class PersonManager(models.Manager): def get_fun_people(self): return self.filter(fun=True) +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) fun = models.BooleanField() objects = PersonManager() - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) # An example of a custom manager that sets get_query_set(). @@ -34,6 +36,7 @@ class PublishedBookManager(models.Manager): def get_query_set(self): return super(PublishedBookManager, self).get_query_set().filter(is_published=True) +@python_2_unicode_compatible class Book(models.Model): title = models.CharField(max_length=50) author = models.CharField(max_length=30) @@ -41,7 +44,7 @@ class Book(models.Model): published_objects = PublishedBookManager() authors = models.ManyToManyField(Person, related_name='books') - def __unicode__(self): + def __str__(self): return self.title # An example of providing multiple custom managers. @@ -50,6 +53,7 @@ class FastCarManager(models.Manager): def get_query_set(self): return super(FastCarManager, self).get_query_set().filter(top_speed__gt=150) +@python_2_unicode_compatible class Car(models.Model): name = models.CharField(max_length=10) mileage = models.IntegerField() @@ -57,5 +61,5 @@ class Car(models.Model): cars = models.Manager() fast_cars = FastCarManager() - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/custom_methods/models.py b/tests/modeltests/custom_methods/models.py index 4e3da58851..cef3fd722b 100644 --- a/tests/modeltests/custom_methods/models.py +++ b/tests/modeltests/custom_methods/models.py @@ -7,13 +7,15 @@ Any method you add to a model will be available to instances. import datetime from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() - def __unicode__(self): + def __str__(self): return self.headline def was_published_today(self): diff --git a/tests/modeltests/custom_pk/fields.py b/tests/modeltests/custom_pk/fields.py index 68fb9dcd16..d25eeca1d5 100644 --- a/tests/modeltests/custom_pk/fields.py +++ b/tests/modeltests/custom_pk/fields.py @@ -3,8 +3,10 @@ import string from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class MyWrapper(object): def __init__(self, value): self.value = value @@ -12,7 +14,7 @@ class MyWrapper(object): def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self.value) - def __unicode__(self): + def __str__(self): return self.value def __eq__(self, other): diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py index 8199b05a1a..5ef9b69f0c 100644 --- a/tests/modeltests/custom_pk/models.py +++ b/tests/modeltests/custom_pk/models.py @@ -11,8 +11,10 @@ from __future__ import absolute_import, unicode_literals from django.db import models from .fields import MyAutoField +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Employee(models.Model): employee_code = models.IntegerField(primary_key=True, db_column = 'code') first_name = models.CharField(max_length=20) @@ -20,22 +22,24 @@ class Employee(models.Model): class Meta: ordering = ('last_name', 'first_name') - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Business(models.Model): name = models.CharField(max_length=20, primary_key=True) employees = models.ManyToManyField(Employee) class Meta: verbose_name_plural = 'businesses' - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Bar(models.Model): id = MyAutoField(primary_key=True, db_index=True) - def __unicode__(self): + def __str__(self): return repr(self.pk) diff --git a/tests/modeltests/defer/models.py b/tests/modeltests/defer/models.py index c64becf972..0688cbc984 100644 --- a/tests/modeltests/defer/models.py +++ b/tests/modeltests/defer/models.py @@ -3,18 +3,20 @@ Tests for defer() and only(). """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Secondary(models.Model): first = models.CharField(max_length=50) second = models.CharField(max_length=50) +@python_2_unicode_compatible class Primary(models.Model): name = models.CharField(max_length=50) value = models.CharField(max_length=50) related = models.ForeignKey(Secondary) - def __unicode__(self): + def __str__(self): return self.name class Child(Primary): diff --git a/tests/modeltests/delete/models.py b/tests/modeltests/delete/models.py index 7e7766e824..e0cec426ea 100644 --- a/tests/modeltests/delete/models.py +++ b/tests/modeltests/delete/models.py @@ -1,12 +1,14 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class R(models.Model): is_default = models.BooleanField(default=False) - def __unicode__(self): + def __str__(self): return "%s" % self.pk diff --git a/tests/modeltests/distinct_on_fields/models.py b/tests/modeltests/distinct_on_fields/models.py index 33665e9624..7982f435d0 100644 --- a/tests/modeltests/distinct_on_fields/models.py +++ b/tests/modeltests/distinct_on_fields/models.py @@ -1,7 +1,9 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=10) parent = models.ForeignKey('self', blank=True, null=True, @@ -10,19 +12,21 @@ class Tag(models.Model): class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Celebrity(models.Model): name = models.CharField("Name", max_length=20) greatest_fan = models.ForeignKey("Fan", null=True, unique=True) - def __unicode__(self): + def __str__(self): return self.name class Fan(models.Model): fan_of = models.ForeignKey(Celebrity) +@python_2_unicode_compatible class Staff(models.Model): id = models.IntegerField(primary_key=True) name = models.CharField(max_length=50) @@ -30,12 +34,13 @@ class Staff(models.Model): tags = models.ManyToManyField(Tag, through='StaffTag') coworkers = models.ManyToManyField('self') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class StaffTag(models.Model): staff = models.ForeignKey(Staff) tag = models.ForeignKey(Tag) - def __unicode__(self): + def __str__(self): return "%s -> %s" % (self.tag, self.staff) diff --git a/tests/modeltests/expressions/models.py b/tests/modeltests/expressions/models.py index 018a0cf795..f592a0eb13 100644 --- a/tests/modeltests/expressions/models.py +++ b/tests/modeltests/expressions/models.py @@ -4,15 +4,18 @@ Tests for F() query expression syntax. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Employee(models.Model): firstname = models.CharField(max_length=50) lastname = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return '%s %s' % (self.firstname, self.lastname) +@python_2_unicode_compatible class Company(models.Model): name = models.CharField(max_length=100) num_employees = models.PositiveIntegerField() @@ -25,5 +28,5 @@ class Company(models.Model): related_name='company_point_of_contact_set', null=True) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/field_defaults/models.py b/tests/modeltests/field_defaults/models.py index 18840c8b4b..c99d4871cd 100644 --- a/tests/modeltests/field_defaults/models.py +++ b/tests/modeltests/field_defaults/models.py @@ -13,11 +13,13 @@ field. from datetime import datetime from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField(default=datetime.now) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/field_subclassing/fields.py b/tests/modeltests/field_subclassing/fields.py index 47d710717b..6fecbb63fc 100644 --- a/tests/modeltests/field_subclassing/fields.py +++ b/tests/modeltests/field_subclassing/fields.py @@ -5,8 +5,10 @@ import json from django.db import models from django.utils.encoding import force_text from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Small(object): """ A simple class to show that non-trivial Python objects can be used as @@ -15,7 +17,7 @@ class Small(object): def __init__(self, first, second): self.first, self.second = first, second - def __unicode__(self): + def __str__(self): return '%s%s' % (force_text(self.first), force_text(self.second)) class SmallField(models.Field): diff --git a/tests/modeltests/field_subclassing/models.py b/tests/modeltests/field_subclassing/models.py index 2df9664cdc..642573cc83 100644 --- a/tests/modeltests/field_subclassing/models.py +++ b/tests/modeltests/field_subclassing/models.py @@ -8,13 +8,15 @@ from django.db import models from django.utils.encoding import force_text from .fields import SmallField, SmallerField, JSONField +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class MyModel(models.Model): name = models.CharField(max_length=10) data = SmallField('small field') - def __unicode__(self): + def __str__(self): return force_text(self.name) class OtherModel(models.Model): diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py index 586f65e60f..8bd3501926 100644 --- a/tests/modeltests/fixtures/models.py +++ b/tests/modeltests/fixtures/models.py @@ -12,38 +12,43 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Category(models.Model): title = models.CharField(max_length=100) description = models.TextField() - def __unicode__(self): + def __str__(self): return self.title class Meta: ordering = ('title',) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() - def __unicode__(self): + def __str__(self): return self.headline class Meta: ordering = ('-pub_date', 'headline') +@python_2_unicode_compatible class Blog(models.Model): name = models.CharField(max_length=100) featured = models.ForeignKey(Article, related_name='fixtures_featured_set') articles = models.ManyToManyField(Article, blank=True, related_name='fixtures_articles_set') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=100) tagged_type = models.ForeignKey(ContentType, related_name="fixtures_tag_set") @@ -51,7 +56,7 @@ class Tag(models.Model): tagged = generic.GenericForeignKey(ct_field='tagged_type', fk_field='tagged_id') - def __unicode__(self): + def __str__(self): return '<%s: %s> tagged "%s"' % (self.tagged.__class__.__name__, self.tagged, self.name) @@ -59,10 +64,11 @@ class PersonManager(models.Manager): def get_by_natural_key(self, name): return self.get(name=name) +@python_2_unicode_compatible class Person(models.Model): objects = PersonManager() name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name class Meta: @@ -79,19 +85,21 @@ class Spy(Person): objects = SpyManager() cover_blown = models.BooleanField(default=False) +@python_2_unicode_compatible class Visa(models.Model): person = models.ForeignKey(Person) permissions = models.ManyToManyField(Permission, blank=True) - def __unicode__(self): + def __str__(self): return '%s %s' % (self.person.name, ', '.join(p.name for p in self.permissions.all())) +@python_2_unicode_compatible class Book(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Person) - def __unicode__(self): + def __str__(self): authors = ' and '.join(a.name for a in self.authors.all()) return '%s by %s' % (self.name, authors) if authors else self.name diff --git a/tests/modeltests/fixtures_model_package/models/__init__.py b/tests/modeltests/fixtures_model_package/models/__init__.py index d309165e9c..deeba48aa9 100644 --- a/tests/modeltests/fixtures_model_package/models/__init__.py +++ b/tests/modeltests/fixtures_model_package/models/__init__.py @@ -1,11 +1,13 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py index f2dcf7db24..2f025e660b 100644 --- a/tests/modeltests/generic_relations/models.py +++ b/tests/modeltests/generic_relations/models.py @@ -14,8 +14,10 @@ from __future__ import unicode_literals from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class TaggedItem(models.Model): """A tag on an item.""" tag = models.SlugField() @@ -27,12 +29,13 @@ class TaggedItem(models.Model): class Meta: ordering = ["tag", "content_type__name"] - def __unicode__(self): + def __str__(self): return self.tag class ValuableTaggedItem(TaggedItem): value = models.PositiveIntegerField() +@python_2_unicode_compatible class Comparison(models.Model): """ A model that tests having multiple GenericForeignKeys @@ -48,9 +51,10 @@ class Comparison(models.Model): first_obj = generic.GenericForeignKey(ct_field="content_type1", fk_field="object_id1") other_obj = generic.GenericForeignKey(ct_field="content_type2", fk_field="object_id2") - def __unicode__(self): + def __str__(self): return "%s is %s than %s" % (self.first_obj, self.comparative, self.other_obj) +@python_2_unicode_compatible class Animal(models.Model): common_name = models.CharField(max_length=150) latin_name = models.CharField(max_length=150) @@ -60,25 +64,27 @@ class Animal(models.Model): object_id_field="object_id1", content_type_field="content_type1") - def __unicode__(self): + def __str__(self): return self.common_name +@python_2_unicode_compatible class Vegetable(models.Model): name = models.CharField(max_length=150) is_yucky = models.BooleanField(default=True) tags = generic.GenericRelation(TaggedItem) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Mineral(models.Model): name = models.CharField(max_length=150) hardness = models.PositiveSmallIntegerField() # note the lack of an explicit GenericRelation here... - def __unicode__(self): + def __str__(self): return self.name class GeckoManager(models.Manager): diff --git a/tests/modeltests/get_latest/models.py b/tests/modeltests/get_latest/models.py index d8a690f48c..fe594dd802 100644 --- a/tests/modeltests/get_latest/models.py +++ b/tests/modeltests/get_latest/models.py @@ -9,8 +9,10 @@ farthest into the future." """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() @@ -18,14 +20,15 @@ class Article(models.Model): class Meta: get_latest_by = 'pub_date' - def __unicode__(self): + def __str__(self): return self.headline +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=30) birthday = models.DateField() # Note that this model doesn't have "get_latest_by" set. - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py index f0c73ed94a..bda060569e 100644 --- a/tests/modeltests/get_object_or_404/models.py +++ b/tests/modeltests/get_object_or_404/models.py @@ -11,23 +11,26 @@ performing a ``filter()`` lookup and raising a ``Http404`` exception if a """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name class ArticleManager(models.Manager): def get_query_set(self): return super(ArticleManager, self).get_query_set().filter(authors__name__icontains='sir') +@python_2_unicode_compatible class Article(models.Model): authors = models.ManyToManyField(Author) title = models.CharField(max_length=50) objects = models.Manager() by_a_sir = ArticleManager() - def __unicode__(self): + def __str__(self): return self.title diff --git a/tests/modeltests/get_or_create/models.py b/tests/modeltests/get_or_create/models.py index 78b92f09df..678f5a401c 100644 --- a/tests/modeltests/get_or_create/models.py +++ b/tests/modeltests/get_or_create/models.py @@ -9,14 +9,16 @@ parameters. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) birthday = models.DateField() - def __unicode__(self): + def __str__(self): return '%s %s' % (self.first_name, self.last_name) class ManualPrimaryKeyTest(models.Model): diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py index b685750347..f388ddf403 100644 --- a/tests/modeltests/lookup/models.py +++ b/tests/modeltests/lookup/models.py @@ -8,6 +8,7 @@ from __future__ import unicode_literals from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class Author(models.Model): @@ -15,6 +16,7 @@ class Author(models.Model): class Meta: ordering = ('name', ) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() @@ -22,7 +24,7 @@ class Article(models.Model): class Meta: ordering = ('-pub_date', 'headline') - def __unicode__(self): + def __str__(self): return self.headline class Tag(models.Model): @@ -31,24 +33,27 @@ class Tag(models.Model): class Meta: ordering = ('name', ) +@python_2_unicode_compatible class Season(models.Model): year = models.PositiveSmallIntegerField() gt = models.IntegerField(null=True, blank=True) - def __unicode__(self): + def __str__(self): return six.text_type(self.year) +@python_2_unicode_compatible class Game(models.Model): season = models.ForeignKey(Season, related_name='games') home = models.CharField(max_length=100) away = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return "%s at %s" % (self.away, self.home) +@python_2_unicode_compatible class Player(models.Model): name = models.CharField(max_length=100) games = models.ManyToManyField(Game, related_name='players') - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/m2m_and_m2o/models.py b/tests/modeltests/m2m_and_m2o/models.py index 92ed3fbcd9..99c7b01017 100644 --- a/tests/modeltests/m2m_and_m2o/models.py +++ b/tests/modeltests/m2m_and_m2o/models.py @@ -7,17 +7,19 @@ from __future__ import unicode_literals from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class User(models.Model): username = models.CharField(max_length=20) +@python_2_unicode_compatible class Issue(models.Model): num = models.IntegerField() cc = models.ManyToManyField(User, blank=True, related_name='test_issue_cc') client = models.ForeignKey(User, related_name='test_issue_client') - def __unicode__(self): + def __str__(self): return six.text_type(self.num) class Meta: diff --git a/tests/modeltests/m2m_intermediary/models.py b/tests/modeltests/m2m_intermediary/models.py index 85786e8458..e9ae422afb 100644 --- a/tests/modeltests/m2m_intermediary/models.py +++ b/tests/modeltests/m2m_intermediary/models.py @@ -12,27 +12,31 @@ field, which specifies the ``Reporter``'s position for the given article from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() - def __unicode__(self): + def __str__(self): return self.headline +@python_2_unicode_compatible class Writer(models.Model): reporter = models.ForeignKey(Reporter) article = models.ForeignKey(Article) position = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return '%s (%s)' % (self.reporter, self.position) diff --git a/tests/modeltests/m2m_multiple/models.py b/tests/modeltests/m2m_multiple/models.py index 3efe7108b9..c2e3b030a4 100644 --- a/tests/modeltests/m2m_multiple/models.py +++ b/tests/modeltests/m2m_multiple/models.py @@ -8,16 +8,19 @@ Set ``related_name`` to designate what the reverse relationship is called. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=50) pub_date = models.DateTimeField() @@ -26,6 +29,6 @@ class Article(models.Model): class Meta: ordering = ('pub_date',) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/m2m_recursive/models.py b/tests/modeltests/m2m_recursive/models.py index 83c943ae60..b69930208c 100644 --- a/tests/modeltests/m2m_recursive/models.py +++ b/tests/modeltests/m2m_recursive/models.py @@ -17,12 +17,14 @@ appropriate. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=20) friends = models.ManyToManyField('self') idols = models.ManyToManyField('self', symmetrical=False, related_name='stalkers') - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/m2m_signals/models.py b/tests/modeltests/m2m_signals/models.py index 526c4a782e..e997d87f21 100644 --- a/tests/modeltests/m2m_signals/models.py +++ b/tests/modeltests/m2m_signals/models.py @@ -1,15 +1,18 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Part(models.Model): name = models.CharField(max_length=20) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Car(models.Model): name = models.CharField(max_length=20) default_parts = models.ManyToManyField(Part) @@ -18,12 +21,13 @@ class Car(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name class SportsCar(Car): price = models.IntegerField() +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=20) fans = models.ManyToManyField('self', related_name='idols', symmetrical=False) @@ -32,5 +36,5 @@ class Person(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/m2m_through/models.py b/tests/modeltests/m2m_through/models.py index aa71a049cb..a896f6d9ff 100644 --- a/tests/modeltests/m2m_through/models.py +++ b/tests/modeltests/m2m_through/models.py @@ -1,18 +1,21 @@ from datetime import datetime from django.db import models +from django.utils.encoding import python_2_unicode_compatible # M2M described on one of the models +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=128) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') @@ -22,9 +25,10 @@ class Group(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Membership(models.Model): person = models.ForeignKey(Person) group = models.ForeignKey(Group) @@ -34,16 +38,17 @@ class Membership(models.Model): class Meta: ordering = ('date_joined', 'invite_reason', 'group') - def __unicode__(self): + def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) +@python_2_unicode_compatible class CustomMembership(models.Model): person = models.ForeignKey(Person, db_column="custom_person_column", related_name="custom_person_related_name") group = models.ForeignKey(Group) weird_fk = models.ForeignKey(Membership, null=True) date_joined = models.DateTimeField(default=datetime.now) - def __unicode__(self): + def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) class Meta: @@ -54,11 +59,12 @@ class TestNoDefaultsOrNulls(models.Model): group = models.ForeignKey(Group) nodefaultnonull = models.CharField(max_length=5) +@python_2_unicode_compatible class PersonSelfRefM2M(models.Model): name = models.CharField(max_length=5) friends = models.ManyToManyField('self', through="Friendship", symmetrical=False) - def __unicode__(self): + def __str__(self): return self.name class Friendship(models.Model): diff --git a/tests/modeltests/m2o_recursive/models.py b/tests/modeltests/m2o_recursive/models.py index c0a4bdeec2..2775d713dd 100644 --- a/tests/modeltests/m2o_recursive/models.py +++ b/tests/modeltests/m2o_recursive/models.py @@ -11,19 +11,22 @@ Set ``related_name`` to designate what the reverse relationship is called. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) parent = models.ForeignKey('self', blank=True, null=True, related_name='child_set') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Person(models.Model): full_name = models.CharField(max_length=20) mother = models.ForeignKey('self', null=True, related_name='mothers_child_set') father = models.ForeignKey('self', null=True, related_name='fathers_child_set') - def __unicode__(self): + def __str__(self): return self.full_name diff --git a/tests/modeltests/many_to_many/models.py b/tests/modeltests/many_to_many/models.py index 5076e35653..a196c85092 100644 --- a/tests/modeltests/many_to_many/models.py +++ b/tests/modeltests/many_to_many/models.py @@ -8,22 +8,25 @@ objects, and a ``Publication`` has multiple ``Article`` objects. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Publication(models.Model): title = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return self.title class Meta: ordering = ('title',) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) publications = models.ManyToManyField(Publication) - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/modeltests/many_to_one/models.py b/tests/modeltests/many_to_one/models.py index 0d2688e8a4..4e2ed67eea 100644 --- a/tests/modeltests/many_to_one/models.py +++ b/tests/modeltests/many_to_one/models.py @@ -6,22 +6,25 @@ To define a many-to-one relationship, use ``ForeignKey()``. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) email = models.EmailField() - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() reporter = models.ForeignKey(Reporter) - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/modeltests/many_to_one_null/models.py b/tests/modeltests/many_to_one_null/models.py index be7e650c65..e00ca85928 100644 --- a/tests/modeltests/many_to_one_null/models.py +++ b/tests/modeltests/many_to_one_null/models.py @@ -6,14 +6,17 @@ To define a many-to-one relationship that can have a null foreign key, use """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) reporter = models.ForeignKey(Reporter, null=True) @@ -21,5 +24,5 @@ class Article(models.Model): class Meta: ordering = ('headline',) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 8942b21f73..f4bdcad4af 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -14,6 +14,7 @@ import tempfile from django.core.files.storage import FileSystemStorage from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible temp_storage_dir = tempfile.mkdtemp(dir=os.environ['DJANGO_TEST_TEMP_DIR']) @@ -31,23 +32,26 @@ ARTICLE_STATUS_CHAR = ( ('l', 'Live'), ) +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) slug = models.SlugField(max_length=20) url = models.CharField('The URL', max_length=40) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Writer(models.Model): name = models.CharField(max_length=50, help_text='Use both first and last names.') class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=50) slug = models.SlugField() @@ -64,7 +68,7 @@ class Article(models.Model): self.created = datetime.date.today() return super(Article, self).save() - def __unicode__(self): + def __str__(self): return self.headline class ImprovedArticle(models.Model): @@ -76,26 +80,29 @@ class ImprovedArticleWithParentLink(models.Model): class BetterWriter(Writer): score = models.IntegerField() +@python_2_unicode_compatible class WriterProfile(models.Model): writer = models.OneToOneField(Writer, primary_key=True) age = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s is %s" % (self.writer, self.age) from django.contrib.localflavor.us.models import PhoneNumberField +@python_2_unicode_compatible class PhoneNumber(models.Model): phone = PhoneNumberField() description = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return self.phone +@python_2_unicode_compatible class TextFile(models.Model): description = models.CharField(max_length=20) file = models.FileField(storage=temp_storage, upload_to='tests', max_length=15) - def __unicode__(self): + def __str__(self): return self.description try: @@ -110,6 +117,7 @@ try: test_images = True + @python_2_unicode_compatible class ImageFile(models.Model): def custom_upload_path(self, filename): path = self.path or 'tests' @@ -125,9 +133,10 @@ try: width_field='width', height_field='height') path = models.CharField(max_length=16, blank=True, default='') - def __unicode__(self): + def __str__(self): return self.description + @python_2_unicode_compatible class OptionalImageFile(models.Model): def custom_upload_path(self, filename): path = self.path or 'tests' @@ -141,28 +150,31 @@ try: height = models.IntegerField(editable=False, null=True) path = models.CharField(max_length=16, blank=True, default='') - def __unicode__(self): + def __str__(self): return self.description except ImportError: test_images = False +@python_2_unicode_compatible class CommaSeparatedInteger(models.Model): field = models.CommaSeparatedIntegerField(max_length=20) - def __unicode__(self): + def __str__(self): return self.field +@python_2_unicode_compatible class Product(models.Model): slug = models.SlugField(unique=True) - def __unicode__(self): + def __str__(self): return self.slug +@python_2_unicode_compatible class Price(models.Model): price = models.DecimalField(max_digits=10, decimal_places=2) quantity = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s for %s" % (self.quantity, self.price) class Meta: @@ -171,6 +183,7 @@ class Price(models.Model): class ArticleStatus(models.Model): status = models.CharField(max_length=2, choices=ARTICLE_STATUS_CHAR, blank=True, null=True) +@python_2_unicode_compatible class Inventory(models.Model): barcode = models.PositiveIntegerField(unique=True) parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True) @@ -179,7 +192,7 @@ class Inventory(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name class Book(models.Model): @@ -202,31 +215,34 @@ class BookXtra(models.Model): class DerivedBook(Book, BookXtra): pass +@python_2_unicode_compatible class ExplicitPK(models.Model): key = models.CharField(max_length=20, primary_key=True) desc = models.CharField(max_length=20, blank=True, unique=True) class Meta: unique_together = ('key', 'desc') - def __unicode__(self): + def __str__(self): return self.key +@python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=50, unique_for_date='posted', blank=True) slug = models.CharField(max_length=50, unique_for_year='posted', blank=True) subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) posted = models.DateField() - def __unicode__(self): + def __str__(self): return self.name class DerivedPost(Post): pass +@python_2_unicode_compatible class BigInt(models.Model): biggie = models.BigIntegerField() - def __unicode__(self): + def __str__(self): return six.text_type(self.biggie) class MarkupField(models.CharField): diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py index a01a2da0fa..ae152448ab 100644 --- a/tests/modeltests/model_formsets/models.py +++ b/tests/modeltests/model_formsets/models.py @@ -4,20 +4,23 @@ import datetime from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name class BetterAuthor(Author): write_speed = models.IntegerField() +@python_2_unicode_compatible class Book(models.Model): author = models.ForeignKey(Author) title = models.CharField(max_length=100) @@ -28,20 +31,22 @@ class Book(models.Model): ) ordering = ['id'] - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class BookWithCustomPK(models.Model): my_pk = models.DecimalField(max_digits=5, decimal_places=0, primary_key=True) author = models.ForeignKey(Author) title = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return '%s: %s' % (self.my_pk, self.title) class Editor(models.Model): name = models.CharField(max_length=100) +@python_2_unicode_compatible class BookWithOptionalAltEditor(models.Model): author = models.ForeignKey(Author) # Optional secondary author @@ -53,21 +58,23 @@ class BookWithOptionalAltEditor(models.Model): ('author', 'title', 'alt_editor'), ) - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class AlternateBook(Book): notes = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return '%s - %s' % (self.title, self.notes) +@python_2_unicode_compatible class AuthorMeeting(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Author) created = models.DateField(editable=False) - def __unicode__(self): + def __str__(self): return self.name class CustomPrimaryKey(models.Model): @@ -77,19 +84,21 @@ class CustomPrimaryKey(models.Model): # models for inheritance tests. +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) city = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Owner(models.Model): auto_id = models.AutoField(primary_key=True) name = models.CharField(max_length=100) place = models.ForeignKey(Place) - def __unicode__(self): + def __str__(self): return "%s at %s" % (self.name, self.place) class Location(models.Model): @@ -98,30 +107,34 @@ class Location(models.Model): lat = models.CharField(max_length=100) lon = models.CharField(max_length=100) +@python_2_unicode_compatible class OwnerProfile(models.Model): owner = models.OneToOneField(Owner, primary_key=True) age = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s is %d" % (self.owner.name, self.age) +@python_2_unicode_compatible class Restaurant(Place): serves_pizza = models.BooleanField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Product(models.Model): slug = models.SlugField(unique=True) - def __unicode__(self): + def __str__(self): return self.slug +@python_2_unicode_compatible class Price(models.Model): price = models.DecimalField(max_digits=10, decimal_places=2) quantity = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s for %s" % (self.quantity, self.price) class Meta: @@ -136,12 +149,14 @@ class ClassyMexicanRestaurant(MexicanRestaurant): # models for testing unique_together validation when a fk is involved and # using inlineformset_factory. +@python_2_unicode_compatible class Repository(models.Model): name = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Revision(models.Model): repository = models.ForeignKey(Repository) revision = models.CharField(max_length=40) @@ -149,7 +164,7 @@ class Revision(models.Model): class Meta: unique_together = (("repository", "revision"),) - def __unicode__(self): + def __str__(self): return "%s (%s)" % (self.revision, six.text_type(self.repository)) # models for testing callable defaults (see bug #7975). If you define a model @@ -167,32 +182,36 @@ class Membership(models.Model): class Team(models.Model): name = models.CharField(max_length=100) +@python_2_unicode_compatible class Player(models.Model): team = models.ForeignKey(Team, null=True) name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name # Models for testing custom ModelForm save methods in formsets and inline formsets +@python_2_unicode_compatible class Poet(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Poem(models.Model): poet = models.ForeignKey(Poet) name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=50, unique_for_date='posted', blank=True) slug = models.CharField(max_length=50, unique_for_year='posted', blank=True) subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) posted = models.DateField() - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py index 37ca603021..2101f394f7 100644 --- a/tests/modeltests/model_inheritance/models.py +++ b/tests/modeltests/model_inheritance/models.py @@ -14,11 +14,13 @@ Both styles are demonstrated here. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible # # Abstract base classes # +@python_2_unicode_compatible class CommonInfo(models.Model): name = models.CharField(max_length=50) age = models.PositiveIntegerField() @@ -27,7 +29,7 @@ class CommonInfo(models.Model): abstract = True ordering = ['name'] - def __unicode__(self): + def __str__(self): return '%s %s' % (self.__class__.__name__, self.name) class Worker(CommonInfo): @@ -49,6 +51,7 @@ class StudentWorker(Student, Worker): class Post(models.Model): title = models.CharField(max_length=50) +@python_2_unicode_compatible class Attachment(models.Model): post = models.ForeignKey(Post, related_name='attached_%(class)s_set') content = models.TextField() @@ -56,7 +59,7 @@ class Attachment(models.Model): class Meta: abstract = True - def __unicode__(self): + def __str__(self): return self.content class Comment(Attachment): @@ -69,17 +72,19 @@ class Link(Attachment): # Multi-table inheritance # +@python_2_unicode_compatible class Chef(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "%s the chef" % self.name +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name class Rating(models.Model): @@ -89,6 +94,7 @@ class Rating(models.Model): abstract = True ordering = ['-rating'] +@python_2_unicode_compatible class Restaurant(Place, Rating): serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField() @@ -97,27 +103,30 @@ class Restaurant(Place, Rating): class Meta(Rating.Meta): db_table = 'my_restaurant' - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.name +@python_2_unicode_compatible class ItalianRestaurant(Restaurant): serves_gnocchi = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the italian restaurant" % self.name +@python_2_unicode_compatible class Supplier(Place): customers = models.ManyToManyField(Restaurant, related_name='provider') - def __unicode__(self): + def __str__(self): return "%s the supplier" % self.name +@python_2_unicode_compatible class ParkingLot(Place): # An explicit link to the parent (we can control the attribute name). parent = models.OneToOneField(Place, primary_key=True, parent_link=True) main_site = models.ForeignKey(Place, related_name='lot') - def __unicode__(self): + def __str__(self): return "%s the parking lot" % self.name # @@ -139,10 +148,11 @@ class NamedURL(models.Model): class Meta: abstract = True +@python_2_unicode_compatible class Copy(NamedURL): content = models.TextField() - def __unicode__(self): + def __str__(self): return self.content class Mixin(object): diff --git a/tests/modeltests/model_inheritance_same_model_name/models.py b/tests/modeltests/model_inheritance_same_model_name/models.py index de7541694a..801724df18 100644 --- a/tests/modeltests/model_inheritance_same_model_name/models.py +++ b/tests/modeltests/model_inheritance_same_model_name/models.py @@ -11,12 +11,14 @@ from __future__ import absolute_import from django.db import models from ..model_inheritance.models import NamedURL +from django.utils.encoding import python_2_unicode_compatible # # Abstract base classes with related models # +@python_2_unicode_compatible class Copy(NamedURL): content = models.TextField() - def __unicode__(self): + def __str__(self): return self.content diff --git a/tests/modeltests/one_to_one/models.py b/tests/modeltests/one_to_one/models.py index 8c6d56a795..9599496cb7 100644 --- a/tests/modeltests/one_to_one/models.py +++ b/tests/modeltests/one_to_one/models.py @@ -8,28 +8,32 @@ In this example, a ``Place`` optionally can be a ``Restaurant``. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name +@python_2_unicode_compatible class Restaurant(models.Model): place = models.OneToOneField(Place, primary_key=True) serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.place.name +@python_2_unicode_compatible class Waiter(models.Model): restaurant = models.ForeignKey(Restaurant) name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "%s the waiter at %s" % (self.name, self.restaurant) class ManualPrimaryKey(models.Model): @@ -40,10 +44,11 @@ class RelatedModel(models.Model): link = models.OneToOneField(ManualPrimaryKey) name = models.CharField(max_length = 50) +@python_2_unicode_compatible class MultiModel(models.Model): link1 = models.OneToOneField(Place) link2 = models.OneToOneField(ManualPrimaryKey) name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "Multimodel %s" % self.name diff --git a/tests/modeltests/or_lookups/models.py b/tests/modeltests/or_lookups/models.py index 0037b41afb..f146b2e72d 100644 --- a/tests/modeltests/or_lookups/models.py +++ b/tests/modeltests/or_lookups/models.py @@ -10,8 +10,10 @@ clauses using the variable ``django.db.models.Q`` (or any object with an """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=50) pub_date = models.DateTimeField() @@ -19,5 +21,5 @@ class Article(models.Model): class Meta: ordering = ('pub_date',) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/order_with_respect_to/models.py b/tests/modeltests/order_with_respect_to/models.py index a4e20c2fe0..06bb56b141 100644 --- a/tests/modeltests/order_with_respect_to/models.py +++ b/tests/modeltests/order_with_respect_to/models.py @@ -4,11 +4,13 @@ Tests for the order_with_respect_to Meta attribute. from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class Question(models.Model): text = models.CharField(max_length=200) +@python_2_unicode_compatible class Answer(models.Model): text = models.CharField(max_length=200) question = models.ForeignKey(Question) @@ -16,9 +18,10 @@ class Answer(models.Model): class Meta: order_with_respect_to = 'question' - def __unicode__(self): + def __str__(self): return six.text_type(self.text) +@python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=200) parent = models.ForeignKey("self", related_name="children", null=True) @@ -26,5 +29,5 @@ class Post(models.Model): class Meta: order_with_respect_to = "parent" - def __unicode__(self): + def __str__(self): return self.title diff --git a/tests/modeltests/ordering/models.py b/tests/modeltests/ordering/models.py index bfb4b97107..67126e1bda 100644 --- a/tests/modeltests/ordering/models.py +++ b/tests/modeltests/ordering/models.py @@ -14,22 +14,25 @@ undefined -- not random, just undefined. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() class Meta: ordering = ('-pub_date', 'headline') - def __unicode__(self): + def __str__(self): return self.headline +@python_2_unicode_compatible class ArticlePKOrdering(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() class Meta: ordering = ('-pk',) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/pagination/models.py b/tests/modeltests/pagination/models.py index 48484dd59b..779d3029ba 100644 --- a/tests/modeltests/pagination/models.py +++ b/tests/modeltests/pagination/models.py @@ -7,11 +7,13 @@ objects into easily readable pages. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/prefetch_related/models.py b/tests/modeltests/prefetch_related/models.py index 589f78c7d3..85488f0879 100644 --- a/tests/modeltests/prefetch_related/models.py +++ b/tests/modeltests/prefetch_related/models.py @@ -1,16 +1,18 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible ## Basic tests +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=50, unique=True) first_book = models.ForeignKey('Book', related_name='first_time_authors') favorite_authors = models.ManyToManyField( 'self', through='FavoriteAuthors', symmetrical=False, related_name='favors_me') - def __unicode__(self): + def __str__(self): return self.name class Meta: @@ -30,6 +32,7 @@ class FavoriteAuthors(models.Model): ordering = ['id'] +@python_2_unicode_compatible class AuthorAddress(models.Model): author = models.ForeignKey(Author, to_field='name', related_name='addresses') address = models.TextField() @@ -37,15 +40,16 @@ class AuthorAddress(models.Model): class Meta: ordering = ['id'] - def __unicode__(self): + def __str__(self): return self.address +@python_2_unicode_compatible class Book(models.Model): title = models.CharField(max_length=255) authors = models.ManyToManyField(Author, related_name='books') - def __unicode__(self): + def __str__(self): return self.title class Meta: @@ -58,11 +62,12 @@ class BookWithYear(Book): AuthorWithAge, related_name='books_with_year') +@python_2_unicode_compatible class Reader(models.Model): name = models.CharField(max_length=50) books_read = models.ManyToManyField(Book, related_name='read_by') - def __unicode__(self): + def __str__(self): return self.name class Meta: @@ -86,13 +91,14 @@ class TeacherManager(models.Manager): return super(TeacherManager, self).get_query_set().prefetch_related('qualifications') +@python_2_unicode_compatible class Teacher(models.Model): name = models.CharField(max_length=50) qualifications = models.ManyToManyField(Qualification) objects = TeacherManager() - def __unicode__(self): + def __str__(self): return "%s (%s)" % (self.name, ", ".join(q.name for q in self.qualifications.all())) class Meta: @@ -109,6 +115,7 @@ class Department(models.Model): ## GenericRelation/GenericForeignKey tests +@python_2_unicode_compatible class TaggedItem(models.Model): tag = models.SlugField() content_type = models.ForeignKey(ContentType, related_name="taggeditem_set2") @@ -119,7 +126,7 @@ class TaggedItem(models.Model): created_by_fkey = models.PositiveIntegerField(null=True) created_by = generic.GenericForeignKey('created_by_ct', 'created_by_fkey',) - def __unicode__(self): + def __str__(self): return self.tag @@ -169,12 +176,13 @@ class Person(models.Model): ## Models for nullable FK tests +@python_2_unicode_compatible class Employee(models.Model): name = models.CharField(max_length=50) boss = models.ForeignKey('self', null=True, related_name='serfs') - def __unicode__(self): + def __str__(self): return self.name class Meta: diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py index 49fd87deff..6c962aadc8 100644 --- a/tests/modeltests/proxy_models/models.py +++ b/tests/modeltests/proxy_models/models.py @@ -5,6 +5,7 @@ than using a new table of their own. This allows them to act as simple proxies, providing a modified interface to the data from the base class. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible # A couple of managers for testing managing overriding in proxy model cases. @@ -16,6 +17,7 @@ class SubManager(models.Manager): def get_query_set(self): return super(SubManager, self).get_query_set().exclude(name="wilma") +@python_2_unicode_compatible class Person(models.Model): """ A simple concrete base class. @@ -24,7 +26,7 @@ class Person(models.Model): objects = PersonManager() - def __unicode__(self): + def __str__(self): return self.name class Abstract(models.Model): @@ -82,10 +84,11 @@ class MyPersonProxy(MyPerson): class LowerStatusPerson(MyPersonProxy): status = models.CharField(max_length=80) +@python_2_unicode_compatible class User(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name class UserProxy(User): @@ -100,11 +103,12 @@ class UserProxyProxy(UserProxy): class Country(models.Model): name = models.CharField(max_length=50) +@python_2_unicode_compatible class State(models.Model): name = models.CharField(max_length=50) country = models.ForeignKey(Country) - def __unicode__(self): + def __str__(self): return self.name class StateProxy(State): @@ -124,11 +128,12 @@ class ProxyTrackerUser(TrackerUser): proxy = True +@python_2_unicode_compatible class Issue(models.Model): summary = models.CharField(max_length=255) assignee = models.ForeignKey(TrackerUser) - def __unicode__(self): + def __str__(self): return ':'.join((self.__class__.__name__,self.summary,)) class Bug(Issue): diff --git a/tests/modeltests/reserved_names/models.py b/tests/modeltests/reserved_names/models.py index 010649e681..8e942b20e8 100644 --- a/tests/modeltests/reserved_names/models.py +++ b/tests/modeltests/reserved_names/models.py @@ -8,8 +8,10 @@ reserved-name usage. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Thing(models.Model): when = models.CharField(max_length=1, primary_key=True) join = models.CharField(max_length=1) @@ -22,5 +24,5 @@ class Thing(models.Model): class Meta: db_table = 'select' - def __unicode__(self): - return self.when \ No newline at end of file + def __str__(self): + return self.when diff --git a/tests/modeltests/reverse_lookup/models.py b/tests/modeltests/reverse_lookup/models.py index bb7a163327..ed58177770 100644 --- a/tests/modeltests/reverse_lookup/models.py +++ b/tests/modeltests/reverse_lookup/models.py @@ -5,25 +5,29 @@ This demonstrates the reverse lookup features of the database API. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class User(models.Model): name = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Poll(models.Model): question = models.CharField(max_length=200) creator = models.ForeignKey(User) - def __unicode__(self): + def __str__(self): return self.question +@python_2_unicode_compatible class Choice(models.Model): name = models.CharField(max_length=100) poll = models.ForeignKey(Poll, related_name="poll_choice") related_poll = models.ForeignKey(Poll, related_name="related_choice") - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/save_delete_hooks/models.py b/tests/modeltests/save_delete_hooks/models.py index bae60e4b86..a6e1abfb77 100644 --- a/tests/modeltests/save_delete_hooks/models.py +++ b/tests/modeltests/save_delete_hooks/models.py @@ -7,8 +7,10 @@ the methods. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) @@ -17,7 +19,7 @@ class Person(models.Model): super(Person, self).__init__(*args, **kwargs) self.data = [] - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) def save(self, *args, **kwargs): diff --git a/tests/modeltests/select_related/models.py b/tests/modeltests/select_related/models.py index 3c2e7721fd..ec41957adf 100644 --- a/tests/modeltests/select_related/models.py +++ b/tests/modeltests/select_related/models.py @@ -8,52 +8,61 @@ the select-related behavior will traverse. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible # Who remembers high school biology? +@python_2_unicode_compatible class Domain(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Kingdom(models.Model): name = models.CharField(max_length=50) domain = models.ForeignKey(Domain) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Phylum(models.Model): name = models.CharField(max_length=50) kingdom = models.ForeignKey(Kingdom) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Klass(models.Model): name = models.CharField(max_length=50) phylum = models.ForeignKey(Phylum) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Order(models.Model): name = models.CharField(max_length=50) klass = models.ForeignKey(Klass) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Family(models.Model): name = models.CharField(max_length=50) order = models.ForeignKey(Order) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Genus(models.Model): name = models.CharField(max_length=50) family = models.ForeignKey(Family) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Species(models.Model): name = models.CharField(max_length=50) genus = models.ForeignKey(Genus) - def __unicode__(self): - return self.name \ No newline at end of file + def __str__(self): + return self.name diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py index ebae7605b4..290478607b 100644 --- a/tests/modeltests/serializers/models.py +++ b/tests/modeltests/serializers/models.py @@ -11,28 +11,32 @@ from decimal import Decimal from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=20) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Article(models.Model): author = models.ForeignKey(Author) headline = models.CharField(max_length=50) @@ -42,28 +46,31 @@ class Article(models.Model): class Meta: ordering = ('pub_date',) - def __unicode__(self): + def __str__(self): return self.headline +@python_2_unicode_compatible class AuthorProfile(models.Model): author = models.OneToOneField(Author, primary_key=True) date_of_birth = models.DateField() - def __unicode__(self): + def __str__(self): return "Profile of %s" % self.author +@python_2_unicode_compatible class Actor(models.Model): name = models.CharField(max_length=20, primary_key=True) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Movie(models.Model): actor = models.ForeignKey(Actor) title = models.CharField(max_length=50) @@ -72,7 +79,7 @@ class Movie(models.Model): class Meta: ordering = ('title',) - def __unicode__(self): + def __str__(self): return self.title @@ -80,11 +87,12 @@ class Score(models.Model): score = models.FloatField() +@python_2_unicode_compatible class Team(object): def __init__(self, title): self.title = title - def __unicode__(self): + def __str__(self): raise NotImplementedError("Not so simple") def to_string(self): @@ -109,10 +117,11 @@ class TeamField(models.CharField): return self._get_val_from_obj(obj).to_string() +@python_2_unicode_compatible class Player(models.Model): name = models.CharField(max_length=50) rank = models.IntegerField() team = TeamField() - def __unicode__(self): + def __str__(self): return '%s (%d) playing for %s' % (self.name, self.rank, self.team.to_string()) diff --git a/tests/modeltests/signals/models.py b/tests/modeltests/signals/models.py index 18ca467655..e54a22fce9 100644 --- a/tests/modeltests/signals/models.py +++ b/tests/modeltests/signals/models.py @@ -4,18 +4,21 @@ Testing signals before/after saving and deleting. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Car(models.Model): make = models.CharField(max_length=20) model = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.make, self.model) diff --git a/tests/modeltests/str/models.py b/tests/modeltests/str/models.py index 6f6b877c88..488012e861 100644 --- a/tests/modeltests/str/models.py +++ b/tests/modeltests/str/models.py @@ -15,6 +15,7 @@ if you prefer. You must be careful to encode the results correctly, though. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Article(models.Model): @@ -26,9 +27,10 @@ class Article(models.Model): # in ASCII. return self.headline +@python_2_unicode_compatible class InternationalArticle(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() - def __unicode__(self): - return self.headline \ No newline at end of file + def __str__(self): + return self.headline diff --git a/tests/modeltests/transactions/models.py b/tests/modeltests/transactions/models.py index 7a51b9ac1c..0f8d6b16ec 100644 --- a/tests/modeltests/transactions/models.py +++ b/tests/modeltests/transactions/models.py @@ -9,8 +9,10 @@ manually. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) @@ -19,5 +21,5 @@ class Reporter(models.Model): class Meta: ordering = ('first_name', 'last_name') - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) diff --git a/tests/modeltests/unmanaged_models/models.py b/tests/modeltests/unmanaged_models/models.py index 00303cf17a..0eef69977c 100644 --- a/tests/modeltests/unmanaged_models/models.py +++ b/tests/modeltests/unmanaged_models/models.py @@ -4,9 +4,11 @@ is generated for the table on various manage.py operations. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible # All of these models are created in the database by Django. +@python_2_unicode_compatible class A01(models.Model): f_a = models.CharField(max_length=10, db_index=True) f_b = models.IntegerField() @@ -14,9 +16,10 @@ class A01(models.Model): class Meta: db_table = 'a01' - def __unicode__(self): + def __str__(self): return self.f_a +@python_2_unicode_compatible class B01(models.Model): fk_a = models.ForeignKey(A01) f_a = models.CharField(max_length=10, db_index=True) @@ -27,9 +30,10 @@ class B01(models.Model): # 'managed' is True by default. This tests we can set it explicitly. managed = True - def __unicode__(self): + def __str__(self): return self.f_a +@python_2_unicode_compatible class C01(models.Model): mm_a = models.ManyToManyField(A01, db_table='d01') f_a = models.CharField(max_length=10, db_index=True) @@ -38,13 +42,14 @@ class C01(models.Model): class Meta: db_table = 'c01' - def __unicode__(self): + def __str__(self): return self.f_a # All of these models use the same tables as the previous set (they are shadows # of possibly a subset of the columns). There should be no creation errors, # since we have told Django they aren't managed by Django. +@python_2_unicode_compatible class A02(models.Model): f_a = models.CharField(max_length=10, db_index=True) @@ -52,9 +57,10 @@ class A02(models.Model): db_table = 'a01' managed = False - def __unicode__(self): + def __str__(self): return self.f_a +@python_2_unicode_compatible class B02(models.Model): class Meta: db_table = 'b01' @@ -64,11 +70,12 @@ class B02(models.Model): f_a = models.CharField(max_length=10, db_index=True) f_b = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.f_a # To re-use the many-to-many intermediate table, we need to manually set up # things up. +@python_2_unicode_compatible class C02(models.Model): mm_a = models.ManyToManyField(A02, through="Intermediate") f_a = models.CharField(max_length=10, db_index=True) @@ -78,7 +85,7 @@ class C02(models.Model): db_table = 'c01' managed = False - def __unicode__(self): + def __str__(self): return self.f_a class Intermediate(models.Model): diff --git a/tests/modeltests/update/models.py b/tests/modeltests/update/models.py index b93e4a7aae..08472d98b1 100644 --- a/tests/modeltests/update/models.py +++ b/tests/modeltests/update/models.py @@ -5,21 +5,24 @@ updates. from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class DataPoint(models.Model): name = models.CharField(max_length=20) value = models.CharField(max_length=20) another_value = models.CharField(max_length=20, blank=True) - def __unicode__(self): + def __str__(self): return six.text_type(self.name) +@python_2_unicode_compatible class RelatedPoint(models.Model): name = models.CharField(max_length=20) data = models.ForeignKey(DataPoint) - def __unicode__(self): + def __str__(self): return six.text_type(self.name) diff --git a/tests/modeltests/update_only_fields/models.py b/tests/modeltests/update_only_fields/models.py index 968dba9916..01184f715b 100644 --- a/tests/modeltests/update_only_fields/models.py +++ b/tests/modeltests/update_only_fields/models.py @@ -1,5 +1,6 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible GENDER_CHOICES = ( ('M', 'Male'), @@ -10,11 +11,12 @@ class Account(models.Model): num = models.IntegerField() +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=20) gender = models.CharField(max_length=1, choices=GENDER_CHOICES) - def __unicode__(self): + def __str__(self): return self.name @@ -24,11 +26,12 @@ class Employee(Person): accounts = models.ManyToManyField('Account', related_name='employees', blank=True, null=True) +@python_2_unicode_compatible class Profile(models.Model): name = models.CharField(max_length=200) salary = models.FloatField(default=1000.0) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py index 26fff4b863..dcd9ced52c 100644 --- a/tests/modeltests/validation/models.py +++ b/tests/modeltests/validation/models.py @@ -4,6 +4,7 @@ from datetime import datetime from django.core.exceptions import ValidationError from django.db import models +from django.utils.encoding import python_2_unicode_compatible def validate_answer_to_universe(value): @@ -66,13 +67,14 @@ class Article(models.Model): if self.pub_date is None: self.pub_date = datetime.now() +@python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=50, unique_for_date='posted', blank=True) slug = models.CharField(max_length=50, unique_for_year='posted', blank=True) subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) posted = models.DateField() - def __unicode__(self): + def __str__(self): return self.name class FlexibleDatePost(models.Model): diff --git a/tests/regressiontests/admin_changelist/models.py b/tests/regressiontests/admin_changelist/models.py index 487db50689..4ba2f9c503 100644 --- a/tests/regressiontests/admin_changelist/models.py +++ b/tests/regressiontests/admin_changelist/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Event(models.Model): # Oracle can have problems with a column named "date" @@ -20,17 +21,19 @@ class Band(models.Model): nr_of_members = models.PositiveIntegerField() genres = models.ManyToManyField(Genre) +@python_2_unicode_compatible class Musician(models.Model): name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=30) members = models.ManyToManyField(Musician, through='Membership') - def __unicode__(self): + def __str__(self): return self.name class Membership(models.Model): diff --git a/tests/regressiontests/admin_custom_urls/models.py b/tests/regressiontests/admin_custom_urls/models.py index facb4b52e7..4bc9ce561e 100644 --- a/tests/regressiontests/admin_custom_urls/models.py +++ b/tests/regressiontests/admin_custom_urls/models.py @@ -2,13 +2,15 @@ from functools import update_wrapper from django.contrib import admin from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Action(models.Model): name = models.CharField(max_length=50, primary_key=True) description = models.CharField(max_length=70) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/admin_filters/models.py b/tests/regressiontests/admin_filters/models.py index 371c67061f..e0b8bde2de 100644 --- a/tests/regressiontests/admin_filters/models.py +++ b/tests/regressiontests/admin_filters/models.py @@ -2,8 +2,10 @@ from __future__ import unicode_literals from django.contrib.auth.models import User from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Book(models.Model): title = models.CharField(max_length=50) year = models.PositiveIntegerField(null=True, blank=True) @@ -13,20 +15,22 @@ class Book(models.Model): date_registered = models.DateField(null=True) no = models.IntegerField(verbose_name='number', blank=True, null=True) # This field is intentionally 2 characters long. See #16080. - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Department(models.Model): code = models.CharField(max_length=4, unique=True) description = models.CharField(max_length=50, blank=True, null=True) - def __unicode__(self): + def __str__(self): return self.description +@python_2_unicode_compatible class Employee(models.Model): department = models.ForeignKey(Department, to_field="code") name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py index 5a0f4d84b9..5b703a7481 100644 --- a/tests/regressiontests/admin_inlines/models.py +++ b/tests/regressiontests/admin_inlines/models.py @@ -7,22 +7,26 @@ from __future__ import unicode_literals from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Parent(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Teacher(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Child(models.Model): name = models.CharField(max_length=50) teacher = models.ForeignKey(Teacher) @@ -31,7 +35,7 @@ class Child(models.Model): object_id = models.PositiveIntegerField() parent = generic.GenericForeignKey() - def __unicode__(self): + def __str__(self): return 'I am %s, a child of %s' % (self.name, self.parent) diff --git a/tests/regressiontests/admin_scripts/models.py b/tests/regressiontests/admin_scripts/models.py index da803d0174..cd96052098 100644 --- a/tests/regressiontests/admin_scripts/models.py +++ b/tests/regressiontests/admin_scripts/models.py @@ -1,11 +1,13 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/regressiontests/admin_util/models.py b/tests/regressiontests/admin_util/models.py index 5541097022..b3504a1fa4 100644 --- a/tests/regressiontests/admin_util/models.py +++ b/tests/regressiontests/admin_util/models.py @@ -1,5 +1,6 @@ from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class Article(models.Model): @@ -18,11 +19,12 @@ class Article(models.Model): return "nothing" test_from_model_with_override.short_description = "not What you Expect" +@python_2_unicode_compatible class Count(models.Model): num = models.PositiveSmallIntegerField() parent = models.ForeignKey('self', null=True) - def __unicode__(self): + def __str__(self): return six.text_type(self.num) class Event(models.Model): diff --git a/tests/regressiontests/admin_validation/models.py b/tests/regressiontests/admin_validation/models.py index 5e080a9232..d23849a2a8 100644 --- a/tests/regressiontests/admin_validation/models.py +++ b/tests/regressiontests/admin_validation/models.py @@ -3,12 +3,14 @@ Tests of ModelAdmin validation logic. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Album(models.Model): title = models.CharField(max_length=150) +@python_2_unicode_compatible class Song(models.Model): title = models.CharField(max_length=150) album = models.ForeignKey(Album) @@ -17,7 +19,7 @@ class Song(models.Model): class Meta: ordering = ('title',) - def __unicode__(self): + def __str__(self): return self.title def readonly_method_on_model(self): diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index aee193b572..0d5e327ecf 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -10,6 +10,7 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.core.files.storage import FileSystemStorage from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Section(models.Model): @@ -20,6 +21,7 @@ class Section(models.Model): name = models.CharField(max_length=100) +@python_2_unicode_compatible class Article(models.Model): """ A simple article to test admin views. Test backwards compatibility. @@ -29,7 +31,7 @@ class Article(models.Model): date = models.DateTimeField() section = models.ForeignKey(Section, null=True, blank=True) - def __unicode__(self): + def __str__(self): return self.title def model_year(self): @@ -38,30 +40,33 @@ class Article(models.Model): model_year.short_description = '' +@python_2_unicode_compatible class Book(models.Model): """ A simple book that has chapters. """ name = models.CharField(max_length=100, verbose_name='¿Name?') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Promo(models.Model): name = models.CharField(max_length=100, verbose_name='¿Name?') book = models.ForeignKey(Book) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Chapter(models.Model): title = models.CharField(max_length=100, verbose_name='¿Title?') content = models.TextField() book = models.ForeignKey(Book) - def __unicode__(self): + def __str__(self): return self.title class Meta: @@ -69,19 +74,21 @@ class Chapter(models.Model): verbose_name = '¿Chapter?' +@python_2_unicode_compatible class ChapterXtra1(models.Model): chap = models.OneToOneField(Chapter, verbose_name='¿Chap?') xtra = models.CharField(max_length=100, verbose_name='¿Xtra?') - def __unicode__(self): + def __str__(self): return '¿Xtra1: %s' % self.xtra +@python_2_unicode_compatible class ChapterXtra2(models.Model): chap = models.OneToOneField(Chapter, verbose_name='¿Chap?') xtra = models.CharField(max_length=100, verbose_name='¿Xtra?') - def __unicode__(self): + def __str__(self): return '¿Xtra2: %s' % self.xtra @@ -94,20 +101,22 @@ class CustomArticle(models.Model): date = models.DateTimeField() +@python_2_unicode_compatible class ModelWithStringPrimaryKey(models.Model): string_pk = models.CharField(max_length=255, primary_key=True) - def __unicode__(self): + def __str__(self): return self.string_pk def get_absolute_url(self): return '/dummy/%s/' % self.string_pk +@python_2_unicode_compatible class Color(models.Model): value = models.CharField(max_length=10) warm = models.BooleanField() - def __unicode__(self): + def __str__(self): return self.value # we replicate Color to register with another ModelAdmin @@ -115,29 +124,33 @@ class Color2(Color): class Meta: proxy = True +@python_2_unicode_compatible class Thing(models.Model): title = models.CharField(max_length=20) color = models.ForeignKey(Color, limit_choices_to={'warm': True}) - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Actor(models.Model): name = models.CharField(max_length=50) age = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Inquisition(models.Model): expected = models.BooleanField() leader = models.ForeignKey(Actor) country = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return "by %s from %s" % (self.leader, self.country) +@python_2_unicode_compatible class Sketch(models.Model): title = models.CharField(max_length=100) inquisition = models.ForeignKey(Inquisition, limit_choices_to={'leader__name': 'Palin', @@ -145,7 +158,7 @@ class Sketch(models.Model): 'expected': False, }) - def __unicode__(self): + def __str__(self): return self.title @@ -161,6 +174,7 @@ class Fabric(models.Model): surface = models.CharField(max_length=20, choices=NG_CHOICES) +@python_2_unicode_compatible class Person(models.Model): GENDER_CHOICES = ( (1, "Male"), @@ -171,20 +185,22 @@ class Person(models.Model): age = models.IntegerField(default=21) alive = models.BooleanField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Persona(models.Model): """ A simple persona associated with accounts, to test inlining of related accounts which inherit from a common accounts class. """ name = models.CharField(blank=False, max_length=80) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Account(models.Model): """ A simple, generic account encapsulating the information shared by all @@ -194,7 +210,7 @@ class Account(models.Model): persona = models.ForeignKey(Persona, related_name="accounts") servicename = 'generic service' - def __unicode__(self): + def __str__(self): return "%s: %s" % (self.servicename, self.username) @@ -208,11 +224,12 @@ class BarAccount(Account): servicename = 'bar' +@python_2_unicode_compatible class Subscriber(models.Model): name = models.CharField(blank=False, max_length=80) email = models.EmailField(blank=False, max_length=175) - def __unicode__(self): + def __str__(self): return "%s (%s)" % (self.name, self.email) @@ -249,8 +266,9 @@ class Child(models.Model): name = models.CharField(max_length=30, blank=True) +@python_2_unicode_compatible class EmptyModel(models.Model): - def __unicode__(self): + def __str__(self): return "Primary key = %s" % self.id @@ -332,6 +350,7 @@ class FancyDoodad(Doodad): expensive = models.BooleanField(default=True) +@python_2_unicode_compatible class Category(models.Model): collector = models.ForeignKey(Collector) order = models.PositiveIntegerField() @@ -339,7 +358,7 @@ class Category(models.Model): class Meta: ordering = ('order',) - def __unicode__(self): + def __str__(self): return '%s:o%s' % (self.id, self.order) @@ -376,17 +395,19 @@ class Post(models.Model): return "Very awesome." +@python_2_unicode_compatible class Gadget(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Villain(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name @@ -394,6 +415,7 @@ class SuperVillain(Villain): pass +@python_2_unicode_compatible class FunkyTag(models.Model): "Because we all know there's only one real use case for GFKs." name = models.CharField(max_length=25) @@ -401,59 +423,65 @@ class FunkyTag(models.Model): object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Plot(models.Model): name = models.CharField(max_length=100) team_leader = models.ForeignKey(Villain, related_name='lead_plots') contact = models.ForeignKey(Villain, related_name='contact_plots') tags = generic.GenericRelation(FunkyTag) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class PlotDetails(models.Model): details = models.CharField(max_length=100) plot = models.OneToOneField(Plot) - def __unicode__(self): + def __str__(self): return self.details +@python_2_unicode_compatible class SecretHideout(models.Model): """ Secret! Not registered with the admin! """ location = models.CharField(max_length=100) villain = models.ForeignKey(Villain) - def __unicode__(self): + def __str__(self): return self.location +@python_2_unicode_compatible class SuperSecretHideout(models.Model): """ Secret! Not registered with the admin! """ location = models.CharField(max_length=100) supervillain = models.ForeignKey(SuperVillain) - def __unicode__(self): + def __str__(self): return self.location +@python_2_unicode_compatible class CyclicOne(models.Model): name = models.CharField(max_length=25) two = models.ForeignKey('CyclicTwo') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class CyclicTwo(models.Model): name = models.CharField(max_length=25) one = models.ForeignKey(CyclicOne) - def __unicode__(self): + def __str__(self): return self.name @@ -484,11 +512,12 @@ class Question(models.Model): question = models.CharField(max_length=20) +@python_2_unicode_compatible class Answer(models.Model): question = models.ForeignKey(Question, on_delete=models.PROTECT) answer = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return self.answer @@ -523,11 +552,12 @@ class Paper(models.Model): author = models.CharField(max_length=30, blank=True, null=True) +@python_2_unicode_compatible class CoverLetter(models.Model): author = models.CharField(max_length=30) date_written = models.DateField(null=True, blank=True) - def __unicode__(self): + def __str__(self): return self.author @@ -575,10 +605,11 @@ class AdminOrderedCallable(models.Model): order = models.IntegerField() stuff = models.CharField(max_length=200) +@python_2_unicode_compatible class Report(models.Model): title = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.title diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py index 81a4ec7aba..2e452798b7 100644 --- a/tests/regressiontests/admin_widgets/models.py +++ b/tests/regressiontests/admin_widgets/models.py @@ -2,39 +2,44 @@ from __future__ import unicode_literals from django.db import models from django.contrib.auth.models import User +from django.utils.encoding import python_2_unicode_compatible class MyFileField(models.FileField): pass +@python_2_unicode_compatible class Member(models.Model): name = models.CharField(max_length=100) birthdate = models.DateTimeField(blank=True, null=True) gender = models.CharField(max_length=1, blank=True, choices=[('M','Male'), ('F', 'Female')]) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Band(models.Model): name = models.CharField(max_length=100) members = models.ManyToManyField(Member) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Album(models.Model): band = models.ForeignKey(Band) name = models.CharField(max_length=100) cover_art = models.FileField(upload_to='albums') backside_art = MyFileField(upload_to='albums_back', null=True) - def __unicode__(self): + def __str__(self): return self.name class HiddenInventoryManager(models.Manager): def get_query_set(self): return super(HiddenInventoryManager, self).get_query_set().filter(hidden=False) +@python_2_unicode_compatible class Inventory(models.Model): barcode = models.PositiveIntegerField(unique=True) parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True) @@ -45,7 +50,7 @@ class Inventory(models.Model): default_manager = models.Manager() objects = HiddenInventoryManager() - def __unicode__(self): + def __str__(self): return self.name class Event(models.Model): @@ -56,12 +61,13 @@ class Event(models.Model): link = models.URLField(blank=True) min_age = models.IntegerField(blank=True, null=True) +@python_2_unicode_compatible class Car(models.Model): owner = models.ForeignKey(User) make = models.CharField(max_length=30) model = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.make, self.model) class CarTire(models.Model): @@ -103,19 +109,21 @@ class Advisor(models.Model): companies = models.ManyToManyField(Company) +@python_2_unicode_compatible class Student(models.Model): name = models.CharField(max_length=255) - def __unicode__(self): + def __str__(self): return self.name class Meta: ordering = ('name',) +@python_2_unicode_compatible class School(models.Model): name = models.CharField(max_length=255) students = models.ManyToManyField(Student, related_name='current_schools') alumni = models.ManyToManyField(Student, related_name='previous_schools') - def __unicode__(self): - return self.name \ No newline at end of file + def __str__(self): + return self.name diff --git a/tests/regressiontests/aggregation_regress/models.py b/tests/regressiontests/aggregation_regress/models.py index ccef9a5fc8..dd4ff50aec 100644 --- a/tests/regressiontests/aggregation_regress/models.py +++ b/tests/regressiontests/aggregation_regress/models.py @@ -1,24 +1,28 @@ # coding: utf-8 from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() friends = models.ManyToManyField('self', blank=True) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Publisher(models.Model): name = models.CharField(max_length=255) num_awards = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Book(models.Model): isbn = models.CharField(max_length=9) name = models.CharField(max_length=255) @@ -33,17 +37,18 @@ class Book(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Store(models.Model): name = models.CharField(max_length=255) books = models.ManyToManyField(Book) original_opening = models.DateTimeField() friday_night_closing = models.TimeField() - def __unicode__(self): + def __str__(self): return self.name class Entries(models.Model): @@ -58,8 +63,9 @@ class Clues(models.Model): Clue = models.CharField(max_length=150) +@python_2_unicode_compatible class HardbackBook(Book): weight = models.FloatField() - def __unicode__(self): + def __str__(self): return "%s (hardback): %s" % (self.name, self.weight) diff --git a/tests/regressiontests/backends/models.py b/tests/regressiontests/backends/models.py index af4952dce8..344cf4c798 100644 --- a/tests/regressiontests/backends/models.py +++ b/tests/regressiontests/backends/models.py @@ -3,21 +3,24 @@ from __future__ import unicode_literals from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models, connection +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Square(models.Model): root = models.IntegerField() square = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s ** 2 == %s" % (self.root, self.square) +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return '%s %s' % (self.first_name, self.last_name) @@ -55,18 +58,20 @@ class Post(models.Model): db_table = 'CaseSensitive_Post' +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() reporter = models.ForeignKey(Reporter) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/regressiontests/comment_tests/models.py b/tests/regressiontests/comment_tests/models.py index 7e16e48419..472b66decd 100644 --- a/tests/regressiontests/comment_tests/models.py +++ b/tests/regressiontests/comment_tests/models.py @@ -6,29 +6,33 @@ more information. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return '%s %s' % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): author = models.ForeignKey(Author) headline = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.headline +@python_2_unicode_compatible class Entry(models.Model): title = models.CharField(max_length=250) body = models.TextField() pub_date = models.DateField() enable_comments = models.BooleanField() - def __unicode__(self): + def __str__(self): return self.title class Book(models.Model): diff --git a/tests/regressiontests/custom_columns_regress/models.py b/tests/regressiontests/custom_columns_regress/models.py index c768c12772..169b2246c3 100644 --- a/tests/regressiontests/custom_columns_regress/models.py +++ b/tests/regressiontests/custom_columns_regress/models.py @@ -8,26 +8,29 @@ table creation or queries. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): Article_ID = models.AutoField(primary_key=True, db_column='Article ID') headline = models.CharField(max_length=100) authors = models.ManyToManyField('Author', db_table='my m2m table') primary_author = models.ForeignKey('Author', db_column='Author ID', related_name='primary_set') - def __unicode__(self): + def __str__(self): return self.headline class Meta: ordering = ('headline',) +@python_2_unicode_compatible class Author(models.Model): Author_ID = models.AutoField(primary_key=True, db_column='Author ID') first_name = models.CharField(max_length=30, db_column='first name') last_name = models.CharField(max_length=30, db_column='last name') - def __unicode__(self): + def __str__(self): return '%s %s' % (self.first_name, self.last_name) class Meta: diff --git a/tests/regressiontests/custom_managers_regress/models.py b/tests/regressiontests/custom_managers_regress/models.py index 3c4e621769..71073f0fe7 100644 --- a/tests/regressiontests/custom_managers_regress/models.py +++ b/tests/regressiontests/custom_managers_regress/models.py @@ -3,6 +3,7 @@ Regression tests for custom manager classes. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class RestrictedManager(models.Manager): @@ -12,12 +13,14 @@ class RestrictedManager(models.Manager): def get_query_set(self): return super(RestrictedManager, self).get_query_set().filter(is_public=True) +@python_2_unicode_compatible class RelatedModel(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class RestrictedModel(models.Model): name = models.CharField(max_length=50) is_public = models.BooleanField(default=False) @@ -26,9 +29,10 @@ class RestrictedModel(models.Model): objects = RestrictedManager() plain_manager = models.Manager() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class OneToOneRestrictedModel(models.Model): name = models.CharField(max_length=50) is_public = models.BooleanField(default=False) @@ -37,5 +41,5 @@ class OneToOneRestrictedModel(models.Model): objects = RestrictedManager() plain_manager = models.Manager() - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/datatypes/models.py b/tests/regressiontests/datatypes/models.py index d220d0893a..8e6027dd0f 100644 --- a/tests/regressiontests/datatypes/models.py +++ b/tests/regressiontests/datatypes/models.py @@ -4,8 +4,10 @@ types, which in the past were problematic for some database backends. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Donut(models.Model): name = models.CharField(max_length=100) is_frosted = models.BooleanField(default=False) @@ -18,7 +20,7 @@ class Donut(models.Model): class Meta: ordering = ('consumed_at',) - def __unicode__(self): + def __str__(self): return self.name class RumBaba(models.Model): diff --git a/tests/regressiontests/dates/models.py b/tests/regressiontests/dates/models.py index e1fc1e74fe..e4bffb7199 100644 --- a/tests/regressiontests/dates/models.py +++ b/tests/regressiontests/dates/models.py @@ -1,22 +1,25 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): title = models.CharField(max_length=100) pub_date = models.DateField() categories = models.ManyToManyField("Category", related_name="articles") - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Comment(models.Model): article = models.ForeignKey(Article, related_name="comments") text = models.TextField() pub_date = models.DateField() approval_date = models.DateField(null=True) - def __unicode__(self): + def __str__(self): return 'Comment to %s (%s)' % (self.article.title, self.pub_date) class Category(models.Model): diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py index 2852572d32..bdf3c867c6 100644 --- a/tests/regressiontests/defaultfilters/tests.py +++ b/tests/regressiontests/defaultfilters/tests.py @@ -9,6 +9,7 @@ from django.test import TestCase from django.utils import six from django.utils import unittest, translation from django.utils.safestring import SafeData +from django.utils.encoding import python_2_unicode_compatible class DefaultFiltersTests(TestCase): @@ -456,10 +457,11 @@ class DefaultFiltersTests(TestCase): 'Lawrence\n\t\t\t
  • Topeka
  • \n\t\t\n\t\t'\ '\n\t\t
  • Illinois
  • \n\t\n\t') + @python_2_unicode_compatible class ULItem(object): def __init__(self, title): self.title = title - def __unicode__(self): + def __str__(self): return 'ulitem-%s' % str(self.title) a = ULItem('a') diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py index bd4f845f27..d02adccde9 100644 --- a/tests/regressiontests/defer_regress/models.py +++ b/tests/regressiontests/defer_regress/models.py @@ -3,15 +3,17 @@ Regression tests for defer() / only() behavior. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=15) text = models.TextField(default="xyzzy") value = models.IntegerField() other_value = models.IntegerField(default=0) - def __unicode__(self): + def __str__(self): return self.name class RelatedItem(models.Model): @@ -21,13 +23,14 @@ class Child(models.Model): name = models.CharField(max_length=10) value = models.IntegerField() +@python_2_unicode_compatible class Leaf(models.Model): name = models.CharField(max_length=10) child = models.ForeignKey(Child) second_child = models.ForeignKey(Child, related_name="other", null=True) value = models.IntegerField(default=42) - def __unicode__(self): + def __str__(self): return self.name class ResolveThis(models.Model): @@ -38,11 +41,12 @@ class Proxy(Item): class Meta: proxy = True +@python_2_unicode_compatible class SimpleItem(models.Model): name = models.CharField(max_length=15) value = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name class Feature(models.Model): diff --git a/tests/regressiontests/expressions_regress/models.py b/tests/regressiontests/expressions_regress/models.py index f3b6999377..711329031d 100644 --- a/tests/regressiontests/expressions_regress/models.py +++ b/tests/regressiontests/expressions_regress/models.py @@ -1,15 +1,17 @@ from __future__ import unicode_literals +from django.utils.encoding import python_2_unicode_compatible """ Model for testing arithmetic expressions. """ from django.db import models +@python_2_unicode_compatible class Number(models.Model): integer = models.IntegerField(db_column='the_integer') float = models.FloatField(null=True, db_column='the_float') - def __unicode__(self): + def __str__(self): return '%i, %.3f' % (self.integer, self.float) class Experiment(models.Model): diff --git a/tests/regressiontests/extra_regress/models.py b/tests/regressiontests/extra_regress/models.py index 7d2a6fab34..11996438b4 100644 --- a/tests/regressiontests/extra_regress/models.py +++ b/tests/regressiontests/extra_regress/models.py @@ -5,14 +5,16 @@ import datetime from django.contrib.auth.models import User from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class RevisionableModel(models.Model): base = models.ForeignKey('self', null=True) title = models.CharField(blank=True, max_length=255) when = models.DateTimeField(default=datetime.datetime.now) - def __unicode__(self): + def __str__(self): return "%s (%s, %s)" % (self.title, self.id, self.base.id) def save(self, *args, **kwargs): @@ -32,11 +34,12 @@ class Order(models.Model): created_by = models.ForeignKey(User) text = models.TextField() +@python_2_unicode_compatible class TestObject(models.Model): first = models.CharField(max_length=20) second = models.CharField(max_length=20) third = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return 'TestObject: %s,%s,%s' % (self.first,self.second,self.third) diff --git a/tests/regressiontests/fixtures_regress/models.py b/tests/regressiontests/fixtures_regress/models.py index 7151cb0ed9..1cc30d2e51 100644 --- a/tests/regressiontests/fixtures_regress/models.py +++ b/tests/regressiontests/fixtures_regress/models.py @@ -3,8 +3,10 @@ from __future__ import absolute_import, unicode_literals from django.contrib.auth.models import User from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Animal(models.Model): name = models.CharField(max_length=150) latin_name = models.CharField(max_length=150) @@ -14,7 +16,7 @@ class Animal(models.Model): # use a non-default name for the default manager specimens = models.Manager() - def __unicode__(self): + def __str__(self): return self.name @@ -25,11 +27,12 @@ class Plant(models.Model): # For testing when upper case letter in app name; regression for #4057 db_table = "Fixtures_regress_plant" +@python_2_unicode_compatible class Stuff(models.Model): name = models.CharField(max_length=20, null=True) owner = models.ForeignKey(User, null=True) - def __unicode__(self): + def __str__(self): return six.text_type(self.name) + ' is owned by ' + six.text_type(self.owner) @@ -68,13 +71,14 @@ class Article(models.Model): # Models to regression test #11428 +@python_2_unicode_compatible class Widget(models.Model): name = models.CharField(max_length=255) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name @@ -89,6 +93,7 @@ class TestManager(models.Manager): return self.get(name=key) +@python_2_unicode_compatible class Store(models.Model): objects = TestManager() name = models.CharField(max_length=255) @@ -97,13 +102,14 @@ class Store(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name def natural_key(self): return (self.name,) +@python_2_unicode_compatible class Person(models.Model): objects = TestManager() name = models.CharField(max_length=255) @@ -111,7 +117,7 @@ class Person(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name # Person doesn't actually have a dependency on store, but we need to define @@ -121,6 +127,7 @@ class Person(models.Model): natural_key.dependencies = ['fixtures_regress.store'] +@python_2_unicode_compatible class Book(models.Model): name = models.CharField(max_length=255) author = models.ForeignKey(Person) @@ -129,7 +136,7 @@ class Book(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return '%s by %s (available at %s)' % ( self.name, self.author.name, @@ -142,6 +149,7 @@ class NKManager(models.Manager): return self.get(data=data) +@python_2_unicode_compatible class NKChild(Parent): data = models.CharField(max_length=10, unique=True) objects = NKManager() @@ -149,16 +157,17 @@ class NKChild(Parent): def natural_key(self): return self.data - def __unicode__(self): + def __str__(self): return 'NKChild %s:%s' % (self.name, self.data) +@python_2_unicode_compatible class RefToNKChild(models.Model): text = models.CharField(max_length=10) nk_fk = models.ForeignKey(NKChild, related_name='ref_fks') nk_m2m = models.ManyToManyField(NKChild, related_name='ref_m2ms') - def __unicode__(self): + def __str__(self): return '%s: Reference to %s [%s]' % ( self.text, self.nk_fk, diff --git a/tests/regressiontests/forms/models.py b/tests/regressiontests/forms/models.py index 18e6ddce6d..2f3ee9fa31 100644 --- a/tests/regressiontests/forms/models.py +++ b/tests/regressiontests/forms/models.py @@ -7,6 +7,7 @@ import tempfile from django.core.files.storage import FileSystemStorage from django.db import models +from django.utils.encoding import python_2_unicode_compatible temp_storage_location = tempfile.mkdtemp(dir=os.environ['DJANGO_TEST_TEMP_DIR']) @@ -36,6 +37,7 @@ class ChoiceModel(models.Model): name = models.CharField(max_length=10) +@python_2_unicode_compatible class ChoiceOptionModel(models.Model): """Destination for ChoiceFieldModel's ForeignKey. Can't reuse ChoiceModel because error_message tests require that it have no instances.""" @@ -44,7 +46,7 @@ class ChoiceOptionModel(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return 'ChoiceOption %d' % self.pk @@ -66,10 +68,11 @@ class FileModel(models.Model): file = models.FileField(storage=temp_storage, upload_to='tests') +@python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return '%s' % self.name diff --git a/tests/regressiontests/forms/tests/error_messages.py b/tests/regressiontests/forms/tests/error_messages.py index f69b419483..b76e122bc0 100644 --- a/tests/regressiontests/forms/tests/error_messages.py +++ b/tests/regressiontests/forms/tests/error_messages.py @@ -5,6 +5,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile from django.forms import * from django.test import TestCase from django.utils.safestring import mark_safe +from django.utils.encoding import python_2_unicode_compatible class AssertFormErrorsMixin(object): @@ -213,8 +214,9 @@ class FormsErrorMessagesTestCase(TestCase, AssertFormErrorsMixin): def clean(self): raise ValidationError("I like to be awkward.") + @python_2_unicode_compatible class CustomErrorList(util.ErrorList): - def __unicode__(self): + def __str__(self): return self.as_divs() def as_divs(self): diff --git a/tests/regressiontests/forms/tests/extra.py b/tests/regressiontests/forms/tests/extra.py index e0e62858d0..2ab5d40942 100644 --- a/tests/regressiontests/forms/tests/extra.py +++ b/tests/regressiontests/forms/tests/extra.py @@ -8,8 +8,9 @@ from django.forms import * from django.forms.extras import SelectDateWidget from django.forms.util import ErrorList from django.test import TestCase +from django.utils import six from django.utils import translation -from django.utils.encoding import force_text, smart_text +from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible from .error_messages import AssertFormErrorsMixin @@ -553,14 +554,24 @@ class FormsExtraTestCase(TestCase, AssertFormErrorsMixin): def test_smart_text(self): class Test: - def __str__(self): - return 'ŠĐĆŽćžšđ'.encode('utf-8') + if six.PY3: + def __str__(self): + return 'ŠĐĆŽćžšđ' + else: + def __str__(self): + return 'ŠĐĆŽćžšđ'.encode('utf-8') class TestU: - def __str__(self): - return 'Foo' - def __unicode__(self): - return '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' + if six.PY3: + def __str__(self): + return 'ŠĐĆŽćžšđ' + def __bytes__(self): + return b'Foo' + else: + def __str__(self): + return b'Foo' + def __unicode__(self): + return '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' self.assertEqual(smart_text(Test()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') self.assertEqual(smart_text(TestU()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') @@ -585,8 +596,9 @@ class FormsExtraTestCase(TestCase, AssertFormErrorsMixin): self.assertEqual(f.cleaned_data['username'], 'sirrobin') def test_overriding_errorlist(self): + @python_2_unicode_compatible class DivErrorList(ErrorList): - def __unicode__(self): + def __str__(self): return self.as_divs() def as_divs(self): diff --git a/tests/regressiontests/forms/tests/util.py b/tests/regressiontests/forms/tests/util.py index b7cc4ec809..c2d213ae23 100644 --- a/tests/regressiontests/forms/tests/util.py +++ b/tests/regressiontests/forms/tests/util.py @@ -7,6 +7,7 @@ from django.test import TestCase from django.utils.safestring import mark_safe from django.utils import six from django.utils.translation import ugettext_lazy +from django.utils.encoding import python_2_unicode_compatible class FormsUtilTestCase(TestCase): @@ -46,8 +47,9 @@ class FormsUtilTestCase(TestCase): self.assertHTMLEqual(str(ErrorList(ValidationError(["First error.", "Not \u03C0.", ugettext_lazy("Error.")]).messages)), '
    • First error.
    • Not π.
    • Error.
    ') + @python_2_unicode_compatible class VeryBadError: - def __unicode__(self): return "A very bad error." + def __str__(self): return "A very bad error." # Can take a non-string. self.assertHTMLEqual(str(ErrorList(ValidationError(VeryBadError()).messages)), diff --git a/tests/regressiontests/forms/tests/widgets.py b/tests/regressiontests/forms/tests/widgets.py index 3ea42cf549..c950aef719 100644 --- a/tests/regressiontests/forms/tests/widgets.py +++ b/tests/regressiontests/forms/tests/widgets.py @@ -13,6 +13,7 @@ from django.utils.safestring import mark_safe from django.utils import six from django.utils.translation import activate, deactivate from django.test import TestCase +from django.utils.encoding import python_2_unicode_compatible class FormsWidgetTestCase(TestCase): @@ -1096,6 +1097,7 @@ class WidgetTests(TestCase): self.assertFalse(form.is_valid()) +@python_2_unicode_compatible class FakeFieldFile(object): """ Quacks like a FieldFile (has a .url and unicode representation), but @@ -1104,7 +1106,7 @@ class FakeFieldFile(object): """ url = 'something' - def __unicode__(self): + def __str__(self): return self.url class ClearableFileInputTests(TestCase): @@ -1125,10 +1127,11 @@ class ClearableFileInputTests(TestCase): rendering HTML. Refs #15182. """ + @python_2_unicode_compatible class StrangeFieldFile(object): url = "something?chapter=1§=2©=3&lang=en" - def __unicode__(self): + def __str__(self): return '''something
    .jpg''' widget = ClearableFileInput() diff --git a/tests/regressiontests/generic_inline_admin/models.py b/tests/regressiontests/generic_inline_admin/models.py index a1555666b3..dedc351075 100644 --- a/tests/regressiontests/generic_inline_admin/models.py +++ b/tests/regressiontests/generic_inline_admin/models.py @@ -1,6 +1,7 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Episode(models.Model): @@ -9,6 +10,7 @@ class Episode(models.Model): author = models.CharField(max_length=100, blank=True) +@python_2_unicode_compatible class Media(models.Model): """ Media that can associated to any object. @@ -20,7 +22,7 @@ class Media(models.Model): description = models.CharField(max_length=100, blank=True) keywords = models.CharField(max_length=100, blank=True) - def __unicode__(self): + def __str__(self): return self.url # diff --git a/tests/regressiontests/generic_relations_regress/models.py b/tests/regressiontests/generic_relations_regress/models.py index 7ec752b02b..dacc9b380b 100644 --- a/tests/regressiontests/generic_relations_regress/models.py +++ b/tests/regressiontests/generic_relations_regress/models.py @@ -1,31 +1,36 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible __all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address', 'CharLink', 'TextLink', 'OddRelation1', 'OddRelation2', 'Contact', 'Organization', 'Note') +@python_2_unicode_compatible class Link(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey() - def __unicode__(self): + def __str__(self): return "Link to %s id=%s" % (self.content_type, self.object_id) +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=100) links = generic.GenericRelation(Link) - def __unicode__(self): + def __str__(self): return "Place: %s" % self.name +@python_2_unicode_compatible class Restaurant(Place): - def __unicode__(self): + def __str__(self): return "Restaurant: %s" % self.name +@python_2_unicode_compatible class Address(models.Model): street = models.CharField(max_length=80) city = models.CharField(max_length=50) @@ -35,15 +40,16 @@ class Address(models.Model): object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey() - def __unicode__(self): + def __str__(self): return '%s %s, %s %s' % (self.street, self.city, self.state, self.zipcode) +@python_2_unicode_compatible class Person(models.Model): account = models.IntegerField(primary_key=True) name = models.CharField(max_length=128) addresses = generic.GenericRelation(Address) - def __unicode__(self): + def __str__(self): return self.name class CharLink(models.Model): diff --git a/tests/regressiontests/generic_views/models.py b/tests/regressiontests/generic_views/models.py index 2355769d58..f59389ef78 100644 --- a/tests/regressiontests/generic_views/models.py +++ b/tests/regressiontests/generic_views/models.py @@ -1,6 +1,8 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Artist(models.Model): name = models.CharField(max_length=100) @@ -9,13 +11,14 @@ class Artist(models.Model): verbose_name = 'professional artist' verbose_name_plural = 'professional artists' - def __unicode__(self): + def __str__(self): return self.name @models.permalink def get_absolute_url(self): return ('artist_detail', (), {'pk': self.id}) +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) slug = models.SlugField() @@ -23,9 +26,10 @@ class Author(models.Model): class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Book(models.Model): name = models.CharField(max_length=300) slug = models.SlugField() @@ -36,7 +40,7 @@ class Book(models.Model): class Meta: ordering = ['-pubdate'] - def __unicode__(self): + def __str__(self): return self.name class Page(models.Model): diff --git a/tests/regressiontests/inline_formsets/models.py b/tests/regressiontests/inline_formsets/models.py index d76eea758b..40c85fe739 100644 --- a/tests/regressiontests/inline_formsets/models.py +++ b/tests/regressiontests/inline_formsets/models.py @@ -1,5 +1,6 @@ # coding: utf-8 from django.db import models +from django.utils.encoding import python_2_unicode_compatible class School(models.Model): @@ -14,15 +15,17 @@ class Child(models.Model): school = models.ForeignKey(School) name = models.CharField(max_length=100) +@python_2_unicode_compatible class Poet(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Poem(models.Model): poet = models.ForeignKey(Poet) name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/introspection/models.py b/tests/regressiontests/introspection/models.py index 3ca80c5aab..6e5beba61d 100644 --- a/tests/regressiontests/introspection/models.py +++ b/tests/regressiontests/introspection/models.py @@ -1,8 +1,10 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) @@ -12,15 +14,16 @@ class Reporter(models.Model): class Meta: unique_together = ('first_name', 'last_name') - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() reporter = models.ForeignKey(Reporter) - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/regressiontests/m2m_regress/models.py b/tests/regressiontests/m2m_regress/models.py index be8dcae9c1..7c1108456e 100644 --- a/tests/regressiontests/m2m_regress/models.py +++ b/tests/regressiontests/m2m_regress/models.py @@ -1,37 +1,42 @@ from django.contrib.auth import models as auth from django.db import models +from django.utils.encoding import python_2_unicode_compatible # No related name is needed here, since symmetrical relations are not # explicitly reversible. +@python_2_unicode_compatible class SelfRefer(models.Model): name = models.CharField(max_length=10) references = models.ManyToManyField('self') related = models.ManyToManyField('self') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return self.name # Regression for #11956 -- a many to many to the base class +@python_2_unicode_compatible class TagCollection(Tag): tags = models.ManyToManyField(Tag, related_name='tag_collections') - def __unicode__(self): + def __str__(self): return self.name # A related_name is required on one of the ManyToManyField entries here because # they are both addressable as reverse relations from Tag. +@python_2_unicode_compatible class Entry(models.Model): name = models.CharField(max_length=10) topics = models.ManyToManyField(Tag) related = models.ManyToManyField(Tag, related_name="similar") - def __unicode__(self): + def __str__(self): return self.name # Two models both inheriting from a base model with a self-referential m2m field diff --git a/tests/regressiontests/m2m_through_regress/models.py b/tests/regressiontests/m2m_through_regress/models.py index 1db3de1a4c..1995b78984 100644 --- a/tests/regressiontests/m2m_through_regress/models.py +++ b/tests/regressiontests/m2m_through_regress/models.py @@ -2,40 +2,45 @@ from __future__ import unicode_literals from django.contrib.auth.models import User from django.db import models +from django.utils.encoding import python_2_unicode_compatible # Forward declared intermediate model +@python_2_unicode_compatible class Membership(models.Model): person = models.ForeignKey('Person') group = models.ForeignKey('Group') price = models.IntegerField(default=100) - def __unicode__(self): + def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) # using custom id column to test ticket #11107 +@python_2_unicode_compatible class UserMembership(models.Model): id = models.AutoField(db_column='usermembership_id', primary_key=True) user = models.ForeignKey(User) group = models.ForeignKey('Group') price = models.IntegerField(default=100) - def __unicode__(self): + def __str__(self): return "%s is a user and member of %s" % (self.user.username, self.group.name) +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=128) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=128) # Membership object defined as a class members = models.ManyToManyField(Person, through=Membership) user_members = models.ManyToManyField(User, through='UserMembership') - def __unicode__(self): + def __str__(self): return self.name # A set of models that use an non-abstract inherited model as the 'through' model. diff --git a/tests/regressiontests/managers_regress/models.py b/tests/regressiontests/managers_regress/models.py index fb6c530722..892505f24a 100644 --- a/tests/regressiontests/managers_regress/models.py +++ b/tests/regressiontests/managers_regress/models.py @@ -3,6 +3,7 @@ Various edge-cases for model managers. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class OnlyFred(models.Manager): @@ -44,35 +45,40 @@ class AbstractBase3(models.Model): class Meta: abstract = True +@python_2_unicode_compatible class Parent(models.Model): name = models.CharField(max_length=50) manager = OnlyFred() - def __unicode__(self): + def __str__(self): return self.name # Managers from base classes are inherited and, if no manager is specified # *and* the parent has a manager specified, the first one (in the MRO) will # become the default. +@python_2_unicode_compatible class Child1(AbstractBase1): data = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.data +@python_2_unicode_compatible class Child2(AbstractBase1, AbstractBase2): data = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.data +@python_2_unicode_compatible class Child3(AbstractBase1, AbstractBase3): data = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.data +@python_2_unicode_compatible class Child4(AbstractBase1): data = models.CharField(max_length=25) @@ -80,16 +86,17 @@ class Child4(AbstractBase1): # inherited. default = models.Manager() - def __unicode__(self): + def __str__(self): return self.data +@python_2_unicode_compatible class Child5(AbstractBase3): name = models.CharField(max_length=25) default = OnlyFred() objects = models.Manager() - def __unicode__(self): + def __str__(self): return self.name # Will inherit managers from AbstractBase1, but not Child4. diff --git a/tests/regressiontests/many_to_one_regress/models.py b/tests/regressiontests/many_to_one_regress/models.py index 1e59c4c8c8..f3727820f8 100644 --- a/tests/regressiontests/many_to_one_regress/models.py +++ b/tests/regressiontests/many_to_one_regress/models.py @@ -4,6 +4,7 @@ Regression tests for a few ForeignKey bugs. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible # If ticket #1578 ever slips back in, these models will not be able to be # created (the field names being lower-cased versions of their opposite @@ -30,18 +31,20 @@ class Child(models.Model): # Multiple paths to the same model (#7110, #7125) +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return self.name class Record(models.Model): category = models.ForeignKey(Category) +@python_2_unicode_compatible class Relation(models.Model): left = models.ForeignKey(Record, related_name='left_set') right = models.ForeignKey(Record, related_name='right_set') - def __unicode__(self): + def __str__(self): return "%s - %s" % (self.left.category.name, self.right.category.name) diff --git a/tests/regressiontests/model_forms_regress/models.py b/tests/regressiontests/model_forms_regress/models.py index 9259e260f0..f6e08d24dc 100644 --- a/tests/regressiontests/model_forms_regress/models.py +++ b/tests/regressiontests/model_forms_regress/models.py @@ -4,6 +4,7 @@ import os from django.core.exceptions import ValidationError from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Person(models.Model): @@ -20,18 +21,20 @@ class Triple(models.Model): class FilePathModel(models.Model): path = models.FilePathField(path=os.path.dirname(__file__), match=".*\.py$", blank=True) +@python_2_unicode_compatible class Publication(models.Model): title = models.CharField(max_length=30) date_published = models.DateField() - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) publications = models.ManyToManyField(Publication) - def __unicode__(self): + def __str__(self): return self.headline class CustomFileField(models.FileField): diff --git a/tests/regressiontests/model_formsets_regress/models.py b/tests/regressiontests/model_formsets_regress/models.py index 189ed8072e..f94ad51929 100644 --- a/tests/regressiontests/model_formsets_regress/models.py +++ b/tests/regressiontests/model_formsets_regress/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class User(models.Model): @@ -22,9 +23,10 @@ class Manager(models.Model): class Network(models.Model): name = models.CharField(max_length=15) +@python_2_unicode_compatible class Host(models.Model): network = models.ForeignKey(Network) hostname = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.hostname diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py index 5481298963..811c8175bb 100644 --- a/tests/regressiontests/model_inheritance_regress/models.py +++ b/tests/regressiontests/model_inheritance_regress/models.py @@ -3,7 +3,9 @@ from __future__ import unicode_literals import datetime from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) @@ -11,28 +13,31 @@ class Place(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name +@python_2_unicode_compatible class Restaurant(Place): serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.name +@python_2_unicode_compatible class ItalianRestaurant(Restaurant): serves_gnocchi = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the italian restaurant" % self.name +@python_2_unicode_compatible class ParkingLot(Place): # An explicit link to the parent (we can control the attribute name). parent = models.OneToOneField(Place, primary_key=True, parent_link=True) capacity = models.IntegerField() - def __unicode__(self): + def __str__(self): return "%s the parking lot" % self.name class ParkingLot2(Place): @@ -64,13 +69,14 @@ class SelfRefParent(models.Model): class SelfRefChild(SelfRefParent): child_data = models.IntegerField() +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() class Meta: ordering = ('-pub_date', 'headline') - def __unicode__(self): + def __str__(self): return self.headline class ArticleWithAuthor(Article): @@ -91,17 +97,19 @@ class Evaluation(Article): class QualityControl(Evaluation): assignee = models.CharField(max_length=50) +@python_2_unicode_compatible class BaseM(models.Model): base_name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.base_name +@python_2_unicode_compatible class DerivedM(BaseM): customPK = models.IntegerField(primary_key=True) derived_name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return "PK = %d, base_name = %s, derived_name = %s" \ % (self.customPK, self.base_name, self.derived_name) @@ -120,15 +128,17 @@ class InternalCertificationAudit(CertificationAudit): auditing_dept = models.CharField(max_length=20) # Check that abstract classes don't get m2m tables autocreated. +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=100) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class AbstractEvent(models.Model): name = models.CharField(max_length=100) attendees = models.ManyToManyField(Person, related_name="%(class)s_set") @@ -137,7 +147,7 @@ class AbstractEvent(models.Model): abstract = True ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name class BirthdayParty(AbstractEvent): diff --git a/tests/regressiontests/model_inheritance_select_related/models.py b/tests/regressiontests/model_inheritance_select_related/models.py index f810531bff..6b28772620 100644 --- a/tests/regressiontests/model_inheritance_select_related/models.py +++ b/tests/regressiontests/model_inheritance_select_related/models.py @@ -5,27 +5,31 @@ select_related(). from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name +@python_2_unicode_compatible class Restaurant(Place): serves_sushi = models.BooleanField() serves_steak = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.name +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=50) favorite_restaurant = models.ForeignKey(Restaurant) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py index aca6d44837..6c350d1b5d 100644 --- a/tests/regressiontests/model_regress/models.py +++ b/tests/regressiontests/model_regress/models.py @@ -1,5 +1,6 @@ # coding: utf-8 from django.db import models +from django.utils.encoding import python_2_unicode_compatible CHOICES = ( @@ -8,6 +9,7 @@ CHOICES = ( ) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() @@ -20,7 +22,7 @@ class Article(models.Model): # A utf-8 verbose name (Ångström's Articles) to test they are valid. verbose_name = "\xc3\x85ngstr\xc3\xb6m's Articles" - def __unicode__(self): + def __str__(self): return self.headline @@ -38,26 +40,29 @@ class Event(models.Model): when = models.DateTimeField() +@python_2_unicode_compatible class Department(models.Model): id = models.PositiveIntegerField(primary_key=True) name = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Worker(models.Model): department = models.ForeignKey(Department) name = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class BrokenUnicodeMethod(models.Model): name = models.CharField(max_length=7) - def __unicode__(self): + def __str__(self): # Intentionally broken (trying to insert a unicode value into a str # object). return 'Názov: %s' % self.name diff --git a/tests/regressiontests/modeladmin/models.py b/tests/regressiontests/modeladmin/models.py index 33202fad8f..fdbcabd187 100644 --- a/tests/regressiontests/modeladmin/models.py +++ b/tests/regressiontests/modeladmin/models.py @@ -1,8 +1,10 @@ # coding: utf-8 from django.contrib.auth.models import User from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Band(models.Model): name = models.CharField(max_length=100) bio = models.TextField() @@ -11,7 +13,7 @@ class Band(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name class Concert(models.Model): diff --git a/tests/regressiontests/multiple_database/models.py b/tests/regressiontests/multiple_database/models.py index 7d655fe3d6..e46438649b 100644 --- a/tests/regressiontests/multiple_database/models.py +++ b/tests/regressiontests/multiple_database/models.py @@ -4,15 +4,17 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Review(models.Model): source = models.CharField(max_length=100) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey() - def __unicode__(self): + def __str__(self): return self.source class Meta: @@ -22,11 +24,12 @@ class PersonManager(models.Manager): def get_by_natural_key(self, name): return self.get(name=name) +@python_2_unicode_compatible class Person(models.Model): objects = PersonManager() name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name class Meta: @@ -45,6 +48,7 @@ class BookManager(models.Manager): kwargs.pop('extra_arg', None) return super(BookManager, self).get_or_create(*args, **kwargs) +@python_2_unicode_compatible class Book(models.Model): objects = BookManager() title = models.CharField(max_length=100) @@ -54,17 +58,18 @@ class Book(models.Model): reviews = generic.GenericRelation(Review) pages = models.IntegerField(default=100) - def __unicode__(self): + def __str__(self): return self.title class Meta: ordering = ('title',) +@python_2_unicode_compatible class Pet(models.Model): name = models.CharField(max_length=100) owner = models.ForeignKey(Person) - def __unicode__(self): + def __str__(self): return self.name class Meta: diff --git a/tests/regressiontests/null_fk/models.py b/tests/regressiontests/null_fk/models.py index e32ff542a0..c86ee8a5a9 100644 --- a/tests/regressiontests/null_fk/models.py +++ b/tests/regressiontests/null_fk/models.py @@ -3,6 +3,7 @@ Regression tests for proper working of ForeignKey(null=True). """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class SystemDetails(models.Model): @@ -16,13 +17,15 @@ class Forum(models.Model): system_info = models.ForeignKey(SystemInfo) forum_name = models.CharField(max_length=32) +@python_2_unicode_compatible class Post(models.Model): forum = models.ForeignKey(Forum, null=True) title = models.CharField(max_length=32) - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Comment(models.Model): post = models.ForeignKey(Post, null=True) comment_text = models.CharField(max_length=250) @@ -30,7 +33,7 @@ class Comment(models.Model): class Meta: ordering = ('comment_text',) - def __unicode__(self): + def __str__(self): return self.comment_text # Ticket 15823 diff --git a/tests/regressiontests/null_fk_ordering/models.py b/tests/regressiontests/null_fk_ordering/models.py index e4a19f1512..3caff0d594 100644 --- a/tests/regressiontests/null_fk_ordering/models.py +++ b/tests/regressiontests/null_fk_ordering/models.py @@ -8,17 +8,19 @@ xpected results from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible # The first two models represent a very simple null FK ordering case. class Author(models.Model): name = models.CharField(max_length=150) +@python_2_unicode_compatible class Article(models.Model): title = models.CharField(max_length=150) author = models.ForeignKey(Author, null=True) - def __unicode__(self): + def __str__(self): return 'Article titled: %s' % (self.title, ) class Meta: @@ -33,13 +35,15 @@ class Forum(models.Model): system_info = models.ForeignKey(SystemInfo) forum_name = models.CharField(max_length=32) +@python_2_unicode_compatible class Post(models.Model): forum = models.ForeignKey(Forum, null=True) title = models.CharField(max_length=32) - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Comment(models.Model): post = models.ForeignKey(Post, null=True) comment_text = models.CharField(max_length=250) @@ -47,5 +51,5 @@ class Comment(models.Model): class Meta: ordering = ['post__forum__system_info__system_name', 'comment_text'] - def __unicode__(self): + def __str__(self): return self.comment_text diff --git a/tests/regressiontests/null_queries/models.py b/tests/regressiontests/null_queries/models.py index 886bd75276..25560fbab7 100644 --- a/tests/regressiontests/null_queries/models.py +++ b/tests/regressiontests/null_queries/models.py @@ -1,19 +1,22 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Poll(models.Model): question = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return "Q: %s " % self.question +@python_2_unicode_compatible class Choice(models.Model): poll = models.ForeignKey(Poll) choice = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return "Choice: %s in poll %s" % (self.choice, self.poll) # A set of models with an inner one pointing to two outer ones. diff --git a/tests/regressiontests/one_to_one_regress/models.py b/tests/regressiontests/one_to_one_regress/models.py index 5d32bf03f8..38b801f40e 100644 --- a/tests/regressiontests/one_to_one_regress/models.py +++ b/tests/regressiontests/one_to_one_regress/models.py @@ -1,39 +1,44 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name +@python_2_unicode_compatible class Restaurant(models.Model): place = models.OneToOneField(Place) serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.place.name +@python_2_unicode_compatible class Bar(models.Model): place = models.OneToOneField(Place) serves_cocktails = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the bar" % self.place.name class UndergroundBar(models.Model): place = models.OneToOneField(Place, null=True) serves_cocktails = models.BooleanField() +@python_2_unicode_compatible class Favorites(models.Model): name = models.CharField(max_length = 50) restaurants = models.ManyToManyField(Restaurant) - def __unicode__(self): + def __str__(self): return "Favorites for %s" % self.name class Target(models.Model): diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 6328776e91..45a48ee77c 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -7,6 +7,7 @@ import threading from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class DumbCategory(models.Model): @@ -19,6 +20,7 @@ class ProxyCategory(DumbCategory): class NamedCategory(DumbCategory): name = models.CharField(max_length=10) +@python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=10) parent = models.ForeignKey('self', blank=True, null=True, @@ -28,9 +30,10 @@ class Tag(models.Model): class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Note(models.Model): note = models.CharField(max_length=100) misc = models.CharField(max_length=10) @@ -38,7 +41,7 @@ class Note(models.Model): class Meta: ordering = ['note'] - def __unicode__(self): + def __str__(self): return self.note def __init__(self, *args, **kwargs): @@ -48,14 +51,16 @@ class Note(models.Model): # that use objects of that type as an argument. self.lock = threading.Lock() +@python_2_unicode_compatible class Annotation(models.Model): name = models.CharField(max_length=10) tag = models.ForeignKey(Tag) notes = models.ManyToManyField(Note) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class ExtraInfo(models.Model): info = models.CharField(max_length=100) note = models.ForeignKey(Note) @@ -63,9 +68,10 @@ class ExtraInfo(models.Model): class Meta: ordering = ['info'] - def __unicode__(self): + def __str__(self): return self.info +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=10) num = models.IntegerField(unique=True) @@ -74,9 +80,10 @@ class Author(models.Model): class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=10) created = models.DateTimeField() @@ -88,16 +95,18 @@ class Item(models.Model): class Meta: ordering = ['-note', 'name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Report(models.Model): name = models.CharField(max_length=10) creator = models.ForeignKey(Author, to_field='num', null=True) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Ranking(models.Model): rank = models.IntegerField() author = models.ForeignKey(Author) @@ -106,9 +115,10 @@ class Ranking(models.Model): # A complex ordering specification. Should stress the system a bit. ordering = ('author__extra__note', 'author__name', 'rank') - def __unicode__(self): + def __str__(self): return '%d: %s' % (self.rank, self.author.name) +@python_2_unicode_compatible class Cover(models.Model): title = models.CharField(max_length=50) item = models.ForeignKey(Item) @@ -116,13 +126,14 @@ class Cover(models.Model): class Meta: ordering = ['item'] - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Number(models.Model): num = models.IntegerField() - def __unicode__(self): + def __str__(self): return six.text_type(self.num) # Symmetrical m2m field with a normal field using the reverse accesor name @@ -168,6 +179,7 @@ class CustomManager(models.Manager): qs = super(CustomManager, self).get_query_set() return qs.filter(public=True, tag__name='t1') +@python_2_unicode_compatible class ManagedModel(models.Model): data = models.CharField(max_length=10) tag = models.ForeignKey(Tag) @@ -176,7 +188,7 @@ class ManagedModel(models.Model): objects = CustomManager() normal_manager = models.Manager() - def __unicode__(self): + def __str__(self): return self.data # An inter-related setup with multiple paths from Child to Detail. @@ -211,11 +223,12 @@ class Related(models.Model): # An inter-related setup with a model subclass that has a nullable # path to another model, and a return path from that model. +@python_2_unicode_compatible class Celebrity(models.Model): name = models.CharField("Name", max_length=20) greatest_fan = models.ForeignKey("Fan", null=True, unique=True) - def __unicode__(self): + def __str__(self): return self.name class TvChef(Celebrity): @@ -225,10 +238,11 @@ class Fan(models.Model): fan_of = models.ForeignKey(Celebrity) # Multiple foreign keys +@python_2_unicode_compatible class LeafA(models.Model): data = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return self.data class LeafB(models.Model): @@ -238,11 +252,12 @@ class Join(models.Model): a = models.ForeignKey(LeafA) b = models.ForeignKey(LeafB) +@python_2_unicode_compatible class ReservedName(models.Model): name = models.CharField(max_length=20) order = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name # A simpler shared-foreign-key setup that can expose some problems. @@ -256,13 +271,14 @@ class PointerB(models.Model): connection = models.ForeignKey(SharedConnection) # Multi-layer ordering +@python_2_unicode_compatible class SingleObject(models.Model): name = models.CharField(max_length=10) class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name class RelatedObject(models.Model): @@ -271,6 +287,7 @@ class RelatedObject(models.Model): class Meta: ordering = ['single'] +@python_2_unicode_compatible class Plaything(models.Model): name = models.CharField(max_length=10) others = models.ForeignKey(RelatedObject, null=True) @@ -278,79 +295,89 @@ class Plaything(models.Model): class Meta: ordering = ['others'] - def __unicode__(self): + def __str__(self): return self.name class Article(models.Model): name = models.CharField(max_length=20) created = models.DateTimeField() +@python_2_unicode_compatible class Food(models.Model): name = models.CharField(max_length=20, unique=True) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Eaten(models.Model): food = models.ForeignKey(Food, to_field="name") meal = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return "%s at %s" % (self.food, self.meal) +@python_2_unicode_compatible class Node(models.Model): num = models.IntegerField(unique=True) parent = models.ForeignKey("self", to_field="num", null=True) - def __unicode__(self): + def __str__(self): return "%s" % self.num # Bug #12252 +@python_2_unicode_compatible class ObjectA(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class ObjectB(models.Model): name = models.CharField(max_length=50) objecta = models.ForeignKey(ObjectA) num = models.PositiveSmallIntegerField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class ObjectC(models.Model): name = models.CharField(max_length=50) objecta = models.ForeignKey(ObjectA) objectb = models.ForeignKey(ObjectB) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class SimpleCategory(models.Model): name = models.CharField(max_length=15) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class SpecialCategory(SimpleCategory): special_name = models.CharField(max_length=15) - def __unicode__(self): + def __str__(self): return self.name + " " + self.special_name +@python_2_unicode_compatible class CategoryItem(models.Model): category = models.ForeignKey(SimpleCategory) - def __unicode__(self): + def __str__(self): return "category item: " + str(self.category) +@python_2_unicode_compatible class OneToOneCategory(models.Model): new_name = models.CharField(max_length=15) category = models.OneToOneField(SimpleCategory) - def __unicode__(self): + def __str__(self): return "one2one " + self.new_name class NullableName(models.Model): diff --git a/tests/regressiontests/select_related_onetoone/models.py b/tests/regressiontests/select_related_onetoone/models.py index 3d6da9b4c5..3284defb11 100644 --- a/tests/regressiontests/select_related_onetoone/models.py +++ b/tests/regressiontests/select_related_onetoone/models.py @@ -1,44 +1,50 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class User(models.Model): username = models.CharField(max_length=100) email = models.EmailField() - def __unicode__(self): + def __str__(self): return self.username +@python_2_unicode_compatible class UserProfile(models.Model): user = models.OneToOneField(User) city = models.CharField(max_length=100) state = models.CharField(max_length=2) - def __unicode__(self): + def __str__(self): return "%s, %s" % (self.city, self.state) +@python_2_unicode_compatible class UserStatResult(models.Model): results = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return 'UserStatResults, results = %s' % (self.results,) +@python_2_unicode_compatible class UserStat(models.Model): user = models.OneToOneField(User, primary_key=True) posts = models.IntegerField() results = models.ForeignKey(UserStatResult) - def __unicode__(self): + def __str__(self): return 'UserStat, posts = %s' % (self.posts,) +@python_2_unicode_compatible class StatDetails(models.Model): base_stats = models.OneToOneField(UserStat) comments = models.IntegerField() - def __unicode__(self): + def __str__(self): return 'StatDetails, comments = %s' % (self.comments,) diff --git a/tests/regressiontests/select_related_regress/models.py b/tests/regressiontests/select_related_regress/models.py index 1af9ff4bca..a291a547f7 100644 --- a/tests/regressiontests/select_related_regress/models.py +++ b/tests/regressiontests/select_related_regress/models.py @@ -1,34 +1,39 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Building(models.Model): name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return "Building: %s" % self.name +@python_2_unicode_compatible class Device(models.Model): building = models.ForeignKey('Building') name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return "device '%s' in building %s" % (self.name, self.building) +@python_2_unicode_compatible class Port(models.Model): device = models.ForeignKey('Device') port_number = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return "%s/%s" % (self.device.name, self.port_number) +@python_2_unicode_compatible class Connection(models.Model): start = models.ForeignKey(Port, related_name='connection_start', unique=True) end = models.ForeignKey(Port, related_name='connection_end', unique=True) - def __unicode__(self): + def __str__(self): return "%s to %s" % (self.start, self.end) # Another non-tree hierarchy that exercises code paths similar to the above @@ -72,18 +77,20 @@ class SpecialClient(Client): value = models.IntegerField() # Some model inheritance exercises +@python_2_unicode_compatible class Parent(models.Model): name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return self.name class Child(Parent): value = models.IntegerField() +@python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=10) child = models.ForeignKey(Child, null=True) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/signals_regress/models.py b/tests/regressiontests/signals_regress/models.py index bf64f69e8c..829314c06c 100644 --- a/tests/regressiontests/signals_regress/models.py +++ b/tests/regressiontests/signals_regress/models.py @@ -1,15 +1,18 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Book(models.Model): name = models.CharField(max_length=20) authors = models.ManyToManyField(Author) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/sites_framework/models.py b/tests/regressiontests/sites_framework/models.py index 9ecc3e6660..55c4f4992e 100644 --- a/tests/regressiontests/sites_framework/models.py +++ b/tests/regressiontests/sites_framework/models.py @@ -1,7 +1,9 @@ from django.contrib.sites.managers import CurrentSiteManager from django.contrib.sites.models import Site from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class AbstractArticle(models.Model): title = models.CharField(max_length=50) @@ -11,7 +13,7 @@ class AbstractArticle(models.Model): class Meta: abstract = True - def __unicode__(self): + def __str__(self): return self.title class SyndicatedArticle(AbstractArticle): diff --git a/tests/regressiontests/string_lookup/models.py b/tests/regressiontests/string_lookup/models.py index 0704890cd7..a2d64cd0b2 100644 --- a/tests/regressiontests/string_lookup/models.py +++ b/tests/regressiontests/string_lookup/models.py @@ -2,47 +2,54 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Foo(models.Model): name = models.CharField(max_length=50) friend = models.CharField(max_length=50, blank=True) - def __unicode__(self): + def __str__(self): return "Foo %s" % self.name +@python_2_unicode_compatible class Bar(models.Model): name = models.CharField(max_length=50) normal = models.ForeignKey(Foo, related_name='normal_foo') fwd = models.ForeignKey("Whiz") back = models.ForeignKey("Foo") - def __unicode__(self): + def __str__(self): return "Bar %s" % self.place.name +@python_2_unicode_compatible class Whiz(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "Whiz %s" % self.name +@python_2_unicode_compatible class Child(models.Model): parent = models.OneToOneField('Base') name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "Child %s" % self.name +@python_2_unicode_compatible class Base(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "Base %s" % self.name +@python_2_unicode_compatible class Article(models.Model): name = models.CharField(max_length=50) text = models.TextField() submitted_from = models.IPAddressField(blank=True, null=True) - def __unicode__(self): + def __str__(self): return "Article %s" % self.name diff --git a/tests/regressiontests/syndication/models.py b/tests/regressiontests/syndication/models.py index a2c504e57f..10b3fe3a0c 100644 --- a/tests/regressiontests/syndication/models.py +++ b/tests/regressiontests/syndication/models.py @@ -1,6 +1,8 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Entry(models.Model): title = models.CharField(max_length=200) date = models.DateTimeField() @@ -8,17 +10,18 @@ class Entry(models.Model): class Meta: ordering = ('date',) - def __unicode__(self): + def __str__(self): return self.title def get_absolute_url(self): return "/blog/%s/" % self.pk +@python_2_unicode_compatible class Article(models.Model): title = models.CharField(max_length=200) entry = models.ForeignKey(Entry) - def __unicode__(self): + def __str__(self): return self.title diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py index fd570700af..606282ecc5 100644 --- a/tests/regressiontests/templates/filters.py +++ b/tests/regressiontests/templates/filters.py @@ -13,14 +13,17 @@ from datetime import date, datetime, timedelta from django.test.utils import str_prefix from django.utils.tzinfo import LocalTimezone, FixedOffset from django.utils.safestring import mark_safe +from django.utils.encoding import python_2_unicode_compatible # These two classes are used to test auto-escaping of __unicode__ output. +@python_2_unicode_compatible class UnsafeClass: - def __unicode__(self): + def __str__(self): return 'you & me' +@python_2_unicode_compatible class SafeClass: - def __unicode__(self): + def __str__(self): return mark_safe('you > me') # RESULT SYNTAX -- @@ -338,11 +341,11 @@ def get_filter_tests(): 'join04': (r'{% autoescape off %}{{ a|join:" & " }}{% endautoescape %}', {'a': ['alpha', 'beta & me']}, 'alpha & beta & me'), # Test that joining with unsafe joiners don't result in unsafe strings (#11377) - 'join05': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': ' & '}, 'alpha & beta & me'), - 'join06': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), - 'join07': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': ' & ' }, 'alpha & beta & me'), - 'join08': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), - + 'join05': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': ' & '}, 'alpha & beta & me'), + 'join06': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), + 'join07': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': ' & ' }, 'alpha & beta & me'), + 'join08': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), + 'date01': (r'{{ d|date:"m" }}', {'d': datetime(2008, 1, 1)}, '01'), 'date02': (r'{{ d|date }}', {'d': datetime(2008, 1, 1)}, 'Jan. 1, 2008'), #Ticket 9520: Make sure |date doesn't blow up on non-dates diff --git a/tests/regressiontests/views/models.py b/tests/regressiontests/views/models.py index 54f5c1c1f0..461f98c028 100644 --- a/tests/regressiontests/views/models.py +++ b/tests/regressiontests/views/models.py @@ -3,16 +3,19 @@ Regression tests for Django built-in views. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name def get_absolute_url(self): return '/views/authors/%s/' % self.id +@python_2_unicode_compatible class BaseArticle(models.Model): """ An abstract article Model so that we can create article models with and @@ -25,7 +28,7 @@ class BaseArticle(models.Model): class Meta: abstract = True - def __unicode__(self): + def __str__(self): return self.title class Article(BaseArticle): From 4e68e861533846010e372ecf58e3cd0439081754 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 12 Aug 2012 14:39:48 +0200 Subject: [PATCH 027/253] [py3] Deprecated StrAndUnicode. This mix-in is superseded by the @python_2_unicode_compatible decorator. --- django/utils/encoding.py | 7 +++++++ docs/releases/1.5.txt | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/django/utils/encoding.py b/django/utils/encoding.py index 7030ab1db0..633022dd3b 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -8,6 +8,7 @@ try: from urllib.parse import quote except ImportError: # Python 2 from urllib import quote +import warnings from django.utils.functional import Promise from django.utils import six @@ -32,6 +33,12 @@ class StrAndUnicode(object): Useful as a mix-in. If you support Python 2 and 3 with a single code base, you can inherit this mix-in and just define __unicode__. """ + def __init__(self, *args, **kwargs): + warnings.warn("StrAndUnicode is deprecated. Define a __str__ method " + "and apply the @python_2_unicode_compatible decorator " + "instead.", PendingDeprecationWarning, stacklevel=2) + super(StrAndUnicode, self).__init__(*args, **kwargs) + if six.PY3: def __str__(self): return self.__unicode__() diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index f789ebde40..4aeb61af94 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -273,3 +273,10 @@ our own copy of ``simplejson``. You can safely change any use of The :func:`~django.utils.itercompat.product` function has been deprecated. Use the built-in :func:`itertools.product` instead. + +``django.utils.encoding.StrAndUnicode`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The :class:`~django.utils.encoding.StrAndUnicode` mix-in has been deprecated. +Define a ``__str__`` method and apply the +:func:`~django.utils.encoding.python_2_unicode_compatible` decorator instead. From 031896c5101de83bca65e872fb4a91c15f55a42e Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 12 Aug 2012 15:22:33 +0200 Subject: [PATCH 028/253] [py3] Explained @python_2_unicode_compatible usage --- docs/topics/python3.txt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt index b09c1d2347..742731e1a7 100644 --- a/docs/topics/python3.txt +++ b/docs/topics/python3.txt @@ -36,8 +36,20 @@ In order to enable the same behavior in Python 2, every module must import my_string = "This is an unicode literal" my_bytestring = b"This is a bytestring" -If you need a byte string under Python 2 and a unicode string under Python 3, -use the :func:`str` builtin:: +In classes, define ``__str__`` methods returning unicode strings and apply the +:func:`~django.utils.encoding.python_2_unicode_compatible` decorator. It will +define appropriate ``__unicode__`` and ``__str__`` in Python 2:: + + from __future__ import unicode_literals + from django.utils.encoding import python_2_unicode_compatible + + @python_2_unicode_compatible + class MyClass(object): + def __str__(self): + return "Instance of my class" + +If you need a byte string literal under Python 2 and a unicode string literal +under Python 3, use the :func:`str` builtin:: str('my string') From 49bb72c403b24b58d8d8067dcd945f57878df9f0 Mon Sep 17 00:00:00 2001 From: Karen Tracey Date: Sun, 12 Aug 2012 12:08:58 -0400 Subject: [PATCH 029/253] [py3] Made exception examination py3-compatible. --- tests/regressiontests/templates/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index a21b7ff323..6ae5af56af 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -46,7 +46,7 @@ from .response import (TemplateResponseTest, CacheMiddlewareTest, try: from .loaders import RenderToStringTest, EggLoaderTest except ImportError as e: - if "pkg_resources" in e.message: + if "pkg_resources" in e.args[0]: pass # If setuptools isn't installed, that's fine. Just move on. else: raise From c1684e3dcb2adf0fec8fd423cc73122330c268fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Thu, 9 Aug 2012 23:36:51 +0300 Subject: [PATCH 030/253] Fixed #18731 -- Cleaned up split_exclude's use of can_reuse The outer query's set of reusable joins (can_reuse) was passed to the inner query's add_filter call. This was incorrect. --- django/db/models/sql/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index be257a5410..c62c9ac23e 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1550,7 +1550,7 @@ class Query(object): N-to-many relation field. """ query = Query(self.model) - query.add_filter(filter_expr, can_reuse=can_reuse) + query.add_filter(filter_expr) query.bump_prefix() query.clear_ordering(True) query.set_start(prefix) From dce34dc9696734f7ca02d410ddac69d714a25f1e Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sun, 12 Aug 2012 20:36:43 +0200 Subject: [PATCH 031/253] [py3] Made __repr__ return str with Python 3 --- django/contrib/databrowse/datastructures.py | 12 ++++++------ django/core/files/uploadedfile.py | 4 ++-- django/core/urlresolvers.py | 8 +++++--- django/db/models/base.py | 4 ++-- django/template/base.py | 4 ++-- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/django/contrib/databrowse/datastructures.py b/django/contrib/databrowse/datastructures.py index 13e24167f5..9d6ac34f3f 100644 --- a/django/contrib/databrowse/datastructures.py +++ b/django/contrib/databrowse/datastructures.py @@ -7,7 +7,7 @@ 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_text, smart_bytes, iri_to_uri +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 @@ -23,7 +23,7 @@ class EasyModel(object): self.verbose_name_plural = model._meta.verbose_name_plural def __repr__(self): - return '' % smart_bytes(self.model._meta.object_name) + return smart_str('' % self.model._meta.object_name) def model_databrowse(self): "Returns the ModelDatabrowse class for this model." @@ -62,7 +62,7 @@ class EasyField(object): self.model, self.field = easy_model, field def __repr__(self): - return smart_bytes('' % (self.model.model._meta.object_name, self.field.name)) + return smart_str('' % (self.model.model._meta.object_name, self.field.name)) def choices(self): for value, label in self.field.choices: @@ -80,7 +80,7 @@ class EasyChoice(object): self.value, self.label = value, label def __repr__(self): - return smart_bytes('' % (self.model.model._meta.object_name, self.field.name)) + return smart_str('' % (self.model.model._meta.object_name, self.field.name)) def url(self): 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)) @@ -91,7 +91,7 @@ class EasyInstance(object): self.model, self.instance = easy_model, instance def __repr__(self): - return smart_bytes('' % (self.model.model._meta.object_name, self.instance._get_pk_val())) + return smart_str('' % (self.model.model._meta.object_name, self.instance._get_pk_val())) def __str__(self): val = smart_text(self.instance) @@ -135,7 +135,7 @@ class EasyInstanceField(object): self.raw_value = getattr(instance.instance, field.name) def __repr__(self): - return smart_bytes('' % (self.model.model._meta.object_name, self.field.name)) + return smart_str('' % (self.model.model._meta.object_name, self.field.name)) def values(self): """ diff --git a/django/core/files/uploadedfile.py b/django/core/files/uploadedfile.py index 3a6c632975..97d53482e4 100644 --- a/django/core/files/uploadedfile.py +++ b/django/core/files/uploadedfile.py @@ -8,7 +8,7 @@ from io import BytesIO from django.conf import settings from django.core.files.base import File from django.core.files import temp as tempfile -from django.utils.encoding import smart_bytes +from django.utils.encoding import smart_str __all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile', 'SimpleUploadedFile') @@ -30,7 +30,7 @@ class UploadedFile(File): self.charset = charset def __repr__(self): - return smart_bytes("<%s: %s (%s)>" % ( + return smart_str("<%s: %s (%s)>" % ( self.__class__.__name__, self.name, self.content_type)) def _get_name(self): diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index 2fe744e8eb..1498c9989d 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -14,7 +14,7 @@ from threading import local from django.http import Http404 from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist from django.utils.datastructures import MultiValueDict -from django.utils.encoding import iri_to_uri, force_text, smart_bytes +from django.utils.encoding import iri_to_uri, force_text, smart_str from django.utils.functional import memoize, lazy from django.utils.importlib import import_module from django.utils.module_loading import module_has_submodule @@ -190,7 +190,7 @@ class RegexURLPattern(LocaleRegexProvider): self.name = name def __repr__(self): - return smart_bytes('<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern)) + return smart_str('<%s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern)) def add_prefix(self, prefix): """ @@ -240,7 +240,9 @@ class RegexURLResolver(LocaleRegexProvider): self._app_dict = {} def __repr__(self): - return smart_bytes('<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern)) + return smart_str('<%s %s (%s:%s) %s>' % ( + self.__class__.__name__, self.urlconf_name, self.app_name, + self.namespace, self.regex.pattern)) def _populate(self): lookups = MultiValueDict() diff --git a/django/db/models/base.py b/django/db/models/base.py index 3a569c805a..30e07be3a7 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -23,7 +23,7 @@ from django.db.models import signals from django.db.models.loading import register_models, get_model from django.utils.translation import ugettext_lazy as _ from django.utils.functional import curry -from django.utils.encoding import smart_bytes, force_text +from django.utils.encoding import smart_bytes, smart_str, force_text from django.utils import six from django.utils.text import get_text_list, capfirst @@ -404,7 +404,7 @@ class Model(six.with_metaclass(ModelBase, object)): u = six.text_type(self) except (UnicodeEncodeError, UnicodeDecodeError): u = '[Bad Unicode data]' - return smart_bytes('<%s: %s>' % (self.__class__.__name__, u)) + return smart_str('<%s: %s>' % (self.__class__.__name__, u)) def __str__(self): if not six.PY3 and hasattr(self, '__unicode__'): diff --git a/django/template/base.py b/django/template/base.py index 24ad9320e0..091d53421f 100644 --- a/django/template/base.py +++ b/django/template/base.py @@ -11,7 +11,7 @@ from django.utils.importlib import import_module from django.utils.itercompat import is_iterable from django.utils.text import (smart_split, unescape_string_literal, get_text_list) -from django.utils.encoding import smart_text, force_text, smart_bytes +from django.utils.encoding import smart_text, force_text, smart_str from django.utils.translation import ugettext_lazy, pgettext_lazy from django.utils.safestring import (SafeData, EscapeData, mark_safe, mark_for_escaping) @@ -848,7 +848,7 @@ class TextNode(Node): self.s = s def __repr__(self): - return "" % smart_bytes(self.s[:25], 'ascii', + return "" % smart_str(self.s[:25], 'ascii', errors='replace') def render(self, context): From 5513480fe1e616c236a7629072b4317e334b9b4b Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sun, 12 Aug 2012 20:43:01 +0200 Subject: [PATCH 032/253] [py3] Always convert values from sqlite3 to unicode strings Thanks Aymeric Augustin for the review. --- django/db/backends/sqlite3/base.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 0880079189..775ca20e45 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -56,13 +56,21 @@ def adapt_datetime_with_timezone_support(value): value = value.astimezone(timezone.utc).replace(tzinfo=None) return value.isoformat(str(" ")) -Database.register_converter(str("bool"), lambda s: str(s) == '1') -Database.register_converter(str("time"), parse_time) -Database.register_converter(str("date"), parse_date) -Database.register_converter(str("datetime"), parse_datetime_with_timezone_support) -Database.register_converter(str("timestamp"), parse_datetime_with_timezone_support) -Database.register_converter(str("TIMESTAMP"), parse_datetime_with_timezone_support) -Database.register_converter(str("decimal"), util.typecast_decimal) +def decoder(conv_func): + """ The Python sqlite3 interface returns always byte strings. + This function converts the received value to a regular string before + passing it to the receiver function. + """ + return lambda s: conv_func(s.decode('utf-8')) + +Database.register_converter(str("bool"), decoder(lambda s: s == '1')) +Database.register_converter(str("time"), decoder(parse_time)) +Database.register_converter(str("date"), decoder(parse_date)) +Database.register_converter(str("datetime"), decoder(parse_datetime_with_timezone_support)) +Database.register_converter(str("timestamp"), decoder(parse_datetime_with_timezone_support)) +Database.register_converter(str("TIMESTAMP"), decoder(parse_datetime_with_timezone_support)) +Database.register_converter(str("decimal"), decoder(util.typecast_decimal)) + Database.register_adapter(datetime.datetime, adapt_datetime_with_timezone_support) Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal) if Database.version_info >= (2, 4, 1): From 59a655988ee95e4b4e4646cebea88b620d8fcdd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Sun, 15 Jul 2012 14:41:05 +0300 Subject: [PATCH 033/253] Fixed #13844 -- Avoid converting unknown db values to float This patch removes an unconditional float(value) conversion from db backend default convert_values() method. This can cause problems when aggregating over character fields for example. In addition, Oracle and SQLite already return the bare value from their convert_values(). In the long term the converting should be done by fields, and the fields should then call database backend specific converters when needed. The current setup is inflexible for 3rd party fields. Thanks to Merlijn van Deen for the original patch. --- django/db/backends/__init__.py | 12 ++++++------ tests/regressiontests/aggregation_regress/tests.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 9606245162..9f64cfc5f0 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -878,19 +878,19 @@ class BaseDatabaseOperations(object): return self.year_lookup_bounds(value) def convert_values(self, value, field): - """Coerce the value returned by the database backend into a consistent type that - is compatible with the field type. + """ + Coerce the value returned by the database backend into a consistent type + that is compatible with the field type. """ internal_type = field.get_internal_type() if internal_type == 'DecimalField': return value - elif internal_type and internal_type.endswith('IntegerField') or internal_type == 'AutoField': + elif (internal_type and (internal_type.endswith('IntegerField') + or internal_type == 'AutoField')): return int(value) elif internal_type in ('DateField', 'DateTimeField', 'TimeField'): return value - # No field, or the field isn't known to be a decimal or integer - # Default to a float - return float(value) + return value def check_aggregate_support(self, aggregate_func): """Check that the backend supports the provided aggregate diff --git a/tests/regressiontests/aggregation_regress/tests.py b/tests/regressiontests/aggregation_regress/tests.py index e24eb43b87..b9f3ab27eb 100644 --- a/tests/regressiontests/aggregation_regress/tests.py +++ b/tests/regressiontests/aggregation_regress/tests.py @@ -866,3 +866,15 @@ class AggregationTests(TestCase): ['Peter Norvig'], lambda b: b.name ) + + def test_type_conversion(self): + # The database backend convert_values function should not try to covert + # CharFields to float. Refs #13844. + from django.db.models import CharField + from django.db import connection + testData = 'not_a_float_value' + testField = CharField() + self.assertEqual( + connection.ops.convert_values(testData, testField), + testData + ) From 99321e30cebbffeafc6ae19f4f92a0a665cbf19b Mon Sep 17 00:00:00 2001 From: Andrei Antoukh Date: Sun, 12 Aug 2012 22:17:54 +0300 Subject: [PATCH 034/253] Fixed #18306 -- Made deferred models issue update_fields on save Deferred models now automatically update only the fields which are loaded from the db (with .only() or .defer()). In addition, any field set manually after the load is updated on save. --- django/db/models/base.py | 18 ++++ docs/ref/models/instances.txt | 6 ++ docs/ref/models/querysets.txt | 16 +++ docs/releases/1.5.txt | 4 + tests/modeltests/update_only_fields/models.py | 1 + tests/modeltests/update_only_fields/tests.py | 101 ++++++++++++++++++ .../multiple_database/tests.py | 15 +++ 7 files changed, 161 insertions(+) diff --git a/django/db/models/base.py b/django/db/models/base.py index 30e07be3a7..1e1a138714 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -475,6 +475,7 @@ class Model(six.with_metaclass(ModelBase, object)): that the "save" must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set. """ + using = using or router.db_for_write(self.__class__, instance=self) if force_insert and (force_update or update_fields): raise ValueError("Cannot force both insert and updating in model saving.") @@ -502,6 +503,23 @@ class Model(six.with_metaclass(ModelBase, object)): "model or are m2m fields: %s" % ', '.join(non_model_fields)) + # If saving to the same database, and this model is deferred, then + # automatically do a "update_fields" save on the loaded fields. + elif not force_insert and self._deferred and using == self._state.db: + field_names = set() + for field in self._meta.fields: + if not field.primary_key and not hasattr(field, 'through'): + field_names.add(field.attname) + deferred_fields = [ + f.attname for f in self._meta.fields + if f.attname not in self.__dict__ + and isinstance(self.__class__.__dict__[f.attname], + DeferredAttribute)] + + loaded_fields = field_names.difference(deferred_fields) + if loaded_fields: + update_fields = frozenset(loaded_fields) + self.save_base(using=using, force_insert=force_insert, force_update=force_update, update_fields=update_fields) save.alters_data = True diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index 14541ad0d1..b7ae3890cf 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -386,6 +386,12 @@ perform an update on all fields. Specifying ``update_fields`` will force an update. +When saving a model fetched through deferred model loading +(:meth:`~Model.only()` or :meth:`~Model.defer()`) only the fields loaded from +the DB will get updated. In effect there is an automatic ``update_fields`` in +this case. If you assign or change any deferred field value, these fields will +be added to the updated fields. + Deleting objects ================ diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 8c188c67c3..b59b2ece82 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -1110,6 +1110,14 @@ one, doing so will result in an error. reader, is slightly faster and consumes a little less memory in the Python process. +.. versionchanged:: 1.5 + +.. note:: + + When calling :meth:`~Model.save()` for instances with deferred fields, + only the loaded fields will be saved. See :meth:`~Model.save()` for more + details. + only ~~~~ @@ -1154,6 +1162,14 @@ All of the cautions in the note for the :meth:`defer` documentation apply to options. Also note that using :meth:`only` and omitting a field requested using :meth:`select_related` is an error as well. +.. versionchanged:: 1.5 + +.. note:: + + When calling :meth:`~Model.save()` for instances with deferred fields, + only the loaded fields will be saved. See :meth:`~Model.save()` for more + details. + using ~~~~~ diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 4aeb61af94..2896acb36b 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -42,6 +42,10 @@ keyword argument ``update_fields``. By using this argument it is possible to save only a select list of model's fields. This can be useful for performance reasons or when trying to avoid overwriting concurrent changes. +Deferred instances (those loaded by .only() or .defer()) will automatically +save just the loaded fields. If any field is set manually after load, that +field will also get updated on save. + See the :meth:`Model.save() ` documentation for more details. diff --git a/tests/modeltests/update_only_fields/models.py b/tests/modeltests/update_only_fields/models.py index 01184f715b..bf5dd99166 100644 --- a/tests/modeltests/update_only_fields/models.py +++ b/tests/modeltests/update_only_fields/models.py @@ -15,6 +15,7 @@ class Account(models.Model): class Person(models.Model): name = models.CharField(max_length=20) gender = models.CharField(max_length=1, choices=GENDER_CHOICES) + pid = models.IntegerField(null=True, default=None) def __str__(self): return self.name diff --git a/tests/modeltests/update_only_fields/tests.py b/tests/modeltests/update_only_fields/tests.py index bce53ca621..c904c97236 100644 --- a/tests/modeltests/update_only_fields/tests.py +++ b/tests/modeltests/update_only_fields/tests.py @@ -18,6 +18,107 @@ class UpdateOnlyFieldsTests(TestCase): self.assertEqual(s.gender, 'F') self.assertEqual(s.name, 'Ian') + def test_update_fields_deferred(self): + s = Person.objects.create(name='Sara', gender='F', pid=22) + self.assertEqual(s.gender, 'F') + + s1 = Person.objects.defer("gender", "pid").get(pk=s.pk) + s1.name = "Emily" + s1.gender = "M" + + with self.assertNumQueries(1): + s1.save() + + s2 = Person.objects.get(pk=s1.pk) + self.assertEqual(s2.name, "Emily") + self.assertEqual(s2.gender, "M") + + def test_update_fields_only_1(self): + s = Person.objects.create(name='Sara', gender='F') + self.assertEqual(s.gender, 'F') + + s1 = Person.objects.only('name').get(pk=s.pk) + s1.name = "Emily" + s1.gender = "M" + + with self.assertNumQueries(1): + s1.save() + + s2 = Person.objects.get(pk=s1.pk) + self.assertEqual(s2.name, "Emily") + self.assertEqual(s2.gender, "M") + + def test_update_fields_only_2(self): + s = Person.objects.create(name='Sara', gender='F', pid=22) + self.assertEqual(s.gender, 'F') + + s1 = Person.objects.only('name').get(pk=s.pk) + s1.name = "Emily" + s1.gender = "M" + + with self.assertNumQueries(2): + s1.save(update_fields=['pid']) + + s2 = Person.objects.get(pk=s1.pk) + self.assertEqual(s2.name, "Sara") + self.assertEqual(s2.gender, "F") + + def test_update_fields_only_repeated(self): + s = Person.objects.create(name='Sara', gender='F') + self.assertEqual(s.gender, 'F') + + s1 = Person.objects.only('name').get(pk=s.pk) + s1.gender = 'M' + with self.assertNumQueries(1): + s1.save() + # Test that the deferred class does not remember that gender was + # set, instead the instace should remember this. + s1 = Person.objects.only('name').get(pk=s.pk) + with self.assertNumQueries(1): + s1.save() + + def test_update_fields_inheritance_defer(self): + profile_boss = Profile.objects.create(name='Boss', salary=3000) + e1 = Employee.objects.create(name='Sara', gender='F', + employee_num=1, profile=profile_boss) + e1 = Employee.objects.only('name').get(pk=e1.pk) + e1.name = 'Linda' + with self.assertNumQueries(1): + e1.save() + self.assertEqual(Employee.objects.get(pk=e1.pk).name, + 'Linda') + + def test_update_fields_fk_defer(self): + profile_boss = Profile.objects.create(name='Boss', salary=3000) + profile_receptionist = Profile.objects.create(name='Receptionist', salary=1000) + e1 = Employee.objects.create(name='Sara', gender='F', + employee_num=1, profile=profile_boss) + e1 = Employee.objects.only('profile').get(pk=e1.pk) + e1.profile = profile_receptionist + with self.assertNumQueries(1): + e1.save() + self.assertEqual(Employee.objects.get(pk=e1.pk).profile, profile_receptionist) + e1.profile_id = profile_boss.pk + with self.assertNumQueries(1): + e1.save() + self.assertEqual(Employee.objects.get(pk=e1.pk).profile, profile_boss) + + def test_select_related_only_interaction(self): + profile_boss = Profile.objects.create(name='Boss', salary=3000) + e1 = Employee.objects.create(name='Sara', gender='F', + employee_num=1, profile=profile_boss) + e1 = Employee.objects.only('profile__salary').select_related('profile').get(pk=e1.pk) + profile_boss.name = 'Clerk' + profile_boss.salary = 1000 + profile_boss.save() + # The loaded salary of 3000 gets saved, the name of 'Clerk' isn't + # overwritten. + with self.assertNumQueries(1): + e1.profile.save() + reloaded_profile = Profile.objects.get(pk=profile_boss.pk) + self.assertEqual(reloaded_profile.name, profile_boss.name) + self.assertEqual(reloaded_profile.salary, 3000) + def test_update_fields_m2m(self): profile_boss = Profile.objects.create(name='Boss', salary=3000) e1 = Employee.objects.create(name='Sara', gender='F', diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index 74a5f2f550..782fe2bfc6 100644 --- a/tests/regressiontests/multiple_database/tests.py +++ b/tests/regressiontests/multiple_database/tests.py @@ -1546,6 +1546,21 @@ class RouterTestCase(TestCase): # If you evaluate the query, it should work, running on 'other' self.assertEqual(list(qs.values_list('title', flat=True)), ['Dive into Python']) + def test_deferred_models(self): + mark_def = Person.objects.using('default').create(name="Mark Pilgrim") + mark_other = Person.objects.using('other').create(name="Mark Pilgrim") + orig_b = Book.objects.using('other').create(title="Dive into Python", + published=datetime.date(2009, 5, 4), + editor=mark_other) + b = Book.objects.using('other').only('title').get(pk=orig_b.pk) + self.assertEqual(b.published, datetime.date(2009, 5, 4)) + b = Book.objects.using('other').only('title').get(pk=orig_b.pk) + b.editor = mark_def + b.save(using='default') + self.assertEqual(Book.objects.using('default').get(pk=b.pk).published, + datetime.date(2009, 5, 4)) + + class AuthTestCase(TestCase): multi_db = True From ac37c9e495eb1003cc6d126ed5d686a29fb08596 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sun, 12 Aug 2012 22:22:56 +0200 Subject: [PATCH 035/253] [py3] Encoded value before feeding it to hashlib.md5 --- django/core/cache/backends/filebased.py | 3 ++- django/utils/cache.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/django/core/cache/backends/filebased.py b/django/core/cache/backends/filebased.py index 1170996a76..c54e8d280f 100644 --- a/django/core/cache/backends/filebased.py +++ b/django/core/cache/backends/filebased.py @@ -10,6 +10,7 @@ except ImportError: import pickle from django.core.cache.backends.base import BaseCache +from django.utils.encoding import smart_bytes class FileBasedCache(BaseCache): def __init__(self, dir, params): @@ -136,7 +137,7 @@ class FileBasedCache(BaseCache): Thus, a cache key of "foo" gets turnned into a file named ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``. """ - path = hashlib.md5(key).hexdigest() + path = hashlib.md5(smart_bytes(key)).hexdigest() path = os.path.join(path[:2], path[2:4], path[4:]) return os.path.join(self._dir, path) diff --git a/django/utils/cache.py b/django/utils/cache.py index 42b0de4ce6..95b0fba07b 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -23,7 +23,7 @@ import time from django.conf import settings from django.core.cache import get_cache -from django.utils.encoding import iri_to_uri, force_text +from django.utils.encoding import iri_to_uri, force_text, smart_bytes from django.utils.http import http_date from django.utils.timezone import get_current_timezone_name from django.utils.translation import get_language @@ -180,14 +180,14 @@ def _generate_cache_key(request, method, headerlist, key_prefix): value = request.META.get(header, None) if value is not None: ctx.update(value) - path = hashlib.md5(iri_to_uri(request.get_full_path())) + path = hashlib.md5(smart_bytes(iri_to_uri(request.get_full_path()))) cache_key = 'views.decorators.cache.cache_page.%s.%s.%s.%s' % ( key_prefix, method, path.hexdigest(), ctx.hexdigest()) return _i18n_cache_key_suffix(request, cache_key) def _generate_cache_header_key(key_prefix, request): """Returns a cache key for the header cache.""" - path = hashlib.md5(iri_to_uri(request.get_full_path())) + path = hashlib.md5(smart_bytes(iri_to_uri(request.get_full_path()))) cache_key = 'views.decorators.cache.cache_header.%s.%s' % ( key_prefix, path.hexdigest()) return _i18n_cache_key_suffix(request, cache_key) From 8a1f439d3aca3a021a43ba6c4235c3ac68908657 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sun, 12 Aug 2012 22:26:17 +0200 Subject: [PATCH 036/253] [py3] Fix encoding issues in contrib.sessions --- django/contrib/sessions/backends/base.py | 11 +++++++---- django/contrib/sessions/backends/db.py | 3 +-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py index 153cde9830..4515edbb17 100644 --- a/django/contrib/sessions/backends/base.py +++ b/django/contrib/sessions/backends/base.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import base64 import time from datetime import datetime, timedelta @@ -12,6 +14,7 @@ from django.utils.crypto import constant_time_compare from django.utils.crypto import get_random_string from django.utils.crypto import salted_hmac from django.utils import timezone +from django.utils.encoding import smart_bytes class CreateError(Exception): """ @@ -78,15 +81,15 @@ class SessionBase(object): "Returns the given session dictionary pickled and encoded as a string." pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL) hash = self._hash(pickled) - return base64.encodestring(hash + ":" + pickled) + return base64.encodestring(hash.encode() + b":" + pickled) def decode(self, session_data): - encoded_data = base64.decodestring(session_data) + encoded_data = base64.decodestring(smart_bytes(session_data)) try: # could produce ValueError if there is no ':' - hash, pickled = encoded_data.split(':', 1) + hash, pickled = encoded_data.split(b':', 1) expected_hash = self._hash(pickled) - if not constant_time_compare(hash, expected_hash): + if not constant_time_compare(hash.decode(), expected_hash): raise SuspiciousOperation("Session data corrupted") else: return pickle.loads(pickled) diff --git a/django/contrib/sessions/backends/db.py b/django/contrib/sessions/backends/db.py index 0cc17b44c3..babdb72c27 100644 --- a/django/contrib/sessions/backends/db.py +++ b/django/contrib/sessions/backends/db.py @@ -1,7 +1,6 @@ from django.contrib.sessions.backends.base import SessionBase, CreateError from django.core.exceptions import SuspiciousOperation from django.db import IntegrityError, transaction, router -from django.utils.encoding import force_text from django.utils import timezone @@ -18,7 +17,7 @@ class SessionStore(SessionBase): session_key = self.session_key, expire_date__gt=timezone.now() ) - return self.decode(force_text(s.session_data)) + return self.decode(s.session_data) except (Session.DoesNotExist, SuspiciousOperation): self.create() return {} From 1930b899bd69fb084856257598ab5ddd8ee4e016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Mon, 13 Aug 2012 09:15:20 +0300 Subject: [PATCH 037/253] Refix #13844 -- Made FloatField aggregates work on Python 2.6 + Postgres Fixed a regression introduced in 59a655988ee95e4b4e4646cebea88b620d8fcdd2. --- django/db/backends/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 9f64cfc5f0..77033390eb 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -885,6 +885,8 @@ class BaseDatabaseOperations(object): internal_type = field.get_internal_type() if internal_type == 'DecimalField': return value + elif internal_type == 'FloatField': + return float(value) elif (internal_type and (internal_type.endswith('IntegerField') or internal_type == 'AutoField')): return int(value) From 6d68022a274df2e19205d7313034cf6eb619f346 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sun, 12 Aug 2012 23:53:54 +0200 Subject: [PATCH 038/253] [py3] Removed filename encoding in file_uploads test --- tests/regressiontests/file_uploads/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regressiontests/file_uploads/tests.py b/tests/regressiontests/file_uploads/tests.py index d2362d7197..d3bf20be94 100644 --- a/tests/regressiontests/file_uploads/tests.py +++ b/tests/regressiontests/file_uploads/tests.py @@ -87,7 +87,7 @@ class FileUploadTests(TestCase): tdir = tempfile.gettempdir() # This file contains chinese symbols and an accented char in the name. - with open(os.path.join(tdir, UNICODE_FILENAME.encode('utf-8')), 'w+b') as file1: + with open(os.path.join(tdir, UNICODE_FILENAME), 'w+b') as file1: file1.write(b'b' * (2 ** 10)) file1.seek(0) From a06503d09b73fa2842ec79170b84453034fca904 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sun, 12 Aug 2012 23:25:42 +0200 Subject: [PATCH 039/253] [py3] Fixed content encoding in test client Thanks Andrews Medina for the initial patch. --- AUTHORS | 1 + django/test/client.py | 42 ++++++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/AUTHORS b/AUTHORS index 0ea0ca6da7..5fa957885c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -373,6 +373,7 @@ answer newbie questions, and generally made Django that much better: michael.mcewan@gmail.com Paul McLanahan Tobias McNulty + Andrews Medina Zain Memon Christian Metts michal@plovarna.cz diff --git a/django/test/client.py b/django/test/client.py index 771f7e0da5..2b61c51ce1 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import sys import os import re @@ -108,7 +110,7 @@ def encode_multipart(boundary, data): as an application/octet-stream; otherwise, str(value) will be sent. """ lines = [] - to_str = lambda s: smart_bytes(s, settings.DEFAULT_CHARSET) + to_bytes = lambda s: smart_bytes(s, settings.DEFAULT_CHARSET) # Not by any means perfect, but good enough for our purposes. is_file = lambda thing: hasattr(thing, "read") and callable(thing.read) @@ -124,37 +126,37 @@ def encode_multipart(boundary, data): if is_file(item): lines.extend(encode_file(boundary, key, item)) else: - lines.extend([ - '--' + boundary, - 'Content-Disposition: form-data; name="%s"' % to_str(key), + lines.extend([to_bytes(val) for val in [ + '--%s' % boundary, + 'Content-Disposition: form-data; name="%s"' % key, '', - to_str(item) - ]) + item + ]]) else: - lines.extend([ - '--' + boundary, - 'Content-Disposition: form-data; name="%s"' % to_str(key), + lines.extend([to_bytes(val) for val in [ + '--%s' % boundary, + 'Content-Disposition: form-data; name="%s"' % key, '', - to_str(value) - ]) + value + ]]) lines.extend([ - '--' + boundary + '--', - '', + to_bytes('--%s--' % boundary), + b'', ]) - return '\r\n'.join(lines) + return b'\r\n'.join(lines) def encode_file(boundary, key, file): - to_str = lambda s: smart_bytes(s, settings.DEFAULT_CHARSET) + to_bytes = lambda s: smart_bytes(s, settings.DEFAULT_CHARSET) content_type = mimetypes.guess_type(file.name)[0] if content_type is None: content_type = 'application/octet-stream' return [ - '--' + to_str(boundary), - 'Content-Disposition: form-data; name="%s"; filename="%s"' \ - % (to_str(key), to_str(os.path.basename(file.name))), - 'Content-Type: %s' % content_type, - '', + to_bytes('--%s' % boundary), + to_bytes('Content-Disposition: form-data; name="%s"; filename="%s"' \ + % (key, os.path.basename(file.name))), + to_bytes('Content-Type: %s' % content_type), + b'', file.read() ] From 73f0f18c8fc04a03bbfb20794aabb95944c90f63 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 13 Aug 2012 10:58:21 +0200 Subject: [PATCH 040/253] [py3] Fixed admin_scripts tests --- tests/regressiontests/admin_scripts/tests.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/regressiontests/admin_scripts/tests.py b/tests/regressiontests/admin_scripts/tests.py index 546fa7d79c..a8fc7ed503 100644 --- a/tests/regressiontests/admin_scripts/tests.py +++ b/tests/regressiontests/admin_scripts/tests.py @@ -71,6 +71,10 @@ class AdminScriptTestCase(unittest.TestCase): os.remove(full_name + 'c') except OSError: pass + # Also remove a __pycache__ directory, if it exists + cache_name = os.path.join(test_dir, '__pycache__') + if os.path.isdir(cache_name): + shutil.rmtree(cache_name) def _ext_backend_paths(self): """ @@ -110,14 +114,11 @@ class AdminScriptTestCase(unittest.TestCase): python_path.extend(ext_backend_base_dirs) os.environ[python_path_var_name] = os.pathsep.join(python_path) - # Silence the DeprecationWarning caused by having a locale directory - # in the project directory. - cmd = [sys.executable, '-Wignore:::django.utils.translation', script] - # Move to the test directory and run os.chdir(test_dir) - out, err = subprocess.Popen(cmd + args, - stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + out, err = subprocess.Popen([sys.executable, script] + args, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True).communicate() # Restore the old environment if old_django_settings_module: From 5e958b958b4b27082a393decf64ec573fa7454b2 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 13 Aug 2012 11:14:08 +0200 Subject: [PATCH 041/253] [py3] Avoided comparison with None value in formsets --- django/forms/formsets.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/django/forms/formsets.py b/django/forms/formsets.py index 258c673da8..1e8edfdb79 100644 --- a/django/forms/formsets.py +++ b/django/forms/formsets.py @@ -95,10 +95,11 @@ class BaseFormSet(object): total_forms = initial_forms + self.extra # Allow all existing related objects/inlines to be displayed, # but don't allow extra beyond max_num. - if initial_forms > self.max_num >= 0: - total_forms = initial_forms - elif total_forms > self.max_num >= 0: - total_forms = self.max_num + if self.max_num is not None: + if initial_forms > self.max_num >= 0: + total_forms = initial_forms + elif total_forms > self.max_num >= 0: + total_forms = self.max_num return total_forms def initial_form_count(self): @@ -108,7 +109,7 @@ class BaseFormSet(object): else: # Use the length of the inital data if it's there, 0 otherwise. initial_forms = self.initial and len(self.initial) or 0 - if initial_forms > self.max_num >= 0: + if self.max_num is not None and (initial_forms > self.max_num >= 0): initial_forms = self.max_num return initial_forms From d774ad752d9844cb7a28fe338d4d711c8576ee6f Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 13 Aug 2012 11:34:40 +0200 Subject: [PATCH 042/253] [py3] Made csrf context processor return Unicode --- django/core/context_processors.py | 11 +++++++---- django/middleware/csrf.py | 7 ++++--- tests/regressiontests/csrf_tests/tests.py | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/django/core/context_processors.py b/django/core/context_processors.py index a503270cf4..ca1ac68f55 100644 --- a/django/core/context_processors.py +++ b/django/core/context_processors.py @@ -6,12 +6,15 @@ and returns a dictionary to add to the context. These are referenced from the setting TEMPLATE_CONTEXT_PROCESSORS and used by RequestContext. """ +from __future__ import unicode_literals from django.conf import settings from django.middleware.csrf import get_token -from django.utils.encoding import smart_bytes +from django.utils import six +from django.utils.encoding import smart_text from django.utils.functional import lazy + def csrf(request): """ Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if @@ -23,10 +26,10 @@ def csrf(request): # In order to be able to provide debugging info in the # case of misconfiguration, we use a sentinel value # instead of returning an empty dict. - return b'NOTPROVIDED' + return 'NOTPROVIDED' else: - return smart_bytes(token) - _get_val = lazy(_get_val, str) + return smart_text(token) + _get_val = lazy(_get_val, six.text_type) return {'csrf_token': _get_val() } diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index fd8ff30303..305b20e1c4 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -4,6 +4,7 @@ Cross Site Request Forgery Middleware. This module provides a middleware that implements protection against request forgeries from other sites. """ +from __future__ import unicode_literals import hashlib import re @@ -12,6 +13,7 @@ import random from django.conf import settings from django.core.urlresolvers import get_callable from django.utils.cache import patch_vary_headers +from django.utils.encoding import force_text from django.utils.http import same_origin from django.utils.log import getLogger from django.utils.crypto import constant_time_compare, get_random_string @@ -51,11 +53,10 @@ def get_token(request): def _sanitize_token(token): - # Allow only alphanum, and ensure we return a 'str' for the sake - # of the post processing middleware. + # Allow only alphanum if len(token) > CSRF_KEY_LENGTH: return _get_new_csrf_key() - token = re.sub('[^a-zA-Z0-9]+', '', str(token.decode('ascii', 'ignore'))) + token = re.sub('[^a-zA-Z0-9]+', '', force_text(token)) if token == "": # In case the cookie has been truncated to nothing at some point. return _get_new_csrf_key() diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py index 52fd3ea1c7..c5b66a31d1 100644 --- a/tests/regressiontests/csrf_tests/tests.py +++ b/tests/regressiontests/csrf_tests/tests.py @@ -216,7 +216,7 @@ class CsrfViewMiddlewareTest(TestCase): """ req = self._get_GET_no_csrf_cookie_request() resp = token_view(req) - self.assertEqual("", resp.content) + self.assertEqual(resp.content, b'') def test_token_node_empty_csrf_cookie(self): """ From 45baaabafb6cf911afad9ec63c86753b284f7269 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 13 Aug 2012 12:09:20 +0200 Subject: [PATCH 043/253] [py3] Fixed encoding issues in cache key generation --- django/core/cache/backends/base.py | 6 +++--- django/utils/cache.py | 3 ++- tests/regressiontests/cache/tests.py | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/django/core/cache/backends/base.py b/django/core/cache/backends/base.py index d527e44d8b..06e8952bfb 100644 --- a/django/core/cache/backends/base.py +++ b/django/core/cache/backends/base.py @@ -1,9 +1,9 @@ "Base Cache class." +from __future__ import unicode_literals import warnings from django.core.exceptions import ImproperlyConfigured, DjangoRuntimeWarning -from django.utils.encoding import smart_bytes from django.utils.importlib import import_module class InvalidCacheBackendError(ImproperlyConfigured): @@ -23,7 +23,7 @@ def default_key_func(key, key_prefix, version): the `key_prefix'. KEY_FUNCTION can be used to specify an alternate function with custom key making behavior. """ - return ':'.join([key_prefix, str(version), smart_bytes(key)]) + return ':'.join([key_prefix, str(version), key]) def get_key_func(key_func): """ @@ -62,7 +62,7 @@ class BaseCache(object): except (ValueError, TypeError): self._cull_frequency = 3 - self.key_prefix = smart_bytes(params.get('KEY_PREFIX', '')) + self.key_prefix = params.get('KEY_PREFIX', '') self.version = params.get('VERSION', 1) self.key_func = get_key_func(params.get('KEY_FUNCTION', None)) diff --git a/django/utils/cache.py b/django/utils/cache.py index 95b0fba07b..edac6ec75d 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -16,6 +16,7 @@ cache keys to prevent delivery of wrong content. An example: i18n middleware would need to distinguish caches by the "Accept-language" header. """ +from __future__ import unicode_literals import hashlib import re @@ -170,7 +171,7 @@ def _i18n_cache_key_suffix(request, cache_key): # User-defined tzinfo classes may return absolutely anything. # Hence this paranoid conversion to create a valid cache key. tz_name = force_text(get_current_timezone_name(), errors='ignore') - cache_key += '.%s' % tz_name.encode('ascii', 'ignore').replace(' ', '_') + cache_key += '.%s' % tz_name.encode('ascii', 'ignore').decode('ascii').replace(' ', '_') return cache_key def _generate_cache_key(request, method, headerlist, key_prefix): diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index 8fc749aaa2..4ffca2d458 100644 --- a/tests/regressiontests/cache/tests.py +++ b/tests/regressiontests/cache/tests.py @@ -1308,7 +1308,7 @@ class CacheI18nTest(TestCase): # This is tightly coupled to the implementation, # but it's the most straightforward way to test the key. tz = force_text(timezone.get_current_timezone_name(), errors='ignore') - tz = tz.encode('ascii', 'ignore').replace(' ', '_') + tz = tz.encode('ascii', 'ignore').decode('ascii').replace(' ', '_') response = HttpResponse() key = learn_cache_key(request, response) self.assertIn(tz, key, "Cache keys should include the time zone name when time zones are active") @@ -1320,7 +1320,7 @@ class CacheI18nTest(TestCase): request = self._get_request() lang = translation.get_language() tz = force_text(timezone.get_current_timezone_name(), errors='ignore') - tz = tz.encode('ascii', 'ignore').replace(' ', '_') + tz = tz.encode('ascii', 'ignore').decode('ascii').replace(' ', '_') response = HttpResponse() key = learn_cache_key(request, response) self.assertNotIn(lang, key, "Cache keys shouldn't include the language name when i18n isn't active") From d2975718fe0585cad1ed1462cc4d65373a6c7bb0 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Mon, 13 Aug 2012 16:54:13 +0200 Subject: [PATCH 044/253] Consistenly use _ as alias for ugettext_lazy in the i18n docs. --- docs/topics/i18n/translation.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index bdbb04823d..87fcf07eac 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -317,10 +317,10 @@ Model fields and relationships ``verbose_name`` and ``help_text`` option values For example, to translate the help text of the *name* field in the following model, do the following:: - from django.utils.translation import ugettext_lazy + from django.utils.translation import ugettext_lazy as _ class MyThing(models.Model): - name = models.CharField(help_text=ugettext_lazy('This is the help text')) + name = models.CharField(help_text=_('This is the help text')) You can mark names of ``ForeignKey``, ``ManyTomanyField`` or ``OneToOneField`` relationship as translatable by using their ``verbose_name`` options:: @@ -344,14 +344,14 @@ It is recommended to always provide explicit relying on the fallback English-centric and somewhat naïve determination of verbose names Django performs bu looking at the model's class name:: - from django.utils.translation import ugettext_lazy + from django.utils.translation import ugettext_lazy as _ class MyThing(models.Model): - name = models.CharField(_('name'), help_text=ugettext_lazy('This is the help text')) + name = models.CharField(_('name'), help_text=_('This is the help text')) class Meta: - verbose_name = ugettext_lazy('my thing') - verbose_name_plural = ugettext_lazy('my things') + verbose_name = _('my thing') + verbose_name_plural = _('my things') Model methods ``short_description`` attribute values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 0dac73ebd733d9140476b66b81afa56c5b0f65b2 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 13 Aug 2012 18:22:23 +0200 Subject: [PATCH 045/253] Removed binary flag to open files for writing text content --- django/core/management/commands/makemessages.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py index d8e8f6cff7..fc0a78fb2b 100644 --- a/django/core/management/commands/makemessages.py +++ b/django/core/management/commands/makemessages.py @@ -144,7 +144,7 @@ def write_pot_file(potfile, msgs, file, work_file, is_templatized): msgs = '\n'.join(dropwhile(len, msgs.split('\n'))) else: msgs = msgs.replace('charset=CHARSET', 'charset=UTF-8') - with open(potfile, 'ab') as fp: + with open(potfile, 'a') as fp: fp.write(msgs) def process_file(file, dirpath, potfile, domain, verbosity, @@ -252,7 +252,7 @@ def write_po_file(pofile, potfile, domain, locale, verbosity, stdout, msgs = copy_plural_forms(msgs, locale, domain, verbosity, stdout) msgs = msgs.replace( "#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % domain, "") - with open(pofile, 'wb') as fp: + with open(pofile, 'w') as fp: fp.write(msgs) os.unlink(potfile) if no_obsolete: From b109ff8062f4bb225181ec462d69c9dd79339567 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 13 Aug 2012 20:56:05 +0200 Subject: [PATCH 046/253] Replaced pickle by json in form_hmac calculation Refs #18340 --- django/contrib/formtools/tests/__init__.py | 19 ++++++++++--------- django/contrib/formtools/utils.py | 8 ++++---- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/django/contrib/formtools/tests/__init__.py b/django/contrib/formtools/tests/__init__.py index ee93479cbd..8a3027bc08 100644 --- a/django/contrib/formtools/tests/__init__.py +++ b/django/contrib/formtools/tests/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from __future__ import unicode_literals import os @@ -165,8 +166,8 @@ class FormHmacTests(unittest.TestCase): leading/trailing whitespace so as to be friendly to broken browsers that submit it (usually in textareas). """ - f1 = HashTestForm({'name': 'joe', 'bio': 'Nothing notable.'}) - f2 = HashTestForm({'name': ' joe', 'bio': 'Nothing notable. '}) + f1 = HashTestForm({'name': 'joe', 'bio': 'Speaking español.'}) + f2 = HashTestForm({'name': ' joe', 'bio': 'Speaking español. '}) hash1 = utils.form_hmac(f1) hash2 = utils.form_hmac(f2) self.assertEqual(hash1, hash2) @@ -270,7 +271,7 @@ class WizardTests(TestCase): """ data = {"0-field": "test", "1-field": "test2", - "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", + "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", "wizard_step": "1"} response = self.client.post('/wizard1/', data) self.assertEqual(2, response.context['step0']) @@ -295,15 +296,15 @@ class WizardTests(TestCase): wizard = WizardWithProcessStep([WizardPageOneForm]) data = {"0-field": "test", "1-field": "test2", - "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", + "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", "wizard_step": "1"} wizard(DummyRequest(POST=data)) self.assertTrue(reached[0]) data = {"0-field": "test", "1-field": "test2", - "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", - "hash_1": "1e6f6315da42e62f33a30640ec7e007ad3fbf1a1", + "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", + "hash_1": "4c352938f08b0e6467bef3cda578a1d4a82edc66", "wizard_step": "2"} self.assertRaises(http.Http404, wizard, DummyRequest(POST=data)) @@ -325,7 +326,7 @@ class WizardTests(TestCase): WizardPageThreeForm]) data = {"0-field": "test", "1-field": "test2", - "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", + "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", "wizard_step": "1"} wizard(DummyRequest(POST=data)) self.assertTrue(reached[0]) @@ -349,7 +350,7 @@ class WizardTests(TestCase): data = {"0-field": "test", "1-field": "test2", - "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", + "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", "wizard_step": "1"} wizard(DummyRequest(POST=data)) self.assertTrue(reached[0]) @@ -375,7 +376,7 @@ class WizardTests(TestCase): WizardPageThreeForm]) data = {"0-field": "test", "1-field": "test2", - "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", + "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", "wizard_step": "1"} wizard(DummyRequest(POST=data)) self.assertTrue(reached[0]) diff --git a/django/contrib/formtools/utils.py b/django/contrib/formtools/utils.py index 8763cded07..8b37651ab7 100644 --- a/django/contrib/formtools/utils.py +++ b/django/contrib/formtools/utils.py @@ -1,5 +1,6 @@ -# Do not try cPickle here (see #18340) -import pickle +from __future__ import unicode_literals + +import json from django.utils.crypto import salted_hmac from django.utils import six @@ -21,6 +22,5 @@ def form_hmac(form): value = value.strip() data.append((bf.name, value)) - pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) key_salt = 'django.contrib.formtools' - return salted_hmac(key_salt, pickled).hexdigest() + return salted_hmac(key_salt, json.dumps(data)).hexdigest() From a025b75f6c3021ab400224250defa2db37b8122c Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 13 Aug 2012 21:25:27 +0200 Subject: [PATCH 047/253] [py3] Fixed iterlists usage in QueryDict --- django/http/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django/http/__init__.py b/django/http/__init__.py index 85d9b3b920..12a11cface 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -415,14 +415,14 @@ class QueryDict(MultiValueDict): def __copy__(self): result = self.__class__('', mutable=True, encoding=self.encoding) - for key, value in self.iterlists(): + for key, value in six.iterlists(self): result.setlist(key, value) return result def __deepcopy__(self, memo): result = self.__class__('', mutable=True, encoding=self.encoding) memo[id(self)] = result - for key, value in self.iterlists(): + for key, value in six.iterlists(self): result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo)) return result From 363dbd920e9b77da83895598f0fc9f7f835df65d Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Mon, 13 Aug 2012 21:07:51 +0200 Subject: [PATCH 048/253] [py3] Fixed contrib.formtools tests --- django/contrib/formtools/tests/__init__.py | 15 ++++++++------- django/contrib/formtools/wizard/storage/base.py | 4 +--- django/http/__init__.py | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/django/contrib/formtools/tests/__init__.py b/django/contrib/formtools/tests/__init__.py index 8a3027bc08..f3537f4afd 100644 --- a/django/contrib/formtools/tests/__init__.py +++ b/django/contrib/formtools/tests/__init__.py @@ -17,6 +17,7 @@ from django.contrib.formtools.tests.wizard import * from django.contrib.formtools.tests.forms import * success_string = "Done was called!" +success_string_encoded = success_string.encode() class TestFormPreview(preview.FormPreview): def get_context(self, request, form): @@ -99,11 +100,11 @@ class PreviewTests(TestCase): # show we previously saw first stage of the form. self.test_data.update({'stage':2}) response = self.client.post('/preview/', self.test_data) - self.assertNotEqual(response.content, success_string) + self.assertNotEqual(response.content, success_string_encoded) hash = self.preview.security_hash(None, TestForm(self.test_data)) self.test_data.update({'hash': hash}) response = self.client.post('/preview/', self.test_data) - self.assertEqual(response.content, success_string) + self.assertEqual(response.content, success_string_encoded) def test_bool_submit(self): """ @@ -123,7 +124,7 @@ class PreviewTests(TestCase): self.test_data.update({'hash': hash, 'bool1': 'False'}) with warnings.catch_warnings(record=True): response = self.client.post('/preview/', self.test_data) - self.assertEqual(response.content, success_string) + self.assertEqual(response.content, success_string_encoded) def test_form_submit_good_hash(self): """ @@ -134,11 +135,11 @@ class PreviewTests(TestCase): # show we previously saw first stage of the form. self.test_data.update({'stage':2}) response = self.client.post('/preview/', self.test_data) - self.assertNotEqual(response.content, success_string) + self.assertNotEqual(response.content, success_string_encoded) hash = utils.form_hmac(TestForm(self.test_data)) self.test_data.update({'hash': hash}) response = self.client.post('/preview/', self.test_data) - self.assertEqual(response.content, success_string) + self.assertEqual(response.content, success_string_encoded) def test_form_submit_bad_hash(self): @@ -151,11 +152,11 @@ class PreviewTests(TestCase): self.test_data.update({'stage':2}) response = self.client.post('/preview/', self.test_data) self.assertEqual(response.status_code, 200) - self.assertNotEqual(response.content, success_string) + self.assertNotEqual(response.content, success_string_encoded) hash = utils.form_hmac(TestForm(self.test_data)) + "bad" self.test_data.update({'hash': hash}) response = self.client.post('/previewpreview/', self.test_data) - self.assertNotEqual(response.content, success_string) + self.assertNotEqual(response.content, success_string_encoded) class FormHmacTests(unittest.TestCase): diff --git a/django/contrib/formtools/wizard/storage/base.py b/django/contrib/formtools/wizard/storage/base.py index 05c9f6f121..aafc833484 100644 --- a/django/contrib/formtools/wizard/storage/base.py +++ b/django/contrib/formtools/wizard/storage/base.py @@ -1,6 +1,5 @@ from django.core.files.uploadedfile import UploadedFile from django.utils.datastructures import MultiValueDict -from django.utils.encoding import smart_bytes from django.utils.functional import lazy_property from django.utils import six @@ -74,8 +73,7 @@ class BaseStorage(object): files = {} for field, field_dict in six.iteritems(wizard_files): - field_dict = dict((smart_bytes(k), v) - for k, v in six.iteritems(field_dict)) + field_dict = field_dict.copy() tmp_name = field_dict.pop('tmp_name') files[field] = UploadedFile( file=self.file_storage.open(tmp_name), **field_dict) diff --git a/django/http/__init__.py b/django/http/__init__.py index 12a11cface..ed4a23928f 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -193,7 +193,7 @@ class HttpRequest(object): default argument in which case that value will be returned instead. """ try: - cookie_value = self.COOKIES[key].encode('utf-8') + cookie_value = self.COOKIES[key] except KeyError: if default is not RAISE_ERROR: return default From 0df0cf70d43a642393d118523b7efdc04dae6105 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Tue, 14 Aug 2012 09:45:53 +0200 Subject: [PATCH 049/253] Reverted pickle-json replacement form_hmac calculation This reverts commit b109ff8062f4bb225181ec462d69c9dd79339567 and complement test cases. The change was too hasty, as some form values cannot be json-serialized as is. --- django/contrib/formtools/tests/__init__.py | 19 ++++++++++--------- django/contrib/formtools/tests/forms.py | 1 + django/contrib/formtools/utils.py | 6 ++++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/django/contrib/formtools/tests/__init__.py b/django/contrib/formtools/tests/__init__.py index f3537f4afd..36c7a6d0a2 100644 --- a/django/contrib/formtools/tests/__init__.py +++ b/django/contrib/formtools/tests/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import datetime import os import re import warnings @@ -80,7 +81,7 @@ class PreviewTests(TestCase): """ # Pass strings for form submittal and add stage variable to # show we previously saw first stage of the form. - self.test_data.update({'stage': 1}) + self.test_data.update({'stage': 1, 'date1': datetime.date(2006, 10, 25)}) response = self.client.post('/preview/', self.test_data) # Check to confirm stage is set to 2 in output form. stage = self.input % 2 @@ -98,7 +99,7 @@ class PreviewTests(TestCase): """ # Pass strings for form submittal and add stage variable to # show we previously saw first stage of the form. - self.test_data.update({'stage':2}) + self.test_data.update({'stage': 2, 'date1': datetime.date(2006, 10, 25)}) response = self.client.post('/preview/', self.test_data) self.assertNotEqual(response.content, success_string_encoded) hash = self.preview.security_hash(None, TestForm(self.test_data)) @@ -272,7 +273,7 @@ class WizardTests(TestCase): """ data = {"0-field": "test", "1-field": "test2", - "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", + "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", "wizard_step": "1"} response = self.client.post('/wizard1/', data) self.assertEqual(2, response.context['step0']) @@ -297,15 +298,15 @@ class WizardTests(TestCase): wizard = WizardWithProcessStep([WizardPageOneForm]) data = {"0-field": "test", "1-field": "test2", - "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", + "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", "wizard_step": "1"} wizard(DummyRequest(POST=data)) self.assertTrue(reached[0]) data = {"0-field": "test", "1-field": "test2", - "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", - "hash_1": "4c352938f08b0e6467bef3cda578a1d4a82edc66", + "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", + "hash_1": "1e6f6315da42e62f33a30640ec7e007ad3fbf1a1", "wizard_step": "2"} self.assertRaises(http.Http404, wizard, DummyRequest(POST=data)) @@ -327,7 +328,7 @@ class WizardTests(TestCase): WizardPageThreeForm]) data = {"0-field": "test", "1-field": "test2", - "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", + "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", "wizard_step": "1"} wizard(DummyRequest(POST=data)) self.assertTrue(reached[0]) @@ -351,7 +352,7 @@ class WizardTests(TestCase): data = {"0-field": "test", "1-field": "test2", - "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", + "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", "wizard_step": "1"} wizard(DummyRequest(POST=data)) self.assertTrue(reached[0]) @@ -377,7 +378,7 @@ class WizardTests(TestCase): WizardPageThreeForm]) data = {"0-field": "test", "1-field": "test2", - "hash_0": "09a53d8de15fc155bad33423e1d2ee2d82484d8a", + "hash_0": "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", "wizard_step": "1"} wizard(DummyRequest(POST=data)) self.assertTrue(reached[0]) diff --git a/django/contrib/formtools/tests/forms.py b/django/contrib/formtools/tests/forms.py index 9f6f596d3c..1ed2ab48bb 100644 --- a/django/contrib/formtools/tests/forms.py +++ b/django/contrib/formtools/tests/forms.py @@ -21,6 +21,7 @@ class TestForm(forms.Form): field1 = forms.CharField() field1_ = forms.CharField() bool1 = forms.BooleanField(required=False) + date1 = forms.DateField(required=False) class HashTestForm(forms.Form): name = forms.CharField() diff --git a/django/contrib/formtools/utils.py b/django/contrib/formtools/utils.py index 8b37651ab7..76277c6b49 100644 --- a/django/contrib/formtools/utils.py +++ b/django/contrib/formtools/utils.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals -import json +# Do not try cPickle here (see #18340) +import pickle from django.utils.crypto import salted_hmac from django.utils import six @@ -22,5 +23,6 @@ def form_hmac(form): value = value.strip() data.append((bf.name, value)) + pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) key_salt = 'django.contrib.formtools' - return salted_hmac(key_salt, json.dumps(data)).hexdigest() + return salted_hmac(key_salt, pickled).hexdigest() From e04230e2e406dcf74eeec8d3c95c84362c7da780 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Tue, 14 Aug 2012 09:44:04 +0200 Subject: [PATCH 050/253] [py3] Ported django.http according to PEP 3333. Perfomed some style cleanup while I was in the area. --- django/http/__init__.py | 106 +++++++++------- tests/regressiontests/httpwrappers/tests.py | 129 ++++++++++---------- tests/regressiontests/wsgi/tests.py | 4 +- 3 files changed, 132 insertions(+), 107 deletions(-) diff --git a/django/http/__init__.py b/django/http/__init__.py index ed4a23928f..62415da3c6 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -113,7 +113,7 @@ def build_request_repr(request, path_override=None, GET_override=None, get = (pformat(GET_override) if GET_override is not None else pformat(request.GET)) - except: + except Exception: get = '' if request._post_parse_error: post = '' @@ -122,19 +122,19 @@ def build_request_repr(request, path_override=None, GET_override=None, post = (pformat(POST_override) if POST_override is not None else pformat(request.POST)) - except: + except Exception: post = '' try: cookies = (pformat(COOKIES_override) if COOKIES_override is not None else pformat(request.COOKIES)) - except: + except Exception: cookies = '' try: meta = (pformat(META_override) if META_override is not None else pformat(request.META)) - except: + except Exception: meta = '' path = path_override if path_override is not None else request.path return smart_str('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % @@ -177,14 +177,14 @@ class HttpRequest(object): # Reconstruct the host using the algorithm from PEP 333. host = self.META['SERVER_NAME'] server_port = str(self.META['SERVER_PORT']) - if server_port != (self.is_secure() and '443' or '80'): + if server_port != ('443' if self.is_secure() else '80'): host = '%s:%s' % (host, server_port) return host def get_full_path(self): # RFC 3986 requires query string arguments to be in the ASCII range. # Rather than crash if this doesn't happen, we encode defensively. - return '%s%s' % (self.path, self.META.get('QUERY_STRING', '') and ('?' + iri_to_uri(self.META.get('QUERY_STRING', ''))) or '') + return '%s%s' % (self.path, ('?' + iri_to_uri(self.META.get('QUERY_STRING', ''))) if self.META.get('QUERY_STRING', '') else '') def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None): """ @@ -218,7 +218,7 @@ class HttpRequest(object): if not location: location = self.get_full_path() if not absolute_http_url_re.match(location): - current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http', + current_uri = '%s://%s%s' % ('https' if self.is_secure() else 'http', self.get_host(), self.path) location = urljoin(current_uri, location) return iri_to_uri(location) @@ -281,7 +281,7 @@ class HttpRequest(object): """Returns a tuple of (POST QueryDict, FILES MultiValueDict).""" self.upload_handlers = ImmutableList( self.upload_handlers, - warning = "You cannot alter upload handlers after the upload has been processed." + warning="You cannot alter upload handlers after the upload has been processed." ) parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding) return parser.parse() @@ -294,7 +294,7 @@ class HttpRequest(object): try: self._body = self.read() except IOError as e: - six.reraise(UnreadablePostError, UnreadablePostError(*tuple(e.args)), sys.exc_info()[2]) + six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2]) self._stream = BytesIO(self._body) return self._body @@ -360,6 +360,7 @@ class HttpRequest(object): if not buf: break yield buf + __iter__ = xreadlines def readlines(self): @@ -384,9 +385,16 @@ class QueryDict(MultiValueDict): if not encoding: encoding = settings.DEFAULT_CHARSET self.encoding = encoding - for key, value in parse_qsl((query_string or ''), True): # keep_blank_values=True - self.appendlist(force_text(key, encoding, errors='replace'), - force_text(value, encoding, errors='replace')) + if six.PY3: + for key, value in parse_qsl(query_string or '', + keep_blank_values=True, + encoding=encoding): + self.appendlist(key, value) + else: + for key, value in parse_qsl(query_string or '', + keep_blank_values=True): + self.appendlist(force_text(key, encoding, errors='replace'), + force_text(value, encoding, errors='replace')) self._mutable = mutable def _get_encoding(self): @@ -405,8 +413,8 @@ class QueryDict(MultiValueDict): def __setitem__(self, key, value): self._assert_mutable() - key = str_to_unicode(key, self.encoding) - value = str_to_unicode(value, self.encoding) + key = bytes_to_text(key, self.encoding) + value = bytes_to_text(value, self.encoding) super(QueryDict, self).__setitem__(key, value) def __delitem__(self, key): @@ -428,8 +436,8 @@ class QueryDict(MultiValueDict): def setlist(self, key, list_): self._assert_mutable() - key = str_to_unicode(key, self.encoding) - list_ = [str_to_unicode(elt, self.encoding) for elt in list_] + key = bytes_to_text(key, self.encoding) + list_ = [bytes_to_text(elt, self.encoding) for elt in list_] super(QueryDict, self).setlist(key, list_) def setlistdefault(self, key, default_list=None): @@ -438,8 +446,8 @@ class QueryDict(MultiValueDict): def appendlist(self, key, value): self._assert_mutable() - key = str_to_unicode(key, self.encoding) - value = str_to_unicode(value, self.encoding) + key = bytes_to_text(key, self.encoding) + value = bytes_to_text(value, self.encoding) super(QueryDict, self).appendlist(key, value) def pop(self, key, *args): @@ -456,8 +464,8 @@ class QueryDict(MultiValueDict): def setdefault(self, key, default=None): self._assert_mutable() - key = str_to_unicode(key, self.encoding) - default = str_to_unicode(default, self.encoding) + key = bytes_to_text(key, self.encoding) + default = bytes_to_text(default, self.encoding) return super(QueryDict, self).setdefault(key, default) def copy(self): @@ -531,6 +539,7 @@ class HttpResponse(object): if not content_type: content_type = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE, self._charset) + # content is a bytestring. See _get_content / _set_content. self.content = content self.cookies = SimpleCookie() if status: @@ -538,30 +547,40 @@ class HttpResponse(object): self['Content-Type'] = content_type - def __str__(self): - """Full HTTP message, including headers.""" - return '\n'.join(['%s: %s' % (key, value) - for key, value in self._headers.values()]) \ - + '\n\n' + self.content + def serialize(self): + """Full HTTP message, including headers, as a bytestring.""" + headers = [ + b'%s: %s' % (key.encode('us-ascii'), value.encode('us-ascii')) + for key, value in self._headers.values() + ] + return b'\r\n'.join(headers) + b'\r\n\r\n' + self.content + + if six.PY3: + __bytes__ = serialize + else: + __str__ = serialize def _convert_to_ascii(self, *values): """Converts all values to ascii strings.""" for value in values: - if isinstance(value, six.text_type): - try: - if not six.PY3: - value = value.encode('us-ascii') - else: - # In Python 3, use a string in headers, - # but ensure in only contains ASCII characters. - value.encode('us-ascii') - except UnicodeError as e: - e.reason += ', HTTP response headers must be in US-ASCII format' - raise - else: + if not isinstance(value, six.string_types): value = str(value) + try: + if six.PY3: + # Ensure string only contains ASCII + value.encode('us-ascii') + else: + if isinstance(value, str): + # Ensure string only contains ASCII + value.decode('us-ascii') + else: + # Convert unicode to an ASCII string + value = value.encode('us-ascii') + except UnicodeError as e: + e.reason += ', HTTP response headers must be in US-ASCII format' + raise if '\n' in value or '\r' in value: - raise BadHeaderError("Header values can't contain newlines (got %r)" % (value)) + raise BadHeaderError("Header values can't contain newlines (got %r)" % value) yield value def __setitem__(self, header, value): @@ -652,11 +671,12 @@ class HttpResponse(object): def _get_content(self): if self.has_header('Content-Encoding'): - return b''.join([str(e) for e in self._container]) + # XXX this doesn't work under Python 3 when e is an integer (#18764) + return b''.join([bytes(e) for e in self._container]) return b''.join([smart_bytes(e, self._charset) for e in self._container]) def _set_content(self, value): - if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.text_type)): + if hasattr(value, '__iter__') and not isinstance(value, six.string_types): self._container = value self._base_content_is_iter = True else: @@ -673,7 +693,7 @@ class HttpResponse(object): chunk = next(self._iterator) if isinstance(chunk, six.text_type): chunk = chunk.encode(self._charset) - return str(chunk) + return bytes(chunk) next = __next__ # Python 2 compatibility @@ -743,8 +763,8 @@ def get_host(request): # It's neither necessary nor appropriate to use # django.utils.encoding.smart_text for parsing URLs and form inputs. Thus, -# this slightly more restricted function. -def str_to_unicode(s, encoding): +# this slightly more restricted function, used by QueryDict. +def bytes_to_text(s, encoding): """ Converts basestring objects to unicode, using the given encoding. Illegally encoded input characters are replaced with Unicode "unknown" codepoint diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py index ad0ce4197e..332f25dfe9 100644 --- a/tests/regressiontests/httpwrappers/tests.py +++ b/tests/regressiontests/httpwrappers/tests.py @@ -9,16 +9,17 @@ from django.http import (QueryDict, HttpResponse, HttpResponseRedirect, HttpResponsePermanentRedirect, SimpleCookie, BadHeaderError, parse_cookie) +from django.utils import six from django.utils import unittest class QueryDictTests(unittest.TestCase): def test_missing_key(self): - q = QueryDict('') + q = QueryDict(str('')) self.assertRaises(KeyError, q.__getitem__, 'foo') def test_immutability(self): - q = QueryDict('') + q = QueryDict(str('')) self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') self.assertRaises(AttributeError, q.setlist, 'foo', ['bar']) self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) @@ -28,26 +29,26 @@ class QueryDictTests(unittest.TestCase): self.assertRaises(AttributeError, q.clear) def test_immutable_get_with_default(self): - q = QueryDict('') + q = QueryDict(str('')) self.assertEqual(q.get('foo', 'default'), 'default') def test_immutable_basic_operations(self): - q = QueryDict('') + q = QueryDict(str('')) self.assertEqual(q.getlist('foo'), []) - self.assertEqual(q.has_key('foo'), False) + if not six.PY3: + self.assertEqual(q.has_key('foo'), False) self.assertEqual('foo' in q, False) - self.assertEqual(q.items(), []) - self.assertEqual(q.lists(), []) - self.assertEqual(q.items(), []) - self.assertEqual(q.keys(), []) - self.assertEqual(q.values(), []) + self.assertEqual(list(six.iteritems(q)), []) + self.assertEqual(list(six.iterlists(q)), []) + self.assertEqual(list(six.iterkeys(q)), []) + self.assertEqual(list(six.itervalues(q)), []) self.assertEqual(len(q), 0) self.assertEqual(q.urlencode(), '') def test_single_key_value(self): """Test QueryDict with one key/value pair""" - q = QueryDict('foo=bar') + q = QueryDict(str('foo=bar')) self.assertEqual(q['foo'], 'bar') self.assertRaises(KeyError, q.__getitem__, 'bar') self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') @@ -60,15 +61,17 @@ class QueryDictTests(unittest.TestCase): self.assertRaises(AttributeError, q.setlist, 'foo', ['bar']) self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) - self.assertTrue(q.has_key('foo')) + if not six.PY3: + self.assertTrue(q.has_key('foo')) self.assertTrue('foo' in q) - self.assertFalse(q.has_key('bar')) + if not six.PY3: + self.assertFalse(q.has_key('bar')) self.assertFalse('bar' in q) - self.assertEqual(q.items(), [('foo', 'bar')]) - self.assertEqual(q.lists(), [('foo', ['bar'])]) - self.assertEqual(q.keys(), ['foo']) - self.assertEqual(q.values(), ['bar']) + self.assertEqual(list(six.iteritems(q)), [('foo', 'bar')]) + self.assertEqual(list(six.iterlists(q)), [('foo', ['bar'])]) + self.assertEqual(list(six.iterkeys(q)), ['foo']) + self.assertEqual(list(six.itervalues(q)), ['bar']) self.assertEqual(len(q), 1) self.assertRaises(AttributeError, q.update, {'foo': 'bar'}) @@ -80,30 +83,30 @@ class QueryDictTests(unittest.TestCase): self.assertEqual(q.urlencode(), 'foo=bar') def test_urlencode(self): - q = QueryDict('', mutable=True) + q = QueryDict(str(''), mutable=True) q['next'] = '/a&b/' self.assertEqual(q.urlencode(), 'next=%2Fa%26b%2F') self.assertEqual(q.urlencode(safe='/'), 'next=/a%26b/') - q = QueryDict('', mutable=True) + q = QueryDict(str(''), mutable=True) q['next'] = '/t\xebst&key/' self.assertEqual(q.urlencode(), 'next=%2Ft%C3%ABst%26key%2F') self.assertEqual(q.urlencode(safe='/'), 'next=/t%C3%ABst%26key/') def test_mutable_copy(self): """A copy of a QueryDict is mutable.""" - q = QueryDict('').copy() + q = QueryDict(str('')).copy() self.assertRaises(KeyError, q.__getitem__, "foo") q['name'] = 'john' self.assertEqual(q['name'], 'john') def test_mutable_delete(self): - q = QueryDict('').copy() + q = QueryDict(str('')).copy() q['name'] = 'john' del q['name'] self.assertFalse('name' in q) def test_basic_mutable_operations(self): - q = QueryDict('').copy() + q = QueryDict(str('')).copy() q['name'] = 'john' self.assertEqual(q.get('foo', 'default'), 'default') self.assertEqual(q.get('name', 'default'), 'john') @@ -117,13 +120,14 @@ class QueryDictTests(unittest.TestCase): q.appendlist('foo', 'another') self.assertEqual(q.getlist('foo'), ['bar', 'baz', 'another']) self.assertEqual(q['foo'], 'another') - self.assertTrue(q.has_key('foo')) + if not six.PY3: + self.assertTrue(q.has_key('foo')) self.assertTrue('foo' in q) - self.assertEqual(q.items(), [('foo', 'another'), ('name', 'john')]) - self.assertEqual(q.lists(), [('foo', ['bar', 'baz', 'another']), ('name', ['john'])]) - self.assertEqual(q.keys(), ['foo', 'name']) - self.assertEqual(q.values(), ['another', 'john']) + self.assertEqual(list(six.iteritems(q)), [('foo', 'another'), ('name', 'john')]) + self.assertEqual(list(six.iterlists(q)), [('foo', ['bar', 'baz', 'another']), ('name', ['john'])]) + self.assertEqual(list(six.iterkeys(q)), ['foo', 'name']) + self.assertEqual(list(six.itervalues(q)), ['another', 'john']) self.assertEqual(len(q), 2) q.update({'foo': 'hello'}) @@ -144,7 +148,7 @@ class QueryDictTests(unittest.TestCase): def test_multiple_keys(self): """Test QueryDict with two key/value pairs with same keys.""" - q = QueryDict('vote=yes&vote=no') + q = QueryDict(str('vote=yes&vote=no')) self.assertEqual(q['vote'], 'no') self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') @@ -158,14 +162,16 @@ class QueryDictTests(unittest.TestCase): self.assertRaises(AttributeError, q.setlist, 'foo', ['bar', 'baz']) self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) - self.assertEqual(q.has_key('vote'), True) + if not six.PY3: + self.assertEqual(q.has_key('vote'), True) self.assertEqual('vote' in q, True) - self.assertEqual(q.has_key('foo'), False) + if not six.PY3: + self.assertEqual(q.has_key('foo'), False) self.assertEqual('foo' in q, False) - self.assertEqual(q.items(), [('vote', 'no')]) - self.assertEqual(q.lists(), [('vote', ['yes', 'no'])]) - self.assertEqual(q.keys(), ['vote']) - self.assertEqual(q.values(), ['no']) + self.assertEqual(list(six.iteritems(q)), [('vote', 'no')]) + self.assertEqual(list(six.iterlists(q)), [('vote', ['yes', 'no'])]) + self.assertEqual(list(six.iterkeys(q)), ['vote']) + self.assertEqual(list(six.itervalues(q)), ['no']) self.assertEqual(len(q), 1) self.assertRaises(AttributeError, q.update, {'foo': 'bar'}) @@ -175,45 +181,49 @@ class QueryDictTests(unittest.TestCase): self.assertRaises(AttributeError, q.setdefault, 'foo', 'bar') self.assertRaises(AttributeError, q.__delitem__, 'vote') - def test_invalid_input_encoding(self): - """ - QueryDicts must be able to handle invalid input encoding (in this - case, bad UTF-8 encoding). - """ - q = QueryDict(b'foo=bar&foo=\xff') - self.assertEqual(q['foo'], '\ufffd') - self.assertEqual(q.getlist('foo'), ['bar', '\ufffd']) + if not six.PY3: + def test_invalid_input_encoding(self): + """ + QueryDicts must be able to handle invalid input encoding (in this + case, bad UTF-8 encoding). + + This test doesn't apply under Python 3 because the URL is a string + and not a bytestring. + """ + q = QueryDict(str(b'foo=bar&foo=\xff')) + self.assertEqual(q['foo'], '\ufffd') + self.assertEqual(q.getlist('foo'), ['bar', '\ufffd']) def test_pickle(self): - q = QueryDict('') + q = QueryDict(str('')) q1 = pickle.loads(pickle.dumps(q, 2)) self.assertEqual(q == q1, True) - q = QueryDict('a=b&c=d') + q = QueryDict(str('a=b&c=d')) q1 = pickle.loads(pickle.dumps(q, 2)) self.assertEqual(q == q1, True) - q = QueryDict('a=b&c=d&a=1') + q = QueryDict(str('a=b&c=d&a=1')) q1 = pickle.loads(pickle.dumps(q, 2)) self.assertEqual(q == q1, True) def test_update_from_querydict(self): """Regression test for #8278: QueryDict.update(QueryDict)""" - x = QueryDict("a=1&a=2", mutable=True) - y = QueryDict("a=3&a=4") + x = QueryDict(str("a=1&a=2"), mutable=True) + y = QueryDict(str("a=3&a=4")) x.update(y) self.assertEqual(x.getlist('a'), ['1', '2', '3', '4']) def test_non_default_encoding(self): """#13572 - QueryDict with a non-default encoding""" - q = QueryDict(b'sbb=one', encoding='rot_13') - self.assertEqual(q.encoding , 'rot_13' ) - self.assertEqual(q.items() , [('foo', 'bar')] ) - self.assertEqual(q.urlencode() , 'sbb=one' ) + q = QueryDict(str('cur=%A4'), encoding='iso-8859-15') + self.assertEqual(q.encoding, 'iso-8859-15') + self.assertEqual(list(six.iteritems(q)), [('cur', '€')]) + self.assertEqual(q.urlencode(), 'cur=%A4') q = q.copy() - self.assertEqual(q.encoding , 'rot_13' ) - self.assertEqual(q.items() , [('foo', 'bar')] ) - self.assertEqual(q.urlencode() , 'sbb=one' ) - self.assertEqual(copy.copy(q).encoding , 'rot_13' ) - self.assertEqual(copy.deepcopy(q).encoding , 'rot_13') + self.assertEqual(q.encoding, 'iso-8859-15') + self.assertEqual(list(six.iteritems(q)), [('cur', '€')]) + self.assertEqual(q.urlencode(), 'cur=%A4') + self.assertEqual(copy.copy(q).encoding, 'iso-8859-15') + self.assertEqual(copy.deepcopy(q).encoding, 'iso-8859-15') class HttpResponseTests(unittest.TestCase): def test_unicode_headers(self): @@ -283,13 +293,8 @@ class HttpResponseTests(unittest.TestCase): #test retrieval explicitly using iter and odd inputs r = HttpResponse() r.content = ['1', '2', 3, '\u079e'] - result = [] my_iter = r.__iter__() - while True: - try: - result.append(next(my_iter)) - except StopIteration: - break + result = list(my_iter) #'\xde\x9e' == unichr(1950).encode('utf-8') self.assertEqual(result, ['1', '2', '3', b'\xde\x9e']) self.assertEqual(r.content, b'123\xde\x9e') diff --git a/tests/regressiontests/wsgi/tests.py b/tests/regressiontests/wsgi/tests.py index a482a5c1eb..5a40bd88e2 100644 --- a/tests/regressiontests/wsgi/tests.py +++ b/tests/regressiontests/wsgi/tests.py @@ -40,8 +40,8 @@ class WSGITest(TestCase): response_data["headers"], [('Content-Type', 'text/html; charset=utf-8')]) self.assertEqual( - six.text_type(response), - "Content-Type: text/html; charset=utf-8\n\nHello World!") + bytes(response), + b"Content-Type: text/html; charset=utf-8\r\n\r\nHello World!") class GetInternalWSGIApplicationTest(unittest.TestCase): From d1452f60974da6f0e54ff9ad7a03d2c115675d10 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Tue, 14 Aug 2012 12:19:17 +0200 Subject: [PATCH 051/253] [py3] Favoured unicode strings in assert(Not)Contains In Python 3, HTMLParser does not support bytestrings. --- django/test/testcases.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/django/test/testcases.py b/django/test/testcases.py index 56ba56caf1..af807e075f 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -647,14 +647,13 @@ class TransactionTestCase(SimpleTestCase): self.assertEqual(response.status_code, status_code, msg_prefix + "Couldn't retrieve content: Response code was %d" " (expected %d)" % (response.status_code, status_code)) - enc_text = smart_bytes(text, response._charset) - content = response.content + content = response.content.decode(response._charset) if html: content = assert_and_parse_html(self, content, None, "Response's content is not valid HTML:") - enc_text = assert_and_parse_html(self, enc_text, None, + text = assert_and_parse_html(self, text, None, "Second argument is not valid HTML:") - real_count = content.count(enc_text) + real_count = content.count(text) if count is not None: self.assertEqual(real_count, count, msg_prefix + "Found %d instances of '%s' in response" @@ -683,14 +682,13 @@ class TransactionTestCase(SimpleTestCase): self.assertEqual(response.status_code, status_code, msg_prefix + "Couldn't retrieve content: Response code was %d" " (expected %d)" % (response.status_code, status_code)) - enc_text = smart_bytes(text, response._charset) - content = response.content + content = response.content.decode(response._charset) if html: content = assert_and_parse_html(self, content, None, 'Response\'s content is not valid HTML:') - enc_text = assert_and_parse_html(self, enc_text, None, + text = assert_and_parse_html(self, text, None, 'Second argument is not valid HTML:') - self.assertEqual(content.count(enc_text), 0, + self.assertEqual(content.count(text), 0, msg_prefix + "Response should not contain '%s'" % text) def assertFormError(self, response, form, field, errors, msg_prefix=''): From faf570df187bc82a3bcffa5613165a6e6cc56a57 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Tue, 14 Aug 2012 11:57:18 +0200 Subject: [PATCH 052/253] [py3] Compared response.content with bytes. --- django/contrib/sitemaps/tests/generic.py | 4 ++- django/contrib/sitemaps/tests/http.py | 14 ++++---- django/contrib/sitemaps/tests/https.py | 10 +++--- tests/modeltests/test_client/models.py | 2 +- tests/regressiontests/admin_views/tests.py | 2 +- tests/regressiontests/cache/tests.py | 36 +++++++++---------- .../conditional_processing/models.py | 4 ++- tests/regressiontests/templates/response.py | 34 +++++++++--------- .../urlpatterns_reverse/tests.py | 8 ++--- 9 files changed, 61 insertions(+), 53 deletions(-) diff --git a/django/contrib/sitemaps/tests/generic.py b/django/contrib/sitemaps/tests/generic.py index e392cbf909..f63c5bf6e7 100644 --- a/django/contrib/sitemaps/tests/generic.py +++ b/django/contrib/sitemaps/tests/generic.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.contrib.auth.models import User from django.test.utils import override_settings @@ -12,7 +14,7 @@ class GenericViewsSitemapTests(SitemapTestsBase): expected = '' for username in User.objects.values_list("username", flat=True): expected += "%s/users/%s/" % (self.base_url, username) - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s diff --git a/django/contrib/sitemaps/tests/http.py b/django/contrib/sitemaps/tests/http.py index d71907e09a..5cff67c238 100644 --- a/django/contrib/sitemaps/tests/http.py +++ b/django/contrib/sitemaps/tests/http.py @@ -21,7 +21,7 @@ class HTTPSitemapTests(SitemapTestsBase): def test_simple_sitemap_index(self): "A simple sitemap index can be rendered" response = self.client.get('/simple/index.xml') - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/simple/sitemap-simple.xml @@ -33,7 +33,7 @@ class HTTPSitemapTests(SitemapTestsBase): def test_simple_sitemap_custom_index(self): "A simple sitemap index can be rendered with a custom template" response = self.client.get('/simple/custom-index.xml') - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/simple/sitemap-simple.xml @@ -43,7 +43,7 @@ class HTTPSitemapTests(SitemapTestsBase): def test_simple_sitemap_section(self): "A simple sitemap section can be rendered" response = self.client.get('/simple/sitemap-simple.xml') - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/location/%snever0.5 @@ -52,7 +52,7 @@ class HTTPSitemapTests(SitemapTestsBase): def test_simple_sitemap(self): "A simple sitemap can be rendered" response = self.client.get('/simple/sitemap.xml') - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/location/%snever0.5 @@ -64,7 +64,7 @@ class HTTPSitemapTests(SitemapTestsBase): def test_simple_custom_sitemap(self): "A simple sitemap can be rendered with a custom template" response = self.client.get('/simple/custom-sitemap.xml') - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/location/%snever0.5 @@ -90,7 +90,7 @@ class HTTPSitemapTests(SitemapTestsBase): # installed doesn't raise an exception Site._meta.installed = False response = self.client.get('/simple/sitemap.xml') - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" http://testserver/location/%snever0.5 @@ -131,7 +131,7 @@ class HTTPSitemapTests(SitemapTestsBase): Check that a cached sitemap index can be rendered (#2713). """ response = self.client.get('/cached/index.xml') - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/cached/sitemap-simple.xml diff --git a/django/contrib/sitemaps/tests/https.py b/django/contrib/sitemaps/tests/https.py index d4f9053fc8..34d992fc78 100644 --- a/django/contrib/sitemaps/tests/https.py +++ b/django/contrib/sitemaps/tests/https.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from datetime import date from django.test.utils import override_settings @@ -11,7 +13,7 @@ class HTTPSSitemapTests(SitemapTestsBase): def test_secure_sitemap_index(self): "A secure sitemap index can be rendered" response = self.client.get('/secure/index.xml') - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/secure/sitemap-simple.xml @@ -20,7 +22,7 @@ class HTTPSSitemapTests(SitemapTestsBase): def test_secure_sitemap_section(self): "A secure sitemap section can be rendered" response = self.client.get('/secure/sitemap-simple.xml') - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/location/%snever0.5 @@ -34,7 +36,7 @@ class HTTPSDetectionSitemapTests(SitemapTestsBase): def test_sitemap_index_with_https_request(self): "A sitemap index requested in HTTPS is rendered with HTTPS links" response = self.client.get('/simple/index.xml', **self.extra) - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/simple/sitemap-simple.xml @@ -43,7 +45,7 @@ class HTTPSDetectionSitemapTests(SitemapTestsBase): def test_sitemap_section_with_https_request(self): "A sitemap section requested in HTTPS is rendered with HTTPS links" response = self.client.get('/simple/sitemap-simple.xml', **self.extra) - self.assertEqual(response.content, """ + self.assertEqual(response.content, b""" %s/location/%snever0.5 diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index 399d2906a8..1d9c999f21 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -91,7 +91,7 @@ class ClientTest(TestCase): content_type="text/xml") self.assertEqual(response.status_code, 200) self.assertEqual(response.templates[0].name, "Book template") - self.assertEqual(response.content, "Blink - Malcolm Gladwell") + self.assertEqual(response.content, b"Blink - Malcolm Gladwell") def test_redirect(self): "GET a URL that redirects elsewhere" diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 09be55baba..c86d35ca3e 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -787,7 +787,7 @@ class CustomModelAdminTest(AdminViewBasicTest): def testCustomAdminSiteView(self): self.client.login(username='super', password='secret') response = self.client.get('/test_admin/%s/my_view/' % self.urlbit) - self.assertEqual(response.content, "Django is a magical pony!") + self.assertEqual(response.content, b"Django is a magical pony!") def get_perm(Model, perm): """Return the permission object, for the Model""" diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index 4ffca2d458..24cab64c10 100644 --- a/tests/regressiontests/cache/tests.py +++ b/tests/regressiontests/cache/tests.py @@ -1592,43 +1592,43 @@ class CacheMiddlewareTest(TestCase): # Request the view once response = default_view(request, '1') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # Request again -- hit the cache response = default_view(request, '2') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # Requesting the same view with the explicit cache should yield the same result response = explicit_default_view(request, '3') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # Requesting with a prefix will hit a different cache key response = explicit_default_with_prefix_view(request, '4') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # Hitting the same view again gives a cache hit response = explicit_default_with_prefix_view(request, '5') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # And going back to the implicit cache will hit the same cache response = default_with_prefix_view(request, '6') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # Requesting from an alternate cache won't hit cache response = other_view(request, '7') - self.assertEqual(response.content, 'Hello World 7') + self.assertEqual(response.content, b'Hello World 7') # But a repeated hit will hit cache response = other_view(request, '8') - self.assertEqual(response.content, 'Hello World 7') + self.assertEqual(response.content, b'Hello World 7') # And prefixing the alternate cache yields yet another cache entry response = other_with_prefix_view(request, '9') - self.assertEqual(response.content, 'Hello World 9') + self.assertEqual(response.content, b'Hello World 9') # Request from the alternate cache with a new prefix and a custom timeout response = other_with_timeout_view(request, '10') - self.assertEqual(response.content, 'Hello World 10') + self.assertEqual(response.content, b'Hello World 10') # But if we wait a couple of seconds... time.sleep(2) @@ -1636,38 +1636,38 @@ class CacheMiddlewareTest(TestCase): # ... the default cache will still hit cache = get_cache('default') response = default_view(request, '11') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # ... the default cache with a prefix will still hit response = default_with_prefix_view(request, '12') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # ... the explicit default cache will still hit response = explicit_default_view(request, '13') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # ... the explicit default cache with a prefix will still hit response = explicit_default_with_prefix_view(request, '14') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # .. but a rapidly expiring cache won't hit response = other_view(request, '15') - self.assertEqual(response.content, 'Hello World 15') + self.assertEqual(response.content, b'Hello World 15') # .. even if it has a prefix response = other_with_prefix_view(request, '16') - self.assertEqual(response.content, 'Hello World 16') + self.assertEqual(response.content, b'Hello World 16') # ... but a view with a custom timeout will still hit response = other_with_timeout_view(request, '17') - self.assertEqual(response.content, 'Hello World 10') + self.assertEqual(response.content, b'Hello World 10') # And if we wait a few more seconds time.sleep(2) # the custom timeouot cache will miss response = other_with_timeout_view(request, '18') - self.assertEqual(response.content, 'Hello World 18') + self.assertEqual(response.content, b'Hello World 18') @override_settings( diff --git a/tests/regressiontests/conditional_processing/models.py b/tests/regressiontests/conditional_processing/models.py index d1a8ac605b..dacea34a83 100644 --- a/tests/regressiontests/conditional_processing/models.py +++ b/tests/regressiontests/conditional_processing/models.py @@ -1,4 +1,6 @@ # -*- coding:utf-8 -*- +from __future__ import unicode_literals + from datetime import datetime from django.test import TestCase @@ -28,7 +30,7 @@ class ConditionalGet(TestCase): def assertNotModified(self, response): self.assertEqual(response.status_code, 304) - self.assertEqual(response.content, '') + self.assertEqual(response.content, b'') def testWithoutConditions(self): response = self.client.get('/condition/') diff --git a/tests/regressiontests/templates/response.py b/tests/regressiontests/templates/response.py index 3c45b7a9d4..93919b95cd 100644 --- a/tests/regressiontests/templates/response.py +++ b/tests/regressiontests/templates/response.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import os import pickle import time @@ -29,16 +31,16 @@ class SimpleTemplateResponseTest(TestCase): def test_template_resolving(self): response = SimpleTemplateResponse('first/test.html') response.render() - self.assertEqual('First template\n', response.content) + self.assertEqual(response.content, b'First template\n') templates = ['foo.html', 'second/test.html', 'first/test.html'] response = SimpleTemplateResponse(templates) response.render() - self.assertEqual('Second template\n', response.content) + self.assertEqual(response.content, b'Second template\n') response = self._response() response.render() - self.assertEqual(response.content, 'foo') + self.assertEqual(response.content, b'foo') def test_explicit_baking(self): # explicit baking @@ -50,17 +52,17 @@ class SimpleTemplateResponseTest(TestCase): def test_render(self): # response is not re-rendered without the render call response = self._response().render() - self.assertEqual(response.content, 'foo') + self.assertEqual(response.content, b'foo') # rebaking doesn't change the rendered content response.template_name = Template('bar{{ baz }}') response.render() - self.assertEqual(response.content, 'foo') + self.assertEqual(response.content, b'foo') # but rendered content can be overridden by manually # setting content response.content = 'bar' - self.assertEqual(response.content, 'bar') + self.assertEqual(response.content, b'bar') def test_iteration_unrendered(self): # unrendered response raises an exception on iteration @@ -77,7 +79,7 @@ class SimpleTemplateResponseTest(TestCase): # iteration works for rendered responses response = self._response().render() res = [x for x in response] - self.assertEqual(res, ['foo']) + self.assertEqual(res, [b'foo']) def test_content_access_unrendered(self): # unrendered response raises an exception when content is accessed @@ -89,7 +91,7 @@ class SimpleTemplateResponseTest(TestCase): def test_content_access_rendered(self): # rendered response content can be accessed response = self._response().render() - self.assertEqual(response.content, 'foo') + self.assertEqual(response.content, b'foo') def test_set_content(self): # content can be overriden @@ -97,23 +99,23 @@ class SimpleTemplateResponseTest(TestCase): self.assertFalse(response.is_rendered) response.content = 'spam' self.assertTrue(response.is_rendered) - self.assertEqual(response.content, 'spam') + self.assertEqual(response.content, b'spam') response.content = 'baz' - self.assertEqual(response.content, 'baz') + self.assertEqual(response.content, b'baz') def test_dict_context(self): response = self._response('{{ foo }}{{ processors }}', {'foo': 'bar'}) self.assertEqual(response.context_data, {'foo': 'bar'}) response.render() - self.assertEqual(response.content, 'bar') + self.assertEqual(response.content, b'bar') def test_context_instance(self): response = self._response('{{ foo }}{{ processors }}', Context({'foo': 'bar'})) self.assertEqual(response.context_data.__class__, Context) response.render() - self.assertEqual(response.content, 'bar') + self.assertEqual(response.content, b'bar') def test_kwargs(self): response = self._response(content_type = 'application/json', status=504) @@ -140,7 +142,7 @@ class SimpleTemplateResponseTest(TestCase): # When the content is rendered, all the callbacks are invoked, too. response.render() - self.assertEqual('First template\n', response.content) + self.assertEqual(response.content, b'First template\n') self.assertEqual(post, ['post1','post2']) @@ -202,17 +204,17 @@ class TemplateResponseTest(TestCase): def test_render(self): response = self._response('{{ foo }}{{ processors }}').render() - self.assertEqual(response.content, 'yes') + self.assertEqual(response.content, b'yes') def test_render_with_requestcontext(self): response = self._response('{{ foo }}{{ processors }}', {'foo': 'bar'}).render() - self.assertEqual(response.content, 'baryes') + self.assertEqual(response.content, b'baryes') def test_render_with_context(self): response = self._response('{{ foo }}{{ processors }}', Context({'foo': 'bar'})).render() - self.assertEqual(response.content, 'bar') + self.assertEqual(response.content, b'bar') def test_kwargs(self): response = self._response(content_type = 'application/json', diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py index 500a0e0327..207cc56488 100644 --- a/tests/regressiontests/urlpatterns_reverse/tests.py +++ b/tests/regressiontests/urlpatterns_reverse/tests.py @@ -1,7 +1,7 @@ """ Unit tests for reverse URL lookups. """ -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals from django.conf import settings from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist @@ -405,8 +405,8 @@ class RequestURLconfTests(TestCase): def test_urlconf(self): response = self.client.get('/test/me/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'outer:/test/me/,' - 'inner:/inner_urlconf/second_test/') + self.assertEqual(response.content, b'outer:/test/me/,' + b'inner:/inner_urlconf/second_test/') response = self.client.get('/inner_urlconf/second_test/') self.assertEqual(response.status_code, 200) response = self.client.get('/second_test/') @@ -422,7 +422,7 @@ class RequestURLconfTests(TestCase): self.assertEqual(response.status_code, 404) response = self.client.get('/second_test/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'outer:,inner:/second_test/') + self.assertEqual(response.content, b'outer:,inner:/second_test/') def test_urlconf_overridden_with_null(self): settings.MIDDLEWARE_CLASSES += ( From dcf72835e358529571dba53af1451fb39ce36c6b Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Tue, 14 Aug 2012 12:03:45 +0200 Subject: [PATCH 053/253] [py3] Ensured the template tests run. --- tests/regressiontests/templates/tests.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 6ae5af56af..a3be96cd62 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -408,8 +408,7 @@ class Templates(unittest.TestCase): ) failures = [] - tests = template_tests.items() - tests.sort() + tests = sorted(template_tests.items()) # Turn TEMPLATE_DEBUG off, because tests assume that. old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False @@ -418,7 +417,7 @@ class Templates(unittest.TestCase): old_invalid = settings.TEMPLATE_STRING_IF_INVALID expected_invalid_str = 'INVALID' - #Set ALLOWED_INCLUDE_ROOTS so that ssi works. + # Set ALLOWED_INCLUDE_ROOTS so that ssi works. old_allowed_include_roots = settings.ALLOWED_INCLUDE_ROOTS settings.ALLOWED_INCLUDE_ROOTS = ( os.path.dirname(os.path.abspath(__file__)), From 2ae58b20ec72ce501c91dff8a8fcc8fe40cf5019 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Tue, 14 Aug 2012 12:09:45 +0200 Subject: [PATCH 054/253] [py3] Fixed egg template loader. --- django/template/loaders/eggs.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/django/template/loaders/eggs.py b/django/template/loaders/eggs.py index 42f87a459e..7e35b34ec4 100644 --- a/django/template/loaders/eggs.py +++ b/django/template/loaders/eggs.py @@ -1,13 +1,15 @@ # Wrapper for loading templates from eggs via pkg_resources.resource_string. +from __future__ import unicode_literals try: from pkg_resources import resource_string except ImportError: resource_string = None +from django.conf import settings from django.template.base import TemplateDoesNotExist from django.template.loader import BaseLoader -from django.conf import settings +from django.utils import six class Loader(BaseLoader): is_usable = resource_string is not None @@ -22,9 +24,12 @@ class Loader(BaseLoader): pkg_name = 'templates/' + template_name for app in settings.INSTALLED_APPS: try: - return (resource_string(app, pkg_name).decode(settings.FILE_CHARSET), 'egg:%s:%s' % (app, pkg_name)) - except: - pass + resource = resource_string(app, pkg_name) + except Exception: + continue + if not six.PY3: + resource = resource.decode(settings.FILE_CHARSET) + return (resource, 'egg:%s:%s' % (app, pkg_name)) raise TemplateDoesNotExist(template_name) _loader = Loader() From 9299dc42ed4986d2a1eb7c7f7fe7c276b9f6b6d6 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Tue, 14 Aug 2012 14:09:23 +0200 Subject: [PATCH 055/253] [py3] Removed unnecessary calls to .keys() when computing the length of a dictionary. This fails on Python 3. --- django/db/backends/mysql/compiler.py | 2 +- django/db/backends/oracle/compiler.py | 2 +- django/db/models/base.py | 2 +- django/db/models/sql/compiler.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/django/db/backends/mysql/compiler.py b/django/db/backends/mysql/compiler.py index bd4105ff8e..012bca5da6 100644 --- a/django/db/backends/mysql/compiler.py +++ b/django/db/backends/mysql/compiler.py @@ -3,7 +3,7 @@ from django.db.models.sql import compiler class SQLCompiler(compiler.SQLCompiler): def resolve_columns(self, row, fields=()): values = [] - index_extra_select = len(self.query.extra_select.keys()) + index_extra_select = len(self.query.extra_select) for value, field in map(None, row[index_extra_select:], fields): if (field and field.get_internal_type() in ("BooleanField", "NullBooleanField") and value in (0, 1)): diff --git a/django/db/backends/oracle/compiler.py b/django/db/backends/oracle/compiler.py index c7b10429c2..e21f867f82 100644 --- a/django/db/backends/oracle/compiler.py +++ b/django/db/backends/oracle/compiler.py @@ -10,7 +10,7 @@ class SQLCompiler(compiler.SQLCompiler): rn_offset = 1 else: rn_offset = 0 - index_start = rn_offset + len(self.query.extra_select.keys()) + index_start = rn_offset + len(self.query.extra_select) values = [self.query.convert_values(v, None, connection=self.connection) for v in row[rn_offset:index_start]] for value, field in map(None, row[index_start:], fields): diff --git a/django/db/models/base.py b/django/db/models/base.py index 1e1a138714..157b74ae49 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -779,7 +779,7 @@ class Model(six.with_metaclass(ModelBase, object)): lookup_kwargs[str(field_name)] = lookup_value # some fields were skipped, no reason to do the check - if len(unique_check) != len(lookup_kwargs.keys()): + if len(unique_check) != len(lookup_kwargs): continue qs = model_class._default_manager.filter(**lookup_kwargs) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 1311ea546c..3dc0c6ece4 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -786,7 +786,7 @@ class SQLCompiler(object): row = self.resolve_columns(row, fields) if has_aggregate_select: - aggregate_start = len(self.query.extra_select.keys()) + len(self.query.select) + aggregate_start = len(self.query.extra_select) + len(self.query.select) aggregate_end = aggregate_start + len(self.query.aggregate_select) row = tuple(row[:aggregate_start]) + tuple([ self.query.resolve_aggregate(value, aggregate, self.connection) From 8fe03865f7d7059e051976d1f9336f2ff83a30c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Tue, 14 Aug 2012 15:24:43 +0300 Subject: [PATCH 056/253] [py3] Fixed invalid use of dict.items() An ordering test had two problems related to dict.items() usage: - It assumed the order of the dict was non-randomized - It indexed to the dict.items() which is py3 incompatible. I fixed the test by using dict['rank'] directly, where rank is the column tested on the values queryset. --- tests/regressiontests/queries/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py index 1582993dfc..7eae302c51 100644 --- a/tests/regressiontests/queries/tests.py +++ b/tests/regressiontests/queries/tests.py @@ -1272,8 +1272,8 @@ class Queries5Tests(TestCase): # them in a values() query. dicts = qs.values('id', 'rank').order_by('id') self.assertEqual( - [d.items()[1] for d in dicts], - [('rank', 2), ('rank', 1), ('rank', 3)] + [d['rank'] for d in dicts], + [2, 1, 3] ) def test_ticket7256(self): From 5b27e6f64b481be0fda0996456482eb56e12f2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Tue, 14 Aug 2012 15:28:55 +0300 Subject: [PATCH 057/253] [py3] Fixed comparison of list and range() A test compared list directly against a range(). This is py3 incompatible. Fixed by using list(range()). --- tests/regressiontests/queries/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py index 7eae302c51..53afd2bd93 100644 --- a/tests/regressiontests/queries/tests.py +++ b/tests/regressiontests/queries/tests.py @@ -806,7 +806,7 @@ class Queries1Tests(BaseQuerysetTest): qs = Tag.objects.values_list('id', flat=True).order_by('id') qs.query.bump_prefix() first = qs[0] - self.assertEqual(list(qs), range(first, first+5)) + self.assertEqual(list(qs), list(range(first, first+5))) def test_ticket8439(self): # Complex combinations of conjunctions, disjunctions and nullable From 0c198b85a3292cb7f2f2cd1b2d6dc6f04a1ed831 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Tue, 14 Aug 2012 14:22:08 +0200 Subject: [PATCH 058/253] [py3] Replace filter/lambda by list comprehensions This is more idiomatic and avoids returning a list on Python 2 and an iterator on Python 3. --- django/core/management/commands/syncdb.py | 2 +- django/db/models/options.py | 2 +- django/db/models/query.py | 2 +- django/utils/autoreload.py | 3 ++- django/utils/ipv6.py | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py index cf26e754ae..cceec07be8 100644 --- a/django/core/management/commands/syncdb.py +++ b/django/core/management/commands/syncdb.py @@ -75,7 +75,7 @@ class Command(NoArgsCommand): (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)) manifest = SortedDict( - (app_name, filter(model_installed, model_list)) + (app_name, list(filter(model_installed, model_list))) for app_name, model_list in all_models ) diff --git a/django/db/models/options.py b/django/db/models/options.py index 014b8bf359..6814ce27ff 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -387,7 +387,7 @@ class Options(object): predicates.append(lambda k, v: not k.field.rel.is_hidden()) cache = (self._related_objects_proxy_cache if include_proxy_eq else self._related_objects_cache) - return filter(lambda t: all([p(*t) for p in predicates]), cache.items()) + return [t for t in cache.items() if all(p(*t) for p in predicates)] def _fill_related_objects_cache(self): cache = SortedDict() diff --git a/django/db/models/query.py b/django/db/models/query.py index 3cf794924c..090ef0b7be 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1107,7 +1107,7 @@ class ValuesListQuerySet(ValuesQuerySet): # If a field list has been specified, use it. Otherwise, use the # full list of fields, including extras and aggregates. if self._fields: - fields = list(self._fields) + filter(lambda f: f not in self._fields, aggregate_names) + fields = list(self._fields) + [f for f in aggregate_names if f not in self._fields] else: fields = names diff --git a/django/utils/autoreload.py b/django/utils/autoreload.py index b6c055383c..9032cd024d 100644 --- a/django/utils/autoreload.py +++ b/django/utils/autoreload.py @@ -54,7 +54,8 @@ _win = (sys.platform == "win32") def code_changed(): global _mtimes, _win - for filename in filter(lambda v: v, map(lambda m: getattr(m, "__file__", None), sys.modules.values())): + filenames = [getattr(m, "__file__", None) for m in sys.modules.values()] + for filename in filter(None, filenames): if filename.endswith(".pyc") or filename.endswith(".pyo"): filename = filename[:-1] if filename.endswith("$py.class"): diff --git a/django/utils/ipv6.py b/django/utils/ipv6.py index 2ccae8cdd0..7624bb9c76 100644 --- a/django/utils/ipv6.py +++ b/django/utils/ipv6.py @@ -263,6 +263,6 @@ def _is_shorthand_ip(ip_str): """ if ip_str.count('::') == 1: return True - if filter(lambda x: len(x) < 4, ip_str.split(':')): + if any(len(x) < 4 for x in ip_str.split(':')): return True return False From d6b8b125fbd370bb575c43ba66270e22de919a51 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Tue, 14 Aug 2012 14:38:23 +0200 Subject: [PATCH 059/253] [py3] Fixed admin_custom_urls tests. --- tests/regressiontests/admin_custom_urls/models.py | 2 +- tests/regressiontests/admin_custom_urls/tests.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/regressiontests/admin_custom_urls/models.py b/tests/regressiontests/admin_custom_urls/models.py index 4bc9ce561e..a5b4983b09 100644 --- a/tests/regressiontests/admin_custom_urls/models.py +++ b/tests/regressiontests/admin_custom_urls/models.py @@ -28,7 +28,7 @@ class ActionAdmin(admin.ModelAdmin): Remove all entries named 'name' from the ModelAdmin instance URL patterns list """ - return filter(lambda e: e.name != name, super(ActionAdmin, self).get_urls()) + return [url for url in super(ActionAdmin, self).get_urls() if url.name != name] def get_urls(self): # Add the URL of our custom 'add_view' view to the front of the URLs diff --git a/tests/regressiontests/admin_custom_urls/tests.py b/tests/regressiontests/admin_custom_urls/tests.py index 4a6235e7d5..459f67c521 100644 --- a/tests/regressiontests/admin_custom_urls/tests.py +++ b/tests/regressiontests/admin_custom_urls/tests.py @@ -29,10 +29,7 @@ class AdminCustomUrlsTest(TestCase): def testAddWithGETArgs(self): response = self.client.get('/custom_urls/admin/admin_custom_urls/action/!add/', {'name': 'My Action'}) self.assertEqual(response.status_code, 200) - self.assertTrue( - 'value="My Action"' in response.content, - "Couldn't find an input with the right value in the response." - ) + self.assertContains(response, 'value="My Action"') def testBasicAddPost(self): """ From c2d59e556499ecee3d8a1fc684f49a21463ab1c1 Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Tue, 14 Aug 2012 14:36:11 +0200 Subject: [PATCH 060/253] [py3] Fixed admin_views tests Also changed several occurrences of 'request' to 'response'. --- django/contrib/admin/templatetags/log.py | 2 +- django/contrib/admin/views/main.py | 2 +- django/db/models/base.py | 2 +- django/utils/cache.py | 2 +- django/utils/text.py | 2 +- tests/regressiontests/admin_views/tests.py | 345 ++++++++++----------- 6 files changed, 163 insertions(+), 192 deletions(-) diff --git a/django/contrib/admin/templatetags/log.py b/django/contrib/admin/templatetags/log.py index 888b5ed9c3..463e0792f0 100644 --- a/django/contrib/admin/templatetags/log.py +++ b/django/contrib/admin/templatetags/log.py @@ -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 diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 3eabf3dbeb..4ff1a9cce5 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -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.keys()): if k.startswith(r): del p[k] for k, v in new_params.items(): diff --git a/django/db/models/base.py b/django/db/models/base.py index 157b74ae49..1beb6f9e30 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -855,7 +855,7 @@ class Model(six.with_metaclass(ModelBase, object)): } # unique_together else: - field_labels = map(lambda f: capfirst(opts.get_field(f).verbose_name), unique_check) + field_labels = [capfirst(opts.get_field(f).verbose_name) for f in unique_check] field_labels = get_text_list(field_labels, _('and')) return _("%(model_name)s with this %(field_label)s already exists.") % { 'model_name': six.text_type(model_name), diff --git a/django/utils/cache.py b/django/utils/cache.py index edac6ec75d..3e99833aa6 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -66,7 +66,7 @@ def patch_cache_control(response, **kwargs): # max-age, use the minimum of the two ages. In practice this happens when # a decorator and a piece of middleware both operate on a given view. if 'max-age' in cc and 'max_age' in kwargs: - kwargs['max_age'] = min(cc['max-age'], kwargs['max_age']) + kwargs['max_age'] = min(int(cc['max-age']), kwargs['max_age']) # Allow overriding private caching and vice versa if 'private' in cc and 'public' in kwargs: diff --git a/django/utils/text.py b/django/utils/text.py index 0838d79c65..d7f3a03174 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -287,7 +287,7 @@ ustring_re = re.compile("([\u0080-\uffff])") def javascript_quote(s, quote_double_quotes=False): def fix(match): - return b"\u%04x" % ord(match.group(1)) + return "\\u%04x" % ord(match.group(1)) if type(s) == bytes: s = s.decode('utf-8') diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index c86d35ca3e..e43ebe471e 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -30,7 +30,7 @@ from django.template.response import TemplateResponse from django.test import TestCase from django.utils import formats, translation, unittest from django.utils.cache import get_max_age -from django.utils.encoding import iri_to_uri +from django.utils.encoding import iri_to_uri, smart_bytes from django.utils.html import escape from django.utils.http import urlencode from django.utils import six @@ -78,12 +78,22 @@ class AdminViewBasicTest(TestCase): self.client.logout() formats.reset_format_cache() + def assertContentBefore(self, response, text1, text2, failing_msg=None): + """ + Testing utility asserting that text1 appears before text2 in response + content. + """ + self.assertEqual(response.status_code, 200) + self.assertTrue(response.content.index(smart_bytes(text1)) < response.content.index(smart_bytes(text2)), + failing_msg + ) + def testTrailingSlashRequired(self): """ If you leave off the trailing slash, app should redirect and add it. """ - request = self.client.get('/test_admin/%s/admin_views/article/add' % self.urlbit) - self.assertRedirects(request, + response = self.client.get('/test_admin/%s/admin_views/article/add' % self.urlbit) + self.assertRedirects(response, '/test_admin/%s/admin_views/article/add/' % self.urlbit, status_code=301 ) @@ -219,12 +229,10 @@ class AdminViewBasicTest(TestCase): (column 2 is callable_year in ArticleAdmin) """ response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': 2}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index('Oldest content') < response.content.index('Middle content') and - response.content.index('Middle content') < response.content.index('Newest content'), - "Results of sorting on callable are out of order." - ) + self.assertContentBefore(response, 'Oldest content', 'Middle content', + "Results of sorting on callable are out of order.") + self.assertContentBefore(response, 'Middle content', 'Newest content', + "Results of sorting on callable are out of order.") def testChangeListSortingModel(self): """ @@ -232,12 +240,10 @@ class AdminViewBasicTest(TestCase): (colunn 3 is 'model_year' in ArticleAdmin) """ response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '-3'}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index('Newest content') < response.content.index('Middle content') and - response.content.index('Middle content') < response.content.index('Oldest content'), - "Results of sorting on Model method are out of order." - ) + self.assertContentBefore(response, 'Newest content', 'Middle content', + "Results of sorting on Model method are out of order.") + self.assertContentBefore(response, 'Middle content', 'Oldest content', + "Results of sorting on Model method are out of order.") def testChangeListSortingModelAdmin(self): """ @@ -245,12 +251,10 @@ class AdminViewBasicTest(TestCase): (colunn 4 is 'modeladmin_year' in ArticleAdmin) """ response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '4'}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index('Oldest content') < response.content.index('Middle content') and - response.content.index('Middle content') < response.content.index('Newest content'), - "Results of sorting on ModelAdmin method are out of order." - ) + self.assertContentBefore(response, 'Oldest content', 'Middle content', + "Results of sorting on ModelAdmin method are out of order.") + self.assertContentBefore(response, 'Middle content', 'Newest content', + "Results of sorting on ModelAdmin method are out of order.") def testChangeListSortingMultiple(self): p1 = Person.objects.create(name="Chris", gender=1, alive=True) @@ -262,19 +266,13 @@ class AdminViewBasicTest(TestCase): # This hard-codes the URL because it'll fail if it runs against the # 'admin2' custom admin (which doesn't have the Person model). response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '1.2'}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % p3.id) < response.content.index(link % p1.id) and - response.content.index(link % p1.id) < response.content.index(link % p2.id) - ) + self.assertContentBefore(response, link % p3.id, link % p1.id) + self.assertContentBefore(response, link % p1.id, link % p2.id) # Sort by gender descending, name response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '-2.1'}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % p2.id) < response.content.index(link % p3.id) and - response.content.index(link % p3.id) < response.content.index(link % p1.id) - ) + self.assertContentBefore(response, link % p2.id, link % p3.id) + self.assertContentBefore(response, link % p3.id, link % p1.id) def testChangeListSortingPreserveQuerySetOrdering(self): """ @@ -291,11 +289,8 @@ class AdminViewBasicTest(TestCase): # This hard-codes the URL because it'll fail if it runs against the # 'admin2' custom admin (which doesn't have the Person model). response = self.client.get('/test_admin/admin/admin_views/person/', {}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % p3.id) < response.content.index(link % p2.id) and - response.content.index(link % p2.id) < response.content.index(link % p1.id) - ) + self.assertContentBefore(response, link % p3.id, link % p2.id) + self.assertContentBefore(response, link % p2.id, link % p1.id) def testChangeListSortingModelMeta(self): # Test ordering on Model Meta is respected @@ -305,17 +300,11 @@ class AdminViewBasicTest(TestCase): link = 'Horizontal' in response.content and - 'Vertical' in response.content, - "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group." - ) + fail_msg = "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group." + self.assertContains(response, 'Horizontal', msg_prefix=fail_msg, html=True) + self.assertContains(response, 'Vertical', msg_prefix=fail_msg, html=True) def testNamedGroupFieldChoicesFilter(self): """ @@ -493,13 +468,12 @@ class AdminViewBasicTest(TestCase): been used in the choices option of a model field. """ response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit) - self.assertEqual(response.status_code, 200) + fail_msg = "Changelist filter isn't showing options contained inside a model field 'choices' option named group." self.assertContains(response, '
    ') - self.assertTrue( - 'Horizontal' in response.content and - 'Vertical' in response.content, - "Changelist filter isn't showing options contained inside a model field 'choices' option named group." - ) + self.assertContains(response, + 'Horizontal', msg_prefix=fail_msg, html=True) + self.assertContains(response, + 'Vertical', msg_prefix=fail_msg, html=True) def testChangeListNullBooleanDisplay(self): Post.objects.create(public=None) @@ -590,8 +564,8 @@ class AdminViewBasicTest(TestCase): user.save() response = self.client.get('/test_admin/admin/') - self.assertFalse(reverse('admin:password_change') in response.content, - msg='The "change password" link should not be displayed if a user does not have a usable password.') + self.assertNotContains(response, reverse('admin:password_change'), + msg_prefix='The "change password" link should not be displayed if a user does not have a usable password.') def test_change_view_with_show_delete_extra_context(self): """ @@ -618,7 +592,7 @@ class AdminViewFormUrlTest(TestCase): def testChangeFormUrlHasCorrectValue(self): """ - Tests whether change_view has form_url in request.context + Tests whether change_view has form_url in response.context """ response = self.client.get('/test_admin/%s/admin_views/section/1/' % self.urlbit) self.assertTrue('form_url' in response.context, msg='form_url not present in response.context') @@ -755,34 +729,34 @@ class CustomModelAdminTest(AdminViewBasicTest): def testCustomAdminSiteLoginTemplate(self): self.client.logout() - request = self.client.get('/test_admin/admin2/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/login.html') - self.assertTrue('Hello from a custom login template' in request.content) + response = self.client.get('/test_admin/admin2/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/login.html') + self.assertContains(response, 'Hello from a custom login template') def testCustomAdminSiteLogoutTemplate(self): - request = self.client.get('/test_admin/admin2/logout/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/logout.html') - self.assertTrue('Hello from a custom logout template' in request.content) + response = self.client.get('/test_admin/admin2/logout/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/logout.html') + self.assertContains(response, 'Hello from a custom logout template') def testCustomAdminSiteIndexViewAndTemplate(self): - request = self.client.get('/test_admin/admin2/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/index.html') - self.assertTrue('Hello from a custom index template *bar*' in request.content) + response = self.client.get('/test_admin/admin2/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/index.html') + self.assertContains(response, 'Hello from a custom index template *bar*') def testCustomAdminSitePasswordChangeTemplate(self): - request = self.client.get('/test_admin/admin2/password_change/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/password_change_form.html') - self.assertTrue('Hello from a custom password change form template' in request.content) + response = self.client.get('/test_admin/admin2/password_change/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/password_change_form.html') + self.assertContains(response, 'Hello from a custom password change form template') def testCustomAdminSitePasswordChangeDoneTemplate(self): - request = self.client.get('/test_admin/admin2/password_change/done/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/password_change_done.html') - self.assertTrue('Hello from a custom password change done template' in request.content) + response = self.client.get('/test_admin/admin2/password_change/done/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/password_change_done.html') + self.assertContains(response, 'Hello from a custom password change done template') def testCustomAdminSiteView(self): self.client.login(username='super', password='secret') @@ -885,16 +859,16 @@ class AdminViewPermissionsTest(TestCase): a 200 status code. """ # Super User - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.super_login) self.assertRedirects(login, '/test_admin/admin/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Test if user enters e-mail address - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.super_email_login) self.assertContains(login, "Your e-mail address is not your username") # only correct passwords get a username hint @@ -907,47 +881,47 @@ class AdminViewPermissionsTest(TestCase): self.assertContains(login, ERROR_MESSAGE) # Add User - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.adduser_login) self.assertRedirects(login, '/test_admin/admin/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Change User - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.changeuser_login) self.assertRedirects(login, '/test_admin/admin/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Delete User - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.deleteuser_login) self.assertRedirects(login, '/test_admin/admin/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Regular User should not be able to login. - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.joepublic_login) self.assertEqual(login.status_code, 200) self.assertContains(login, ERROR_MESSAGE) # Requests without username should not return 500 errors. - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.no_username_login) self.assertEqual(login.status_code, 200) form = login.context[0].get('form') self.assertEqual(form.errors['username'][0], 'This field is required.') def testLoginSuccessfullyRedirectsToOriginalUrl(self): - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) query_string = 'the-answer=42' redirect_url = '/test_admin/admin/?%s' % query_string new_next = {REDIRECT_FIELD_NAME: redirect_url} @@ -967,8 +941,8 @@ class AdminViewPermissionsTest(TestCase): self.client.post('/test_admin/admin/', self.changeuser_login) # make sure the view removes test cookie self.assertEqual(self.client.session.test_cookie_worked(), False) - request = self.client.get('/test_admin/admin/admin_views/article/add/') - self.assertEqual(request.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/article/add/') + self.assertEqual(response.status_code, 403) # Try POST just to make sure post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.assertEqual(post.status_code, 403) @@ -979,10 +953,9 @@ class AdminViewPermissionsTest(TestCase): self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.adduser_login) addpage = self.client.get('/test_admin/admin/admin_views/article/add/') - self.assertEqual(addpage.status_code, 200) change_list_link = '› Articles' - self.assertFalse(change_list_link in addpage.content, - 'User restricted to add permission is given link to change list view in breadcrumbs.') + self.assertNotContains(addpage, change_list_link, + msg_prefix='User restricted to add permission is given link to change list view in breadcrumbs.') post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.assertRedirects(post, '/test_admin/admin/') self.assertEqual(Article.objects.all().count(), 4) @@ -994,9 +967,8 @@ class AdminViewPermissionsTest(TestCase): self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.super_login) addpage = self.client.get('/test_admin/admin/admin_views/article/add/') - self.assertEqual(addpage.status_code, 200) - self.assertFalse(change_list_link not in addpage.content, - 'Unrestricted user is not given link to change list view in breadcrumbs.') + self.assertContains(addpage, change_list_link, + msg_prefix='Unrestricted user is not given link to change list view in breadcrumbs.') post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.assertRedirects(post, '/test_admin/admin/admin_views/article/') self.assertEqual(Article.objects.all().count(), 5) @@ -1022,10 +994,10 @@ class AdminViewPermissionsTest(TestCase): # add user shoud not be able to view the list of article or change any of them self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.adduser_login) - request = self.client.get('/test_admin/admin/admin_views/article/') - self.assertEqual(request.status_code, 403) - request = self.client.get('/test_admin/admin/admin_views/article/1/') - self.assertEqual(request.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/article/') + self.assertEqual(response.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/article/1/') + self.assertEqual(response.status_code, 403) post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict) self.assertEqual(post.status_code, 403) self.client.get('/test_admin/admin/logout/') @@ -1033,10 +1005,10 @@ class AdminViewPermissionsTest(TestCase): # change user can view all items and edit them self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.changeuser_login) - request = self.client.get('/test_admin/admin/admin_views/article/') - self.assertEqual(request.status_code, 200) - request = self.client.get('/test_admin/admin/admin_views/article/1/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/admin_views/article/') + self.assertEqual(response.status_code, 200) + response = self.client.get('/test_admin/admin/admin_views/article/1/') + self.assertEqual(response.status_code, 200) post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict) self.assertRedirects(post, '/test_admin/admin/admin_views/article/') self.assertEqual(Article.objects.get(pk=1).content, '

    edited article

    ') @@ -1058,33 +1030,33 @@ class AdminViewPermissionsTest(TestCase): RowLevelChangePermissionModel.objects.create(id=2, name="even id") for login_dict in [self.super_login, self.changeuser_login, self.adduser_login, self.deleteuser_login]: self.client.post('/test_admin/admin/', login_dict) - request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') - self.assertEqual(request.status_code, 403) - request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) + response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') + self.assertEqual(response.status_code, 403) + response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') - self.assertEqual(request.status_code, 403) - request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') - self.assertEqual(request.status_code, 200) - request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed'}) + self.assertEqual(response.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') + self.assertEqual(response.status_code, 200) + response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed'}) self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') - self.assertRedirects(request, '/test_admin/admin/') + self.assertRedirects(response, '/test_admin/admin/') self.client.get('/test_admin/admin/logout/') for login_dict in [self.joepublic_login, self.no_username_login]: self.client.post('/test_admin/admin/', login_dict) - request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') - self.assertEqual(request.status_code, 200) - self.assertContains(request, 'login-form') - request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) + response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'login-form') + response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') - self.assertEqual(request.status_code, 200) - self.assertContains(request, 'login-form') - request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') - self.assertEqual(request.status_code, 200) - self.assertContains(request, 'login-form') - request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed again'}) + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'login-form') + response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'login-form') + response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed again'}) self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') - self.assertEqual(request.status_code, 200) - self.assertContains(request, 'login-form') + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'login-form') self.client.get('/test_admin/admin/logout/') def testConditionallyShowAddSectionLink(self): @@ -1114,14 +1086,13 @@ class AdminViewPermissionsTest(TestCase): self.client.post('/test_admin/admin/', self.super_login) # Test custom change list template with custom extra context - request = self.client.get('/test_admin/admin/admin_views/customarticle/') - self.assertEqual(request.status_code, 200) - self.assertTrue("var hello = 'Hello!';" in request.content) - self.assertTemplateUsed(request, 'custom_admin/change_list.html') + response = self.client.get('/test_admin/admin/admin_views/customarticle/') + self.assertContains(response, "var hello = 'Hello!';") + self.assertTemplateUsed(response, 'custom_admin/change_list.html') # Test custom add form template - request = self.client.get('/test_admin/admin/admin_views/customarticle/add/') - self.assertTemplateUsed(request, 'custom_admin/add_form.html') + response = self.client.get('/test_admin/admin/admin_views/customarticle/add/') + self.assertTemplateUsed(response, 'custom_admin/add_form.html') # Add an article so we can test delete, change, and history views post = self.client.post('/test_admin/admin/admin_views/customarticle/add/', { @@ -1135,18 +1106,18 @@ class AdminViewPermissionsTest(TestCase): # Test custom delete, change, and object history templates # Test custom change form template - request = self.client.get('/test_admin/admin/admin_views/customarticle/%d/' % article_pk) - self.assertTemplateUsed(request, 'custom_admin/change_form.html') - request = self.client.get('/test_admin/admin/admin_views/customarticle/%d/delete/' % article_pk) - self.assertTemplateUsed(request, 'custom_admin/delete_confirmation.html') - request = self.client.post('/test_admin/admin/admin_views/customarticle/', data={ + response = self.client.get('/test_admin/admin/admin_views/customarticle/%d/' % article_pk) + self.assertTemplateUsed(response, 'custom_admin/change_form.html') + response = self.client.get('/test_admin/admin/admin_views/customarticle/%d/delete/' % article_pk) + self.assertTemplateUsed(response, 'custom_admin/delete_confirmation.html') + response = self.client.post('/test_admin/admin/admin_views/customarticle/', data={ 'index': 0, 'action': ['delete_selected'], '_selected_action': ['1'], }) - self.assertTemplateUsed(request, 'custom_admin/delete_selected_confirmation.html') - request = self.client.get('/test_admin/admin/admin_views/customarticle/%d/history/' % article_pk) - self.assertTemplateUsed(request, 'custom_admin/object_history.html') + self.assertTemplateUsed(response, 'custom_admin/delete_selected_confirmation.html') + response = self.client.get('/test_admin/admin/admin_views/customarticle/%d/history/' % article_pk) + self.assertTemplateUsed(response, 'custom_admin/object_history.html') self.client.get('/test_admin/admin/logout/') @@ -1158,8 +1129,8 @@ class AdminViewPermissionsTest(TestCase): # add user shoud not be able to delete articles self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.adduser_login) - request = self.client.get('/test_admin/admin/admin_views/article/1/delete/') - self.assertEqual(request.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/article/1/delete/') + self.assertEqual(response.status_code, 403) post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict) self.assertEqual(post.status_code, 403) self.assertEqual(Article.objects.all().count(), 3) @@ -1246,9 +1217,9 @@ class AdminViewDeletedObjectsTest(TestCase): Objects should be nested to display the relationships that cause them to be scheduled for deletion. """ - pattern = re.compile(r"""
  • Plot: World Domination\s*