From eff6ba2f64ea5426502daadb04fbe1e85ace068a Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 10 Aug 2012 16:19:20 -0400 Subject: Fixed #17016 - Added examples for file uploads in views. Thanks Tim Saylor for the draft patch and Aymeric Augustin and Claude Paroz for feedback. --- docs/topics/http/file-uploads.txt | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'docs') 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 ======================== -- cgit v1.3 From cb38fd9632f314bd78324083005dc8df55200390 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 10 Aug 2012 16:12:45 -0400 Subject: Fixed #17680 - Clarified when logging is configured. --- docs/topics/logging.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'docs') 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 -- cgit v1.3 From 060ac8e7115b51374de7d5e34c784d9df5764d0b Mon Sep 17 00:00:00 2001 From: Issac Kelly Date: Fri, 10 Aug 2012 23:07:15 -0700 Subject: 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 ++++++++++++++-------- docs/ref/class-based-views/generic-date-based.txt | 24 ++++++++++++++- docs/ref/class-based-views/generic-display.txt | 10 +++++-- docs/ref/class-based-views/generic-editing.txt | 27 ++++++++++++----- docs/ref/class-based-views/index.txt | 2 +- docs/ref/class-based-views/mixins-date-based.txt | 18 +++++++++++ docs/ref/class-based-views/mixins-editing.txt | 14 +++++++-- .../class-based-views/mixins-multiple-object.txt | 6 ++++ docs/ref/class-based-views/mixins-simple.txt | 6 ++++ .../ref/class-based-views/mixins-single-object.txt | 6 ++++ 10 files changed, 121 insertions(+), 27 deletions(-) (limited to 'docs') 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 -- cgit v1.3 From 4d9e4c64f1330fbdfe6a7931879deebf7154f403 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sat, 11 Aug 2012 21:46:36 +0200 Subject: Fixed #18698 -- Configure latex to support '≥' in the docs. 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(-) (limited to 'docs') 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 = [] -- cgit v1.3 From 140179c77046f45136821db2f902f2da64ec4bb6 Mon Sep 17 00:00:00 2001 From: Martijn Vermaat Date: Sun, 12 Aug 2012 00:01:11 +0300 Subject: Fix link to Gunicorn website in deployment howto. --- docs/howto/deployment/wsgi/gunicorn.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') 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 =================== -- cgit v1.3 From 9e7b5ba50fd9c23876b0a17a03e6875a6899b7c6 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Sun, 12 Aug 2012 12:43:05 +0200 Subject: Removed missplaced label in the docs. --- docs/internals/contributing/bugs-and-features.txt | 2 -- 1 file changed, 2 deletions(-) (limited to 'docs') 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 ------------------------------------------ -- cgit v1.3 From a0a0203a392f67832ba7a8a2f099e70d7db2d19e Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 11 Aug 2012 15:34:51 +0200 Subject: [py3] Added python_2_unicode_compatible decorator. --- django/utils/encoding.py | 13 +++++++++++++ docs/ref/utils.txt | 8 ++++++++ 2 files changed, 21 insertions(+) (limited to 'docs') 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 -- cgit v1.3 From 4e68e861533846010e372ecf58e3cd0439081754 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 12 Aug 2012 14:39:48 +0200 Subject: [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(+) (limited to 'docs') 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. -- cgit v1.3 From 031896c5101de83bca65e872fb4a91c15f55a42e Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 12 Aug 2012 15:22:33 +0200 Subject: [py3] Explained @python_2_unicode_compatible usage --- docs/topics/python3.txt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'docs') 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') -- cgit v1.3 From 99321e30cebbffeafc6ae19f4f92a0a665cbf19b Mon Sep 17 00:00:00 2001 From: Andrei Antoukh Date: Sun, 12 Aug 2012 22:17:54 +0300 Subject: 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 +++++++++++++++++++++++ tests/regressiontests/multiple_database/tests.py | 15 ++++ 7 files changed, 161 insertions(+) (limited to 'docs') 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 -- cgit v1.3 From d2975718fe0585cad1ed1462cc4d65373a6c7bb0 Mon Sep 17 00:00:00 2001 From: Florian Apolloner Date: Mon, 13 Aug 2012 16:54:13 +0200 Subject: Consistenly use _ as alias for ugettext_lazy in the i18n docs. --- docs/topics/i18n/translation.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'docs') 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.3 From 383da137851bd5fbe4f5f22e13d6237f3532a5bb Mon Sep 17 00:00:00 2001 From: Tom Mortimer-Jones Date: Wed, 15 Aug 2012 19:41:16 +0200 Subject: Fixed comment to match the code --- docs/ref/contrib/admin/index.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 4d39981a4d..7ca102c529 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -623,7 +623,7 @@ subclass:: provided in the query string and retrievable via `self.value()`. """ - # Compare the requested value (either '80s' or 'other') + # Compare the requested value (either '80s' or '90s') # to decide how to filter the queryset. if self.value() == '80s': return queryset.filter(birthday__gte=date(1980, 1, 1), -- cgit v1.3 From e38112d882a8aec0aaf6d52ab6d07fa1a408a3aa Mon Sep 17 00:00:00 2001 From: Piet Delport Date: Mon, 13 Aug 2012 14:10:40 +0200 Subject: Fixed #18759 -- updated SECRET_KEY documentation Document SECRET_KEY becoming required in 1.5. Also expand the description slightly, and add a more prominent warning about the security implications of running with an exposed SECRET_KEY. --- docs/ref/settings.txt | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 531ff33da2..f319a9bee9 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1537,9 +1537,23 @@ SECRET_KEY Default: ``''`` (Empty string) -A secret key for this particular Django installation. Used to provide a seed in -secret-key hashing algorithms. Set this to a random string -- the longer, the -better. ``django-admin.py startproject`` creates one automatically. +A secret key for a particular Django installation. This is used to provide +:doc:`cryptographic signing `, and should be set to a unique, +unpredictable value. + +:djadmin:`django-admin.py startproject ` automatically adds a +randomly-generated ``SECRET_KEY`` to each new project. + +.. warning:: + + **Keep this value secret.** + + Running Django with a known :setting:`SECRET_KEY` defeats many of Django's + security protections, and can lead to privilege escalation and remote code + execution vulnerabilities. + +.. versionchanged:: 1.5 + Django will now refuse to start if :setting:`SECRET_KEY` is not set. .. setting:: SECURE_PROXY_SSL_HEADER -- cgit v1.3 From d1d514af044209a6af790ba012c4637232c5335d Mon Sep 17 00:00:00 2001 From: Simon Meers Date: Thu, 16 Aug 2012 10:13:28 +1000 Subject: Marked up a few raw values in the settings documentation. --- docs/ref/settings.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'docs') diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index f319a9bee9..0eac229c16 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -747,14 +747,15 @@ Did you catch that? NEVER deploy a site into production with :setting:`DEBUG` turned on. One of the main features of debug mode is the display of detailed error pages. -If your app raises an exception when ``DEBUG`` is ``True``, Django will display -a detailed traceback, including a lot of metadata about your environment, such -as all the currently defined Django settings (from ``settings.py``). +If your app raises an exception when :setting:`DEBUG` is ``True``, Django will +display a detailed traceback, including a lot of metadata about your +environment, such as all the currently defined Django settings (from +``settings.py``). As a security measure, Django will *not* include settings that might be -sensitive (or offensive), such as ``SECRET_KEY`` or ``PROFANITIES_LIST``. -Specifically, it will exclude any setting whose name includes any of the -following: +sensitive (or offensive), such as :setting:`SECRET_KEY` or +:setting:`PROFANITIES_LIST`. Specifically, it will exclude any setting whose +name includes any of the following: * API * KEY @@ -1356,7 +1357,7 @@ MANAGERS Default: ``()`` (Empty tuple) A tuple in the same format as :setting:`ADMINS` that specifies who should get -broken-link notifications when ``SEND_BROKEN_LINK_EMAILS=True``. +broken-link notifications when :setting:`SEND_BROKEN_LINK_EMAILS` is ``True``. .. setting:: MEDIA_ROOT -- cgit v1.3 From 2079b730f139685bcedf0c92d5ed9a3f64b51e9f Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 16 Aug 2012 16:05:41 -0400 Subject: Fixed #18223 - Corrected default transaction behavior in postgresql docs. Thanks philipn for the report and mateusgondim for the patch. --- docs/ref/databases.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index 92b5665bea..3e256e9d9e 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -56,10 +56,10 @@ will do some additional queries to set these parameters. Transaction handling --------------------- -:doc:`By default `, Django starts a transaction when a -database connection is first used and commits the result at the end of the -request/response handling. The PostgreSQL backends normally operate the same -as any other Django backend in this respect. +:doc:`By default `, Django runs with an open +transaction which it commits automatically when any built-in, data-altering +model function is called. The PostgreSQL backends normally operate the same as +any other Django backend in this respect. .. _postgresql-autocommit-mode: -- cgit v1.3 From b1f18e95a5481e582f52a42807506369b73d8c6a Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 16 Aug 2012 16:13:45 -0400 Subject: Fixed #17183 - Added a note regarding LocaleMiddleware at the top of the i18n docs. Thanks krzysiumed for the patch. --- docs/topics/i18n/translation.txt | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'docs') diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 87fcf07eac..79bd1a6a6a 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -37,6 +37,13 @@ from your :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting. controls if Django should implement format localization. See :doc:`/topics/i18n/formatting` for more details. +.. note:: + + Make sure you've activated translation for your project (the fastest way is + to check if :setting:`MIDDLEWARE_CLASSES` includes + :mod:`django.middleware.locale.LocaleMiddleware`). If you haven't yet, + see :ref:`how-django-discovers-language-preference`. + Internationalization: in Python code ==================================== -- cgit v1.3 From 2a36a1a07145860bad45d9c416281313ff53b910 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Fri, 17 Aug 2012 11:06:38 -0400 Subject: Fixed #18696 - Clarified WizardView heading; thanks sergzach. --- docs/ref/contrib/formtools/form-wizard.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/ref/contrib/formtools/form-wizard.txt b/docs/ref/contrib/formtools/form-wizard.txt index 7d229a5d66..3cc8c7aed4 100644 --- a/docs/ref/contrib/formtools/form-wizard.txt +++ b/docs/ref/contrib/formtools/form-wizard.txt @@ -86,8 +86,8 @@ the message itself. Here's what the :file:`forms.py` might look like:: section :ref:`Handling files ` below to learn more about what to do. -Creating a ``WizardView`` class -------------------------------- +Creating a ``WizardView`` subclass +---------------------------------- The next step is to create a :class:`django.contrib.formtools.wizard.views.WizardView` subclass. You can -- cgit v1.3 From e437dd1d6be5fdc3a7acf7abcf97b922e628313b Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Fri, 17 Aug 2012 17:29:46 -0700 Subject: Update docs/topics/class-based-views/index.txt View class does not have a render_to_response method - so does not make sense for this mixin --- docs/topics/class-based-views/index.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/topics/class-based-views/index.txt b/docs/topics/class-based-views/index.txt index 1ac70e6938..c2900ecc01 100644 --- a/docs/topics/class-based-views/index.txt +++ b/docs/topics/class-based-views/index.txt @@ -119,11 +119,11 @@ For example, a simple JSON mixin might look something like this:: # -- can be serialized as JSON. return json.dumps(context) -Now we mix this into the base view:: +Now we mix this into the base TemplateView:: - from django.views.generic import View + from django.views.generic import TemplateView - class JSONView(JSONResponseMixin, View): + class JSONView(JSONResponseMixin, TemplateView): pass Equally we could use our mixin with one of the generic views. We can make our -- cgit v1.3 From 4da1d0fd65e01abe013e0d5a9174b81c6bbfa677 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 18 Aug 2012 09:55:13 +0200 Subject: Added a warning about the {% url %} syntax change at the point where it bites most beginners. Refs #18787, #18762, #18756, #18723, #18705, #18689 and several duplicates. --- docs/intro/tutorial03.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'docs') diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index d15b2f43ae..6d4fb7eef1 100644 --- a/docs/intro/tutorial03.txt +++ b/docs/intro/tutorial03.txt @@ -550,5 +550,16 @@ with the :ttag:`url` template tag:
  • {{ poll.question }}
  • +.. note:: + + If ``{% url 'polls.views.detail' poll.id %}`` (with quotes) doesn't work, + but ``{% url polls.views.detail poll.id %}`` (without quotes) does, that + means you're using a version of Django ≤ 1.4. In this case, add the + following declaration at the top of your template: + + .. code-block:: html+django + + {% load url from future %} + When you're comfortable with writing views, read :doc:`part 4 of this tutorial ` to learn about simple form processing and generic views. -- cgit v1.3 From 4c1286cf78d03fb7df03774f5f4beb9756ec29c0 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 18 Aug 2012 10:56:56 +0200 Subject: [py3] Added compatibility import of thread/_thread This commit fixes the auto-reload of the development server. I should have done that change in ca07fda2. --- django/db/backends/__init__.py | 2 +- django/utils/autoreload.py | 2 +- django/utils/six.py | 2 ++ docs/topics/python3.txt | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index c7682a546b..023a4faba0 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -1,7 +1,7 @@ from django.db.utils import DatabaseError try: - import thread + from django.utils.six.moves import _thread as thread except ImportError: from django.utils.six.moves import _dummy_thread as thread from contextlib import contextmanager diff --git a/django/utils/autoreload.py b/django/utils/autoreload.py index 9032cd024d..2daafedd85 100644 --- a/django/utils/autoreload.py +++ b/django/utils/autoreload.py @@ -31,7 +31,7 @@ import os, sys, time, signal try: - import thread + from django.utils.six.moves import _thread as thread except ImportError: from django.utils.six.moves import _dummy_thread as thread diff --git a/django/utils/six.py b/django/utils/six.py index ceb5d70e58..767fe0bbe9 100644 --- a/django/utils/six.py +++ b/django/utils/six.py @@ -365,4 +365,6 @@ def iterlists(d): """Return an iterator over the values of a MultiValueDict.""" return getattr(d, _iterlists)() + add_move(MovedModule("_dummy_thread", "dummy_thread")) +add_move(MovedModule("_thread", "thread")) diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt index 742731e1a7..32463f2e51 100644 --- a/docs/topics/python3.txt +++ b/docs/topics/python3.txt @@ -122,8 +122,8 @@ Moved modules Some modules were renamed in Python 3. The :mod:`django.utils.six.moves ` module provides a compatible location to import them. -In addition to six' defaults, Django's version provides ``dummy_thread`` as -``_dummy_thread``. +In addition to six' defaults, Django's version provides ``thread`` as +``_thread`` and ``dummy_thread`` as ``_dummy_thread``. PY3 --- -- cgit v1.3 From 547b181046539f548839e42544521503fbe4d821 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 18 Aug 2012 16:04:06 +0200 Subject: [py3] Ported django.utils.safestring. Backwards compatibility aliases were created under Python 2. --- django/db/backends/mysql/base.py | 8 +-- django/db/backends/postgresql_psycopg2/base.py | 6 +-- django/db/backends/sqlite3/base.py | 4 +- django/utils/encoding.py | 4 +- django/utils/safestring.py | 74 +++++++++++++++----------- docs/howto/custom-template-tags.txt | 6 +-- docs/ref/utils.txt | 22 ++++++-- tests/regressiontests/i18n/tests.py | 12 ++--- 8 files changed, 82 insertions(+), 54 deletions(-) (limited to 'docs') diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 2222f89cf0..cec3b04f7e 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -38,7 +38,7 @@ from django.db.backends.mysql.creation import DatabaseCreation from django.db.backends.mysql.introspection import DatabaseIntrospection from django.db.backends.mysql.validation import DatabaseValidation from django.utils.functional import cached_property -from django.utils.safestring import SafeString, SafeUnicode +from django.utils.safestring import SafeBytes, SafeText from django.utils import six from django.utils import timezone @@ -75,7 +75,7 @@ def adapt_datetime_with_timezone_support(value, conv): # MySQLdb-1.2.1 returns TIME columns as timedelta -- they are more like # timedelta in terms of actual behavior as they are signed and include days -- # and Django expects time, so we still need to override that. We also need to -# add special handling for SafeUnicode and SafeString as MySQLdb's type +# add special handling for SafeText and SafeBytes as MySQLdb's type # checking is too tight to catch those (see Django ticket #6052). # Finally, MySQLdb always returns naive datetime objects. However, when # timezone support is active, Django expects timezone-aware datetime objects. @@ -402,8 +402,8 @@ class DatabaseWrapper(BaseDatabaseWrapper): kwargs['client_flag'] = CLIENT.FOUND_ROWS kwargs.update(settings_dict['OPTIONS']) self.connection = Database.connect(**kwargs) - self.connection.encoders[SafeUnicode] = self.connection.encoders[six.text_type] - self.connection.encoders[SafeString] = self.connection.encoders[bytes] + self.connection.encoders[SafeText] = self.connection.encoders[six.text_type] + self.connection.encoders[SafeBytes] = self.connection.encoders[bytes] connection_created.send(sender=self.__class__, connection=self) cursor = self.connection.cursor() if new_connection: diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index d553594ec1..f6f534da8c 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -14,7 +14,7 @@ from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation from django.db.backends.postgresql_psycopg2.version import get_version from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection from django.utils.log import getLogger -from django.utils.safestring import SafeUnicode, SafeString +from django.utils.safestring import SafeText, SafeBytes from django.utils import six from django.utils.timezone import utc @@ -29,8 +29,8 @@ DatabaseError = Database.DatabaseError IntegrityError = Database.IntegrityError psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) -psycopg2.extensions.register_adapter(SafeString, psycopg2.extensions.QuotedString) -psycopg2.extensions.register_adapter(SafeUnicode, psycopg2.extensions.QuotedString) +psycopg2.extensions.register_adapter(SafeBytes, psycopg2.extensions.QuotedString) +psycopg2.extensions.register_adapter(SafeText, psycopg2.extensions.QuotedString) logger = getLogger('django.db.backends') diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 775ca20e45..31a16b6a2b 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -20,7 +20,7 @@ from django.db.backends.sqlite3.creation import DatabaseCreation from django.db.backends.sqlite3.introspection import DatabaseIntrospection from django.utils.dateparse import parse_date, parse_datetime, parse_time from django.utils.functional import cached_property -from django.utils.safestring import SafeString +from django.utils.safestring import SafeBytes from django.utils import six from django.utils import timezone @@ -80,7 +80,7 @@ if Database.version_info >= (2, 4, 1): # slow-down, this adapter is only registered for sqlite3 versions # needing it (Python 2.6 and up). Database.register_adapter(str, lambda s: s.decode('utf-8')) - Database.register_adapter(SafeString, lambda s: s.decode('utf-8')) + Database.register_adapter(SafeBytes, lambda s: s.decode('utf-8')) class DatabaseFeatures(BaseDatabaseFeatures): # SQLite cannot handle us only partially reading from a cursor's result set diff --git a/django/utils/encoding.py b/django/utils/encoding.py index 7b80f13496..3ac2c508b6 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -119,8 +119,8 @@ def force_text(s, encoding='utf-8', strings_only=False, errors='strict'): errors) for arg in s]) else: # Note: We use .decode() here, instead of six.text_type(s, encoding, - # errors), so that if s is a SafeString, it ends up being a - # SafeUnicode at the end. + # errors), so that if s is a SafeBytes, it ends up being a + # SafeText at the end. s = s.decode(encoding, errors) except UnicodeDecodeError as e: if not isinstance(s, Exception): diff --git a/django/utils/safestring.py b/django/utils/safestring.py index bfaefd07ee..07e0bf4cea 100644 --- a/django/utils/safestring.py +++ b/django/utils/safestring.py @@ -10,36 +10,43 @@ from django.utils import six class EscapeData(object): pass -class EscapeString(bytes, EscapeData): +class EscapeBytes(bytes, EscapeData): """ - A string that should be HTML-escaped when output. + A byte string that should be HTML-escaped when output. """ pass -class EscapeUnicode(six.text_type, EscapeData): +class EscapeText(six.text_type, EscapeData): """ - A unicode object that should be HTML-escaped when output. + A unicode string object that should be HTML-escaped when output. """ pass +if six.PY3: + EscapeString = EscapeText +else: + EscapeString = EscapeBytes + # backwards compatibility for Python 2 + EscapeUnicode = EscapeText + class SafeData(object): pass -class SafeString(bytes, SafeData): +class SafeBytes(bytes, SafeData): """ - A string subclass that has been specifically marked as "safe" (requires no + A bytes subclass that has been specifically marked as "safe" (requires no further escaping) for HTML output purposes. """ def __add__(self, rhs): """ - Concatenating a safe string with another safe string or safe unicode - object is safe. Otherwise, the result is no longer safe. + Concatenating a safe byte string with another safe byte string or safe + unicode string is safe. Otherwise, the result is no longer safe. """ - t = super(SafeString, self).__add__(rhs) - if isinstance(rhs, SafeUnicode): - return SafeUnicode(t) - elif isinstance(rhs, SafeString): - return SafeString(t) + t = super(SafeBytes, self).__add__(rhs) + if isinstance(rhs, SafeText): + return SafeText(t) + elif isinstance(rhs, SafeBytes): + return SafeBytes(t) return t def _proxy_method(self, *args, **kwargs): @@ -51,25 +58,25 @@ class SafeString(bytes, SafeData): method = kwargs.pop('method') data = method(self, *args, **kwargs) if isinstance(data, bytes): - return SafeString(data) + return SafeBytes(data) else: - return SafeUnicode(data) + return SafeText(data) decode = curry(_proxy_method, method=bytes.decode) -class SafeUnicode(six.text_type, SafeData): +class SafeText(six.text_type, SafeData): """ - A unicode subclass that has been specifically marked as "safe" for HTML - output purposes. + A unicode (Python 2) / str (Python 3) subclass that has been specifically + marked as "safe" for HTML output purposes. """ def __add__(self, rhs): """ - Concatenating a safe unicode object with another safe string or safe - unicode object is safe. Otherwise, the result is no longer safe. + Concatenating a safe unicode string with another safe byte string or + safe unicode string is safe. Otherwise, the result is no longer safe. """ - t = super(SafeUnicode, self).__add__(rhs) + t = super(SafeText, self).__add__(rhs) if isinstance(rhs, SafeData): - return SafeUnicode(t) + return SafeText(t) return t def _proxy_method(self, *args, **kwargs): @@ -81,12 +88,19 @@ class SafeUnicode(six.text_type, SafeData): method = kwargs.pop('method') data = method(self, *args, **kwargs) if isinstance(data, bytes): - return SafeString(data) + return SafeBytes(data) else: - return SafeUnicode(data) + return SafeText(data) encode = curry(_proxy_method, method=six.text_type.encode) +if six.PY3: + SafeString = SafeText +else: + SafeString = SafeBytes + # backwards compatibility for Python 2 + SafeUnicode = SafeText + def mark_safe(s): """ Explicitly mark a string as safe for (HTML) output purposes. The returned @@ -97,10 +111,10 @@ def mark_safe(s): if isinstance(s, SafeData): return s if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_bytes): - return SafeString(s) + return SafeBytes(s) if isinstance(s, (six.text_type, Promise)): - return SafeUnicode(s) - return SafeString(bytes(s)) + return SafeText(s) + return SafeString(str(s)) def mark_for_escaping(s): """ @@ -113,8 +127,8 @@ def mark_for_escaping(s): if isinstance(s, (SafeData, EscapeData)): return s if isinstance(s, bytes) or (isinstance(s, Promise) and s._delegate_bytes): - return EscapeString(s) + return EscapeBytes(s) if isinstance(s, (six.text_type, Promise)): - return EscapeUnicode(s) - return EscapeString(bytes(s)) + return EscapeText(s) + return EscapeBytes(bytes(s)) diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt index 054c831fad..5b27af82d6 100644 --- a/docs/howto/custom-template-tags.txt +++ b/docs/howto/custom-template-tags.txt @@ -189,7 +189,7 @@ passed around inside the template code: They're commonly used for output that contains raw HTML that is intended to be interpreted as-is on the client side. - Internally, these strings are of type ``SafeString`` or ``SafeUnicode``. + Internally, these strings are of type ``SafeBytes`` or ``SafeText``. They share a common base class of ``SafeData``, so you can test for them using code like: @@ -204,8 +204,8 @@ passed around inside the template code: not. These strings are only escaped once, however, even if auto-escaping applies. - Internally, these strings are of type ``EscapeString`` or - ``EscapeUnicode``. Generally you don't have to worry about these; they + Internally, these strings are of type ``EscapeBytes`` or + ``EscapeText``. Generally you don't have to worry about these; they exist for the implementation of the :tfilter:`escape` filter. Template filter code falls into one of two situations: diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 97643c29e9..20775fcc81 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -560,15 +560,29 @@ string" means that the producer of the string has already turned characters that should not be interpreted by the HTML engine (e.g. '<') into the appropriate entities. +.. class:: SafeBytes + + .. versionadded:: 1.5 + + A :class:`bytes` subclass that has been specifically marked as "safe" + (requires no further escaping) for HTML output purposes. + .. class:: SafeString - A string subclass that has been specifically marked as "safe" (requires no - further escaping) for HTML output purposes. + A :class:`str` subclass that has been specifically marked as "safe" + (requires no further escaping) for HTML output purposes. This is + :class:`SafeBytes` on Python 2 and :class:`SafeText` on Python 3. + +.. class:: SafeText + + .. versionadded:: 1.5 + + A :class:`str` (in Python 3) or :class:`unicode` (in Python 2) subclass + that has been specifically marked as "safe" for HTML output purposes. .. class:: SafeUnicode - A unicode subclass that has been specifically marked as "safe" for HTML - output purposes. + Historical name of :class:`SafeText`. Only available under Python 2. .. function:: mark_safe(s) diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 1a8ff25ff3..c13c1c6f86 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -18,7 +18,7 @@ from django.utils.formats import (get_format, date_format, time_format, number_format) from django.utils.importlib import import_module from django.utils.numberformat import format as nformat -from django.utils.safestring import mark_safe, SafeString, SafeUnicode +from django.utils.safestring import mark_safe, SafeBytes, SafeString, SafeText from django.utils import six from django.utils.six import PY3 from django.utils.translation import (ugettext, ugettext_lazy, activate, @@ -235,9 +235,9 @@ class TranslationTests(TestCase): s = mark_safe(str('Password')) self.assertEqual(SafeString, type(s)) with translation.override('de', deactivate=True): - self.assertEqual(SafeUnicode, type(ugettext(s))) - self.assertEqual('aPassword', SafeString('a') + s) - self.assertEqual('Passworda', s + SafeString('a')) + self.assertEqual(SafeText, type(ugettext(s))) + self.assertEqual('aPassword', SafeText('a') + s) + self.assertEqual('Passworda', s + SafeText('a')) self.assertEqual('Passworda', s + mark_safe('a')) self.assertEqual('aPassword', mark_safe('a') + s) self.assertEqual('as', mark_safe('a') + mark_safe('s')) @@ -897,9 +897,9 @@ class TestModels(TestCase): def test_safestr(self): c = Company(cents_paid=12, products_delivered=1) - c.name = SafeUnicode('Iñtërnâtiônàlizætiøn1') + c.name = SafeText('Iñtërnâtiônàlizætiøn1') c.save() - c.name = SafeString('Iñtërnâtiônàlizætiøn1'.encode('utf-8')) + c.name = SafeBytes('Iñtërnâtiônàlizætiøn1'.encode('utf-8')) c.save() -- cgit v1.3 From 58683e9c82d6e7c5fbb7acef79eef9408b776ab0 Mon Sep 17 00:00:00 2001 From: Marc Tamlyn Date: Sat, 18 Aug 2012 14:46:17 +0100 Subject: Fixed #16744 -- Class based view should have the view object in the context Updated the most recent patch from @claudep to apply again and updated the documentation location. --- django/views/generic/base.py | 2 ++ docs/ref/class-based-views/mixins-simple.txt | 16 ++++++++++++++-- docs/ref/templates/api.txt | 2 ++ docs/releases/1.5.txt | 6 ++++++ tests/regressiontests/generic_views/base.py | 2 ++ tests/regressiontests/generic_views/detail.py | 2 ++ tests/regressiontests/generic_views/edit.py | 5 +++++ tests/regressiontests/generic_views/list.py | 2 ++ 8 files changed, 35 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/django/views/generic/base.py b/django/views/generic/base.py index 6554e898ca..ca039ae9db 100644 --- a/django/views/generic/base.py +++ b/django/views/generic/base.py @@ -18,6 +18,8 @@ class ContextMixin(object): """ def get_context_data(self, **kwargs): + if 'view' not in kwargs: + kwargs['view'] = self return kwargs diff --git a/docs/ref/class-based-views/mixins-simple.txt b/docs/ref/class-based-views/mixins-simple.txt index 085c8a5fc7..61fc945cd3 100644 --- a/docs/ref/class-based-views/mixins-simple.txt +++ b/docs/ref/class-based-views/mixins-simple.txt @@ -17,8 +17,20 @@ ContextMixin .. method:: get_context_data(**kwargs) - Returns a dictionary representing the template context. The - keyword arguments provided will make up the returned context. + Returns a dictionary representing the template context. The keyword + arguments provided will make up the returned context. + + The template context of all class-based generic views include a + ``view`` variable that points to the ``View`` instance. + + .. admonition:: Use ``alters_data`` where appropriate + + Note that having the view instance in the template context may + expose potentially hazardous methods to template authors. To + prevent methods like this from being called in the template, set + ``alters_data=True`` on those methods. For more information, read + the documentation on :ref:`rendering a template context + `. TemplateResponseMixin --------------------- diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index bd2b4c6e9d..c52194e6b7 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -198,6 +198,8 @@ straight lookups. Here are some things to keep in mind: * A variable can only be called if it has no required arguments. Otherwise, the system will return an empty string. +.. _alters-data-description: + * Obviously, there can be side effects when calling some variables, and it'd be either foolish or a security hole to allow the template system to access them. diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 2896acb36b..5f27b7ccbb 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -84,6 +84,12 @@ By passing ``False`` using this argument it is now possible to retreive the :class:`ContentType ` associated with proxy models. +New ``view`` variable in class-based views context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In all :doc:`generic class-based views ` +(or any class-based view inheriting from ``ContextMixin``), the context dictionary +contains a ``view`` variable that points to the ``View`` instance. + Minor features ~~~~~~~~~~~~~~ diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py index 5296f6941a..b9f64888fc 100644 --- a/tests/regressiontests/generic_views/base.py +++ b/tests/regressiontests/generic_views/base.py @@ -276,6 +276,7 @@ class TemplateViewTest(TestCase): response = self.client.get('/template/simple/bar/') self.assertEqual(response.status_code, 200) self.assertEqual(response.context['params'], {'foo': 'bar'}) + self.assertTrue(isinstance(response.context['view'], View)) def test_extra_template_params(self): """ @@ -285,6 +286,7 @@ class TemplateViewTest(TestCase): self.assertEqual(response.status_code, 200) self.assertEqual(response.context['params'], {'foo': 'bar'}) self.assertEqual(response.context['key'], 'value') + self.assertTrue(isinstance(response.context['view'], View)) def test_cached_views(self): """ diff --git a/tests/regressiontests/generic_views/detail.py b/tests/regressiontests/generic_views/detail.py index 60c5f7afe9..c8f1999a2a 100644 --- a/tests/regressiontests/generic_views/detail.py +++ b/tests/regressiontests/generic_views/detail.py @@ -2,6 +2,7 @@ from __future__ import absolute_import from django.core.exceptions import ImproperlyConfigured from django.test import TestCase +from django.views.generic.base import View from .models import Artist, Author, Page @@ -14,6 +15,7 @@ class DetailViewTest(TestCase): res = self.client.get('/detail/obj/') self.assertEqual(res.status_code, 200) self.assertEqual(res.context['object'], {'foo': 'bar'}) + self.assertTrue(isinstance(res.context['view'], View)) self.assertTemplateUsed(res, 'generic_views/detail.html') def test_detail_by_pk(self): diff --git a/tests/regressiontests/generic_views/edit.py b/tests/regressiontests/generic_views/edit.py index 651e14fff3..16f4da8efe 100644 --- a/tests/regressiontests/generic_views/edit.py +++ b/tests/regressiontests/generic_views/edit.py @@ -5,6 +5,7 @@ from django.core.urlresolvers import reverse from django import forms from django.test import TestCase from django.utils.unittest import expectedFailure +from django.views.generic.base import View from django.views.generic.edit import FormMixin from . import views @@ -31,6 +32,7 @@ class CreateViewTests(TestCase): res = self.client.get('/edit/authors/create/') self.assertEqual(res.status_code, 200) self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertTrue(isinstance(res.context['view'], View)) self.assertFalse('object' in res.context) self.assertFalse('author' in res.context) self.assertTemplateUsed(res, 'generic_views/author_form.html') @@ -101,6 +103,7 @@ class CreateViewTests(TestCase): self.assertEqual(res.status_code, 302) self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/') + class UpdateViewTests(TestCase): urls = 'regressiontests.generic_views.urls' @@ -226,6 +229,7 @@ class UpdateViewTests(TestCase): res = self.client.get('/edit/author/update/') self.assertEqual(res.status_code, 200) self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertTrue(isinstance(res.context['view'], View)) self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk)) self.assertTemplateUsed(res, 'generic_views/author_form.html') @@ -237,6 +241,7 @@ class UpdateViewTests(TestCase): self.assertRedirects(res, 'http://testserver/list/authors/') self.assertQuerysetEqual(Author.objects.all(), ['']) + class DeleteViewTests(TestCase): urls = 'regressiontests.generic_views.urls' diff --git a/tests/regressiontests/generic_views/list.py b/tests/regressiontests/generic_views/list.py index b925758524..14dc1d725d 100644 --- a/tests/regressiontests/generic_views/list.py +++ b/tests/regressiontests/generic_views/list.py @@ -2,6 +2,7 @@ from __future__ import absolute_import from django.core.exceptions import ImproperlyConfigured from django.test import TestCase +from django.views.generic.base import View from .models import Author, Artist @@ -21,6 +22,7 @@ class ListViewTests(TestCase): self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'generic_views/author_list.html') self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertTrue(isinstance(res.context['view'], View)) self.assertIs(res.context['author_list'], res.context['object_list']) self.assertIsNone(res.context['paginator']) self.assertIsNone(res.context['page_obj']) -- cgit v1.3 From 212b9826bdda5c3c2eb680e6f9c5b046b4172300 Mon Sep 17 00:00:00 2001 From: Marc Tamlyn Date: Sat, 18 Aug 2012 13:53:22 +0100 Subject: Fixed #14516 -- Extract methods from removetags and slugify template filters Patch by @jphalip updated to apply, documentation and release notes added. I've documented strip_tags as well as remove_tags as the difference between the two wouldn't be immediately obvious. --- django/template/defaultfilters.py | 10 ++++----- django/utils/html.py | 11 +++++++++ django/utils/text.py | 12 ++++++++++ docs/ref/utils.txt | 45 +++++++++++++++++++++++++++++++++++++ docs/releases/1.5.txt | 4 ++++ tests/regressiontests/utils/html.py | 9 ++++++++ tests/regressiontests/utils/text.py | 8 +++++++ 7 files changed, 94 insertions(+), 5 deletions(-) (limited to 'docs') diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 628b6627d2..d8b0c3439a 100644 --- a/django/template/defaultfilters.py +++ b/django/template/defaultfilters.py @@ -231,12 +231,12 @@ def make_list(value): @stringfilter def slugify(value): """ - Normalizes string, converts to lowercase, removes non-alpha characters, - and converts spaces to hyphens. + Converts to lowercase, removes non-word characters (alphanumerics and + underscores) and converts spaces to hyphens. Also strips leading and + trailing whitespace. """ - value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode() - value = re.sub('[^\w\s-]', '', value).strip().lower() - return mark_safe(re.sub('[-\s]+', '-', value)) + from django.utils.text import slugify + return slugify(value) @register.filter(is_safe=True) def stringformat(value, arg): diff --git a/django/utils/html.py b/django/utils/html.py index 647982a15f..13954ce195 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -123,6 +123,17 @@ def strip_tags(value): return re.sub(r'<[^>]*?>', '', force_text(value)) strip_tags = allow_lazy(strip_tags) +def remove_tags(html, tags): + """Returns the given HTML with given tags removed.""" + tags = [re.escape(tag) for tag in tags.split()] + tags_re = u'(%s)' % u'|'.join(tags) + starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U) + endtag_re = re.compile(u'' % tags_re) + html = starttag_re.sub(u'', html) + html = endtag_re.sub(u'', html) + return html +remove_tags = allow_lazy(remove_tags, unicode) + def strip_spaces_between_tags(value): """Returns the given HTML with spaces between tags removed.""" return re.sub(r'>\s+<', '><', force_text(value)) diff --git a/django/utils/text.py b/django/utils/text.py index ddeb29f2d2..cbafab0032 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -16,6 +16,7 @@ if not six.PY3: from django.utils.functional import allow_lazy, SimpleLazyObject from django.utils import six from django.utils.translation import ugettext_lazy, ugettext as _, pgettext +from django.utils.safestring import mark_safe # Capitalizes the first letter of a string. capfirst = lambda x: x and force_text(x)[0].upper() + force_text(x)[1:] @@ -383,3 +384,14 @@ def unescape_string_literal(s): quote = s[0] return s[1:-1].replace(r'\%s' % quote, quote).replace(r'\\', '\\') unescape_string_literal = allow_lazy(unescape_string_literal) + +def slugify(value): + """ + Converts to lowercase, removes non-word characters (alphanumerics and + underscores) and converts spaces to hyphens. Also strips leading and + trailing whitespace. + """ + value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') + value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) + return mark_safe(re.sub('[-\s]+', '-', value)) +slugify = allow_lazy(slugify, unicode) diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 20775fcc81..775d70738b 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -486,6 +486,33 @@ escaping HTML. through :func:`conditional_escape` which (ultimately) calls :func:`~django.utils.encoding.force_text` on the values. +.. function:: strip_tags(value) + + Removes anything that looks like an html tag from the string, that is + anything contained within ``<>``. + + For example:: + + strip_tags(value) + + If ``value`` is ``"Joel a slug"`` the + return value will be ``"Joel is a slug"``. + +.. function:: remove_tags(value, tags) + + Removes a list of [X]HTML tag names from the output. + + For example:: + + remove_tags(value, ["b", "span"]) + + If ``value`` is ``"Joel a slug"`` the + return value will be ``"Joel a slug"``. + + Note that this filter is case-sensitive. + + If ``value`` is ``"Joel a slug"`` the + return value will be ``"Joel a slug"``. .. _str.format: http://docs.python.org/library/stdtypes.html#str.format @@ -599,6 +626,24 @@ appropriate entities. Can be called multiple times on a single string (the resulting escaping is only applied once). +``django.utils.text`` +===================== + +.. module:: django.utils.text + :synopsis: Text manipulation. + +.. function:: slugify + + Converts to lowercase, removes non-word characters (alphanumerics and + underscores) and converts spaces to hyphens. Also strips leading and trailing + whitespace. + + For example:: + + slugify(value) + + If ``value`` is ``"Joel is a slug"``, the output will be ``"joel-is-a-slug"``. + ``django.utils.translation`` ============================ diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 5f27b7ccbb..29cbd45c49 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -267,6 +267,10 @@ Miscellaneous * :func:`~django.utils.http.int_to_base36` properly raises a :exc:`TypeError` instead of :exc:`ValueError` for non-integer inputs. +* The ``slugify`` template filter is now available as a standard python + function at :func:`django.utils.text.slugify`. Similarly, ``remove_tags`` is + available at :func:`django.utils.html.remove_tags`. + Features deprecated in 1.5 ========================== diff --git a/tests/regressiontests/utils/html.py b/tests/regressiontests/utils/html.py index fe40d4eaae..98df80a5e2 100644 --- a/tests/regressiontests/utils/html.py +++ b/tests/regressiontests/utils/html.py @@ -146,3 +146,12 @@ class TestUtilsHtml(unittest.TestCase): ) for value, output in items: self.check_output(f, value, output) + + def test_remove_tags(self): + f = html.remove_tags + items = ( + ("Yes", "b i", "Yes"), + ("x

    y

    ", "a b", "x

    y

    "), + ) + for value, tags, output in items: + self.assertEquals(f(value, tags), output) diff --git a/tests/regressiontests/utils/text.py b/tests/regressiontests/utils/text.py index dd6de63841..9fa86d515c 100644 --- a/tests/regressiontests/utils/text.py +++ b/tests/regressiontests/utils/text.py @@ -113,3 +113,11 @@ class TestUtilsText(SimpleTestCase): self.assertEqual(text.wrap(long_word, 20), long_word) self.assertEqual(text.wrap('a %s word' % long_word, 10), 'a\n%s\nword' % long_word) + + def test_slugify(self): + items = ( + (u'Hello, World!', 'hello-world'), + (u'spam & eggs', 'spam-eggs'), + ) + for value, output in items: + self.assertEqual(text.slugify(value), output) -- cgit v1.3 From f04bb6d798b07aa5e7c1d99d700fa6ddc7d39e62 Mon Sep 17 00:00:00 2001 From: Marc Tamlyn Date: Sat, 18 Aug 2012 12:52:05 +0100 Subject: Fixed #17228 -- params context variable is inconsistent Remove the params variable from the context and just put the variables in directly. This had not been committed previously as the original pattern was used in the functional generic views and we wanted consistency between them, but django.views.generic.simple.direct_to_template is now gone so we can do it 'right'. --- django/views/generic/base.py | 6 +++--- docs/releases/1.5.txt | 8 ++++++++ tests/regressiontests/generic_views/base.py | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'docs') diff --git a/django/views/generic/base.py b/django/views/generic/base.py index ca039ae9db..e11412ba4d 100644 --- a/django/views/generic/base.py +++ b/django/views/generic/base.py @@ -142,11 +142,11 @@ class TemplateResponseMixin(object): class TemplateView(TemplateResponseMixin, ContextMixin, View): """ - A view that renders a template. This view is different from all the others - insofar as it also passes ``kwargs`` as ``params`` to the template context. + A view that renders a template. This view will also pass into the context + any keyword arguments passed by the url conf. """ def get(self, request, *args, **kwargs): - context = self.get_context_data(params=kwargs) + context = self.get_context_data(**kwargs) return self.render_to_response(context) diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 29cbd45c49..4f25919d79 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -144,6 +144,14 @@ year|date:"Y" }}``. ``next_year`` and ``previous_year`` were also added in the context. They are calculated according to ``allow_empty`` and ``allow_future``. +Context in TemplateView +~~~~~~~~~~~~~~~~~~~~~~~ + +For consistency with the design of the other generic views, +:class:`~django.views.generic.base.TemplateView` no longer passes a ``params`` +dictionary into the context, instead passing the variables from the URLconf +directly into the context. + OPTIONS, PUT and DELETE requests in the test client ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py index b9f64888fc..a61b01be0b 100644 --- a/tests/regressiontests/generic_views/base.py +++ b/tests/regressiontests/generic_views/base.py @@ -275,7 +275,7 @@ class TemplateViewTest(TestCase): """ response = self.client.get('/template/simple/bar/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['params'], {'foo': 'bar'}) + self.assertEqual(response.context['foo'], 'bar') self.assertTrue(isinstance(response.context['view'], View)) def test_extra_template_params(self): @@ -284,7 +284,7 @@ class TemplateViewTest(TestCase): """ response = self.client.get('/template/custom/bar/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['params'], {'foo': 'bar'}) + self.assertEqual(response.context['foo'], 'bar') self.assertEqual(response.context['key'], 'value') self.assertTrue(isinstance(response.context['view'], View)) -- cgit v1.3 From a120fac65a17137bc8ac710477478474e3f9973e Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 18 Aug 2012 16:36:55 +0200 Subject: Introduced force_bytes and force_str. This is consistent with the smart_* series of functions and it's going to be used by the next commit. --- django/utils/encoding.py | 19 +++++++++++++++++++ docs/ref/utils.txt | 17 ++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/django/utils/encoding.py b/django/utils/encoding.py index 3ac2c508b6..7027b82a61 100644 --- a/django/utils/encoding.py +++ b/django/utils/encoding.py @@ -139,6 +139,19 @@ def smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): """ Returns a bytestring version of 's', encoded as specified in 'encoding'. + If strings_only is True, don't convert (some) non-string-like objects. + """ + if isinstance(s, Promise): + # The input is the result of a gettext_lazy() call. + return s + return force_bytes(s, encoding, strings_only, errors) + + +def force_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): + """ + Similar to smart_bytes, except that lazy instances are resolved to + strings, rather than kept as lazy objects. + If strings_only is True, don't convert (some) non-string-like objects. """ if isinstance(s, bytes): @@ -169,8 +182,10 @@ def smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): if six.PY3: smart_str = smart_text + force_str = force_text else: smart_str = smart_bytes + force_str = force_bytes # backwards compatibility for Python 2 smart_unicode = smart_text force_unicode = force_text @@ -181,6 +196,10 @@ Apply smart_text in Python 3 and smart_bytes in Python 2. This is suitable for writing to sys.stdout (for instance). """ +force_str.__doc__ = """\ +Apply force_text in Python 3 and force_bytes in Python 2. +""" + def iri_to_uri(iri): """ Convert an Internationalized Resource Identifier (IRI) portion to a URI diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 775d70738b..de19578cac 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -240,14 +240,29 @@ The functions defined in this module share the following properties: If ``strings_only`` is ``True``, don't convert (some) non-string-like objects. +.. function:: force_bytes(s, encoding='utf-8', strings_only=False, errors='strict') + + .. versionadded:: 1.5 + + Similar to ``smart_bytes``, except that lazy instances are resolved to + bytestrings, rather than kept as lazy objects. + + If ``strings_only`` is ``True``, don't convert (some) non-string-like + objects. + .. function:: smart_str(s, encoding='utf-8', strings_only=False, errors='strict') Alias of :func:`smart_bytes` on Python 2 and :func:`smart_text` on Python - 3. This function always returns a :class:`str`. + 3. This function returns a :class:`str` or a lazy string. For instance, this is suitable for writing to :attr:`sys.stdout` on Python 2 and 3. +.. function:: force_str(s, encoding='utf-8', strings_only=False, errors='strict') + + Alias of :func:`force_bytes` on Python 2 and :func:`force_text` on Python + 3. This function always returns a :class:`str`. + .. function:: iri_to_uri(iri) Convert an Internationalized Resource Identifier (IRI) portion to a URI -- cgit v1.3 From 8d5c11caad7f87d14b486a46890782fa0b66bb74 Mon Sep 17 00:00:00 2001 From: Marc Tamlyn Date: Sat, 18 Aug 2012 16:28:17 +0100 Subject: Fixed #3542 -- Add support for changing granularity on ArchiveView. Resolving the concept from a very old ticket in a more class-based-view manner. --- django/views/generic/dates.py | 17 +++++++++++++---- docs/ref/class-based-views/generic-date-based.txt | 3 +++ tests/regressiontests/generic_views/dates.py | 6 +++++- tests/regressiontests/generic_views/urls.py | 2 ++ 4 files changed, 23 insertions(+), 5 deletions(-) (limited to 'docs') diff --git a/django/views/generic/dates.py b/django/views/generic/dates.py index 08b7318738..d44246f0b7 100644 --- a/django/views/generic/dates.py +++ b/django/views/generic/dates.py @@ -327,6 +327,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View): Abstract base class for date-based views displaying a list of objects. """ allow_empty = False + date_list_period = 'year' def get(self, request, *args, **kwargs): self.date_list, self.object_list, extra_context = self.get_dated_items() @@ -370,13 +371,18 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View): return qs - def get_date_list(self, queryset, date_type): + def get_date_list_period(self): + return self.date_list_period + + def get_date_list(self, queryset, date_type=None): """ Get a date list by calling `queryset.dates()`, checking along the way for empty lists that aren't allowed. """ date_field = self.get_date_field() allow_empty = self.get_allow_empty() + if date_type is None: + date_type = self.get_date_list_period() date_list = queryset.dates(date_field, date_type)[::-1] if date_list is not None and not date_list and not allow_empty: @@ -400,7 +406,7 @@ class BaseArchiveIndexView(BaseDateListView): Return (date_list, items, extra_context) for this request. """ qs = self.get_dated_queryset(ordering='-%s' % self.get_date_field()) - date_list = self.get_date_list(qs, 'year') + date_list = self.get_date_list(qs) if not date_list: qs = qs.none() @@ -419,6 +425,7 @@ class BaseYearArchiveView(YearMixin, BaseDateListView): """ List of objects published in a given year. """ + date_list_period = 'month' make_object_list = False def get_dated_items(self): @@ -438,7 +445,7 @@ class BaseYearArchiveView(YearMixin, BaseDateListView): } qs = self.get_dated_queryset(ordering='-%s' % date_field, **lookup_kwargs) - date_list = self.get_date_list(qs, 'month') + date_list = self.get_date_list(qs) if not self.get_make_object_list(): # We need this to be a queryset since parent classes introspect it @@ -470,6 +477,8 @@ class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView): """ List of objects published in a given year. """ + date_list_period = 'day' + def get_dated_items(self): """ Return (date_list, items, extra_context) for this request. @@ -489,7 +498,7 @@ class BaseMonthArchiveView(YearMixin, MonthMixin, BaseDateListView): } qs = self.get_dated_queryset(**lookup_kwargs) - date_list = self.get_date_list(qs, 'day') + date_list = self.get_date_list(qs) return (date_list, qs, { 'month': date, diff --git a/docs/ref/class-based-views/generic-date-based.txt b/docs/ref/class-based-views/generic-date-based.txt index 6730923d1e..12776cbb94 100644 --- a/docs/ref/class-based-views/generic-date-based.txt +++ b/docs/ref/class-based-views/generic-date-based.txt @@ -29,6 +29,9 @@ ArchiveIndexView * Uses a default ``context_object_name`` of ``latest``. * Uses a default ``template_name_suffix`` of ``_archive``. + * Defaults to providing ``date_list`` by year, but this can be altered to + month or day using the attribute ``date_list_period``. This also applies + to all subclass views. YearArchiveView --------------- diff --git a/tests/regressiontests/generic_views/dates.py b/tests/regressiontests/generic_views/dates.py index d18d10e603..c2fa71b376 100644 --- a/tests/regressiontests/generic_views/dates.py +++ b/tests/regressiontests/generic_views/dates.py @@ -60,7 +60,6 @@ class ArchiveIndexViewTests(TestCase): res = self.client.get('/dates/books/allow_empty/') self.assertEqual(res.status_code, 200) self.assertEqual(list(res.context['date_list']), []) - self.assertEqual(list(res.context['date_list']), []) self.assertTemplateUsed(res, 'generic_views/book_archive.html') def test_archive_view_template(self): @@ -80,6 +79,11 @@ class ArchiveIndexViewTests(TestCase): def test_archive_view_invalid(self): self.assertRaises(ImproperlyConfigured, self.client.get, '/dates/books/invalid/') + def test_archive_view_by_month(self): + res = self.client.get('/dates/books/by_month/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'month')[::-1]) + def test_paginated_archive_view(self): self._make_books(20, base_date=datetime.date.today()) res = self.client.get('/dates/books/paginated/') diff --git a/tests/regressiontests/generic_views/urls.py b/tests/regressiontests/generic_views/urls.py index 5e15c6c9c1..c72bfecb65 100644 --- a/tests/regressiontests/generic_views/urls.py +++ b/tests/regressiontests/generic_views/urls.py @@ -113,6 +113,8 @@ urlpatterns = patterns('', views.BookArchive.as_view(paginate_by=10)), (r'^dates/books/reverse/$', views.BookArchive.as_view(queryset=models.Book.objects.order_by('pubdate'))), + (r'^dates/books/by_month/$', + views.BookArchive.as_view(date_list_period='month')), (r'^dates/booksignings/$', views.BookSigningArchive.as_view()), -- cgit v1.3 From cccbb6bff3be5fe5f9653e625cf839eb1aedc7f6 Mon Sep 17 00:00:00 2001 From: Julien Phalip Date: Sun, 19 Aug 2012 00:57:30 -0700 Subject: Made some minor fixes to the GeoDjango installation guide. --- docs/ref/contrib/gis/install.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/ref/contrib/gis/install.txt b/docs/ref/contrib/gis/install.txt index 72bd72a6f8..127091724a 100644 --- a/docs/ref/contrib/gis/install.txt +++ b/docs/ref/contrib/gis/install.txt @@ -140,7 +140,7 @@ internal geometry representation used by GeoDjango (it's behind the "lazy" geometries). Specifically, the C API library is called (e.g., ``libgeos_c.so``) directly from Python using ctypes. -First, download GEOS 3.2 from the refractions Web site and untar the source +First, download GEOS 3.3.0 from the refractions Web site and untar the source archive:: $ wget http://download.osgeo.org/geos/geos-3.3.0.tar.bz2 @@ -421,7 +421,7 @@ After SQLite has been built with the R*Tree module enabled, get the latest SpatiaLite library source and tools bundle from the `download page`__:: $ wget http://www.gaia-gis.it/gaia-sins/libspatialite-sources/libspatialite-amalgamation-2.3.1.tar.gz - $ wget http://www.gaia-gis.it/gaia-sins/libspatialite-sources/spatialite-tools-2.3.1.tar.gz + $ wget http://www.gaia-gis.it/gaia-sins/spatialite-tools-sources/spatialite-tools-2.3.1.tar.gz $ tar xzf libspatialite-amalgamation-2.3.1.tar.gz $ tar xzf spatialite-tools-2.3.1.tar.gz @@ -605,7 +605,7 @@ http://www.gaia-gis.it/spatialite-2.4.0/ for 2.4):: Then, use the ``spatialite`` command to initialize a spatial database:: - $ spatialite geodjango.db < init_spatialite-2.X.sql + $ spatialite geodjango.db < init_spatialite-2.3.sql .. note:: -- cgit v1.3 From 500fe9c639f37533fb8525d53dacbe986c0e3a9f Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 19 Aug 2012 16:30:07 +0200 Subject: [py3] Wrote Django-specific porting tips and extended the existing Python 3 documentation. --- docs/topics/python3.txt | 311 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 276 insertions(+), 35 deletions(-) (limited to 'docs') diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt index 32463f2e51..589856bd4e 100644 --- a/docs/topics/python3.txt +++ b/docs/topics/python3.txt @@ -1,25 +1,212 @@ -====================== -Python 3 compatibility -====================== +=================== +Porting to Python 3 +=================== Django 1.5 is the first version of Django to support Python 3. The same code runs both on Python 2 (≥ 2.6.5) and Python 3 (≥ 3.2), thanks to the six_ -compatibility layer and ``unicode_literals``. +compatibility layer. .. _six: http://packages.python.org/six/ -This document is not meant as a Python 2 to Python 3 migration guide. There -are many existing resources, including `Python's official porting guide`_. -Rather, it describes guidelines that apply to Django's code and are -recommended for pluggable apps that run with both Python 2 and 3. +This document is primarily targeted at authors of pluggable application +who want to support both Python 2 and 3. It also describes guidelines that +apply to Django's code. + +Philosophy +========== + +This document assumes that you are familiar with the changes between Python 2 +and Python 3. If you aren't, read `Python's official porting guide`_ first. +Refreshing your knowledge of unicode handling on Python 2 and 3 will help; the +`Pragmatic Unicode`_ presentation is a good resource. + +Django uses the *Python 2/3 Compatible Source* strategy. Of course, you're +free to chose another strategy for your own code, especially if you don't need +to stay compatible with Python 2. But authors of pluggable applications are +encouraged to use the same porting strategy as Django itself. + +Writing compatible code is much easier if you target Python ≥ 2.6. You will +most likely take advantage of the compatibility functions introduced in Django +1.5, like :mod:`django.utils.six`, so your application will also require +Django ≥ 1.5. + +Obviously, writing compatible source code adds some overhead, and that can +cause frustration. Django's developers have found that attempting to write +Python 3 code that's compatible with Python 2 is much more rewarding than the +opposite. Not only does that make your code more future-proof, but Python 3's +advantages (like the saner string handling) start shining quickly. Dealing +with Python 2 becomes a backwards compatibility requirement, and we as +developers are used to dealing with such constraints. + +Porting tools provided by Django are inspired by this philosophy, and it's +reflected throughout this guide. .. _Python's official porting guide: http://docs.python.org/py3k/howto/pyporting.html +.. _Pragmatic Unicode: http://nedbatchelder.com/text/unipain.html + +Porting tips +============ + +Unicode literals +---------------- + +This step consists in: + +- Adding ``from __future__ import unicode_literals`` at the top of your Python + modules -- it's best to put it in each and every module, otherwise you'll + keep checking the top of your files to see which mode is in effect; +- Removing the ``u`` prefix before unicode strings; +- Adding a ``b`` prefix before bytestrings. + +Performing these changes systematically guarantees backwards compatibility. + +However, Django applications generally don't need bytestrings, since Django +only exposes unicode interfaces to the programmer. Python 3 discourages using +bytestrings, except for binary data or byte-oriented interfaces. Python 2 +makes bytestrings and unicode strings effectively interchangeable, as long as +they only contain ASCII data. Take advantage of this to use unicode strings +wherever possible and avoid the ``b`` prefixes. + +.. note:: + + Python 2's ``u`` prefix is a syntax error in Python 3.2 but it will be + allowed again in Python 3.3 thanks to :pep:`414`. Thus, this + transformation is optional if you target Python ≥ 3.3. It's still + recommended, per the "write Python 3 code" philosophy. + +String handling +--------------- + +Python 2's :class:`unicode` type was renamed :class:`str` in Python 3, +:class:`str` was renamed :class:`bytes`, and :class:`basestring` disappeared. +six_ provides :ref:`tools ` to deal with these +changes. + +Django also contains several string related classes and functions in the +:mod:`django.utils.encoding` and :mod:`django.utils.safestring` modules. Their +names used the words ``str``, which doesn't mean the same thing in Python 2 +and Python 3, and ``unicode``, which doesn't exist in Python 3. In order to +avoid ambiguity and confusion these concepts were renamed ``bytes`` and +``text``. + +Here are the name changes in :mod:`django.utils.encoding`: + +================== ================== +Old name New name +================== ================== +``smart_str`` ``smart_bytes`` +``smart_unicode`` ``smart_text`` +``force_unicode`` ``force_text`` +================== ================== + +For backwards compatibility, the old names still work on Python 2. Under +Python 3, ``smart_str`` is an alias for ``smart_text``. + +.. note:: + + :mod:`django.utils.encoding` was deeply refactored in Django 1.5 to + provide a more consistent API. Check its documentation for more + information. + +:mod:`django.utils.safestring` is mostly used via the +:func:`~django.utils.safestring.mark_safe` and +:func:`~django.utils.safestring.mark_for_escaping` functions, which didn't +change. In case you're using the internals, here are the name changes: + +================== ================== +Old name New name +================== ================== +``EscapeString`` ``EscapeBytes`` +``EscapeUnicode`` ``EscapeText`` +``SafeString`` ``SafeBytes`` +``SafeUnicode`` ``SafeText`` +================== ================== + +For backwards compatibility, the old names still work on Python 2. Under +Python 3, ``EscapeString`` and ``SafeString`` are aliases for ``EscapeText`` +and ``SafeText`` respectively. + +:meth:`__str__` and :meth:`__unicode__` methods +----------------------------------------------- + +In Python 2, the object model specifies :meth:`__str__` and +:meth:`__unicode__` methods. If these methods exist, they must return +:class:`str` (bytes) and :class:`unicode` (text) respectively. + +The ``print`` statement and the :func:`str` built-in call :meth:`__str__` to +determine the human-readable representation of an object. The :func:`unicode` +built-in calls :meth:`__unicode__` if it exists, and otherwise falls back to +:meth:`__str__` and decodes the result with the system encoding. Conversely, +the :class:`~django.db.models.Model` base class automatically derives +:meth:`__str__` from :meth:`__unicode__` by encoding to UTF-8. + +In Python 3, there's simply :meth:`__str__`, which must return :class:`str` +(text). + +(It is also possible to define :meth:`__bytes__`, but Django application have +little use for that method, because they hardly ever deal with +:class:`bytes`.) + +Django provides a simple way to define :meth:`__str__` and :meth:`__unicode__` +methods that work on Python 2 and 3: you must define a :meth:`__str__` method +returning text and to apply the +:func:`~django.utils.encoding.python_2_unicode_compatible` decorator. + +On Python 3, the decorator is a no-op. On Python 2, it defines appropriate +:meth:`__unicode__` and :meth:`__str__` methods (replacing the original +:meth:`__str__` method in the process). Here's an example:: + + 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" + +This technique is the best match for Django's porting philosophy. + +Finally, note that :meth:`__repr__` must return a :class:`str` on all versions +of Python. + +:class:`dict` and :class:`dict`-like classes +-------------------------------------------- + +:meth:`dict.keys`, :meth:`dict.items` and :meth:`dict.values` return lists in +Python 2 and iterators in Python 3. :class:`~django.http.QueryDict` and the +:class:`dict`-like classes defined in :mod:`django.utils.datastructures` +behave likewise in Python 3. + +six_ provides compatibility functions to work around this change: +:func:`~six.iterkeys`, :func:`~six.iteritems`, and :func:`~six.itervalues`. +Django's bundled version adds :func:`~django.utils.six.iterlists` for +:class:`~django.utils.datastructures.MultiValueDict` and its subclasses. + +:class:`~django.http.HttpRequest` and :class:`~django.http.HttpResponse` objects +-------------------------------------------------------------------------------- + +According to :pep:`3333`: + +- headers are always :class:`str` objects, +- input and output streams are always :class:`bytes` objects. + +Specifically, :attr:`HttpResponse.content ` +contains :class:`bytes`, which may require refactoring your tests.This won't +be an issue if you use :meth:`~django.test.TestCase.assertContains` and +:meth:`~django.test.TestCase.assertNotContains`: these methods expect a +unicode string. + +Coding guidelines +================= + +The following guidelines are enforced in Django's source code. They're also +recommended for third-party application who follow the same porting strategy. Syntax requirements -=================== +------------------- Unicode -------- +~~~~~~~ In Python 3, all strings are considered Unicode by default. The ``unicode`` type from Python 2 is called ``str`` in Python 3, and ``str`` becomes @@ -36,29 +223,25 @@ 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" -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') +In Python 3, there aren't any automatic conversions between :class:`str` and +:class:`bytes`, and the :mod:`codecs` module became more strict. +:meth:`str.decode` always returns :class:`bytes`, and :meth:`bytes.decode` +always returns :class:`str`. As a consequence, the following pattern is +sometimes necessary:: + + value = value.encode('ascii', 'ignore').decode('ascii') + Be cautious if you have to `slice bytestrings`_. .. _slice bytestrings: http://docs.python.org/py3k/howto/pyporting.html#bytes-literals Exceptions ----------- +~~~~~~~~~~ When you capture exceptions, use the ``as`` keyword:: @@ -71,17 +254,64 @@ This older syntax was removed in Python 3:: try: ... - except MyException, exc: + except MyException, exc: # Don't do that! ... The syntax to reraise an exception with a different traceback also changed. Use :func:`six.reraise`. +Magic methods +------------- + +Use the patterns below to handle magic methods renamed in Python 3. + +Iterators +~~~~~~~~~ + +:: + + class MyIterator(object): + def __iter__(self): + return self # implement some logic here + + def __next__(self): + raise StopIteration # implement some logic here + + next = __next__ # Python 2 compatibility + +Boolean evaluation +~~~~~~~~~~~~~~~~~~ + +:: + + class MyBoolean(object): + + def __bool__(self): + return True # implement some logic here + + __nonzero__ = __bool__ # Python 2 compatibility + +Division +~~~~~~~~ + +:: + + class MyDivisible(object): + + def __truediv__(self, other): + return self / other # implement some logic here + + __div__ = __truediv__ # Python 2 compatibility + + def __itruediv__(self, other): + return self // other # implement some logic here + + __idiv__ = __itruediv__ # Python 2 compatibility .. module: django.utils.six Writing compatible code with six -================================ +-------------------------------- six_ is the canonical compatibility library for supporting Python 2 and 3 in a single codebase. Read its documentation! @@ -90,8 +320,10 @@ a single codebase. Read its documentation! Here are the most common changes required to write compatible code. -String types ------------- +.. _string-handling-with-six: + +String handling +~~~~~~~~~~~~~~~ The ``basestring`` and ``unicode`` types were removed in Python 3, and the meaning of ``str`` changed. To test these types, use the following idioms:: @@ -104,7 +336,7 @@ Python ≥ 2.6 provides ``bytes`` as an alias for ``str``, so you don't need :attr:`six.binary_type`. ``long`` --------- +~~~~~~~~ The ``long`` type no longer exists in Python 3. ``1L`` is a syntax error. Use :data:`six.integer_types` check if a value is an integer or a long:: @@ -112,21 +344,27 @@ The ``long`` type no longer exists in Python 3. ``1L`` is a syntax error. Use isinstance(myvalue, six.integer_types) # replacement for (int, long) ``xrange`` ----------- +~~~~~~~~~~ Import :func:`six.moves.xrange` wherever you use ``xrange``. Moved modules -------------- +~~~~~~~~~~~~~ Some modules were renamed in Python 3. The :mod:`django.utils.six.moves ` module provides a compatible location to import them. -In addition to six' defaults, Django's version provides ``thread`` as -``_thread`` and ``dummy_thread`` as ``_dummy_thread``. +The ``urllib``, ``urllib2`` and ``urlparse`` modules were reworked in depth +and :mod:`django.utils.six.moves ` doesn't handle them. Django +explicitly tries both locations, as follows:: + + try: + from urllib.parse import urlparse, urlunparse + except ImportError: # Python 2 + from urlparse import urlparse, urlunparse PY3 ---- +~~~ If you need different code in Python 2 and Python 3, check :data:`six.PY3`:: @@ -141,9 +379,9 @@ function. .. module:: django.utils.six Customizations of six -===================== +--------------------- -The version of six bundled with Django includes a few additional tools: +The version of six bundled with Django includes one extra function: .. function:: iterlists(MultiValueDict) @@ -152,3 +390,6 @@ The version of six bundled with Django includes a few additional tools: :meth:`~django.utils.datastructures.MultiValueDict.iterlists()` on Python 2 and :meth:`~django.utils.datastructures.MultiValueDict.lists()` on Python 3. + +In addition to six' defaults moves, Django's version provides ``thread`` as +``_thread`` and ``dummy_thread`` as ``_dummy_thread``. -- cgit v1.3 From 268fa9631ec8bba04ab82aa80891e07cc02cdce2 Mon Sep 17 00:00:00 2001 From: Marc Tamlyn Date: Sun, 19 Aug 2012 20:04:52 +0200 Subject: Fixed indentation in the Python3 docs --- docs/topics/python3.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt index 589856bd4e..b22a331514 100644 --- a/docs/topics/python3.txt +++ b/docs/topics/python3.txt @@ -301,12 +301,12 @@ Division def __truediv__(self, other): return self / other # implement some logic here - __div__ = __truediv__ # Python 2 compatibility + __div__ = __truediv__ # Python 2 compatibility - def __itruediv__(self, other): + def __itruediv__(self, other): return self // other # implement some logic here - __idiv__ = __itruediv__ # Python 2 compatibility + __idiv__ = __itruediv__ # Python 2 compatibility .. module: django.utils.six -- cgit v1.3 From 7631fb8f37f0ba8155208a7a9f79f3a9958ac9b1 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sun, 19 Aug 2012 21:21:23 +0200 Subject: Clarified a sentence in the Python 3 docs. Thanks dstufft for the report. --- docs/topics/python3.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt index b22a331514..9b393ffd85 100644 --- a/docs/topics/python3.txt +++ b/docs/topics/python3.txt @@ -191,10 +191,11 @@ According to :pep:`3333`: - input and output streams are always :class:`bytes` objects. Specifically, :attr:`HttpResponse.content ` -contains :class:`bytes`, which may require refactoring your tests.This won't -be an issue if you use :meth:`~django.test.TestCase.assertContains` and -:meth:`~django.test.TestCase.assertNotContains`: these methods expect a -unicode string. +contains :class:`bytes`, which may become an issue if you compare it with a +:class:`str` in your tests. The preferred solution is to rely on +:meth:`~django.test.TestCase.assertContains` and +:meth:`~django.test.TestCase.assertNotContains`. These methods accept a +response and a unicode string as arguments. Coding guidelines ================= -- cgit v1.3 From 514a0013cd38fc586e3b4d8582da2f65c1cdd50c Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sun, 19 Aug 2012 18:46:46 -0400 Subject: Fixed #17180 - Emphasized the need to load the i18n template tag in each template that uses translations. Thanks stefan.freyr for the suggestion and buddylindsey for the draft patch. --- docs/topics/i18n/translation.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'docs') diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 79bd1a6a6a..9bd53da2b9 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -463,6 +463,9 @@ Internationalization: in template code Translations in :doc:`Django templates ` uses two template tags and a slightly different syntax than in Python code. To give your template access to these tags, put ``{% load i18n %}`` toward the top of your template. +As with all template tags, this tag needs to be loaded in all templates which +use translations, even those templates that extend from other templates which +have already loaded the ``i18n`` tag. .. templatetag:: trans -- cgit v1.3 From 3631db88cbae4b50b1d1b0fda0dfaa6a47f4eb0d Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Sun, 19 Aug 2012 20:13:09 -0400 Subject: Fixed typo in form wizard docs. --- docs/ref/contrib/formtools/form-wizard.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/ref/contrib/formtools/form-wizard.txt b/docs/ref/contrib/formtools/form-wizard.txt index 3cc8c7aed4..b8e585a4d2 100644 --- a/docs/ref/contrib/formtools/form-wizard.txt +++ b/docs/ref/contrib/formtools/form-wizard.txt @@ -528,7 +528,7 @@ We define our wizard in a ``views.py``:: We need to add the ``ContactWizard`` to our ``urls.py`` file:: - from django.conf.urls import pattern + from django.conf.urls import patterns from myapp.forms import ContactForm1, ContactForm2 from myapp.views import ContactWizard, show_message_form_condition -- cgit v1.3 From 26e0ba07aef4dcd6c57e1b03f2000c28672985e5 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Sun, 19 Aug 2012 22:03:50 -0300 Subject: Tweaked SpatiaLite GeoDjango docs. --- docs/ref/contrib/gis/install.txt | 18 +++++++----------- docs/ref/contrib/gis/testing.txt | 3 ++- 2 files changed, 9 insertions(+), 12 deletions(-) (limited to 'docs') diff --git a/docs/ref/contrib/gis/install.txt b/docs/ref/contrib/gis/install.txt index 127091724a..3cd790212c 100644 --- a/docs/ref/contrib/gis/install.txt +++ b/docs/ref/contrib/gis/install.txt @@ -60,14 +60,14 @@ The geospatial libraries required for a GeoDjango installation depends on the spatial database used. The following lists the library requirements, supported versions, and any notes for each of the supported database backends: -================== ============================== ================== ========================================================== +================== ============================== ================== ========================================= Database Library Requirements Supported Versions Notes -================== ============================== ================== ========================================================== +================== ============================== ================== ========================================= PostgreSQL GEOS, PROJ.4, PostGIS 8.1+ Requires PostGIS. MySQL GEOS 5.x Not OGC-compliant; limited functionality. Oracle GEOS 10.2, 11 XE not supported; not tested with 9. -SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 2.3+, pysqlite2 2.5+, and Django 1.1. -================== ============================== ================== ========================================================== +SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 2.3+, pysqlite2 2.5+ +================== ============================== ================== ========================================= .. _geospatial_libs: @@ -467,8 +467,8 @@ pysqlite2 ^^^^^^^^^ Because SpatiaLite must be loaded as an external extension, it requires the -``enable_load_extension`` method, which is only available in versions 2.5+. -Thus, download pysqlite2 2.6, and untar:: +``enable_load_extension`` method, which is only available in versions 2.5+ of +pysqlite2. Thus, download pysqlite2 2.6, and untar:: $ wget http://pysqlite.googlecode.com/files/pysqlite-2.6.0.tar.gz $ tar xzf pysqlite-2.6.0.tar.gz @@ -583,7 +583,7 @@ After you've installed SpatiaLite, you'll need to create a number of spatial metadata tables in your database in order to perform spatial queries. If you're using SpatiaLite 3.0 or newer, use the ``spatialite`` utility to -call the ``InitSpatiaMetaData()`` function, like this:: +call the ``InitSpatialMetaData()`` function, like this:: $ spatialite geodjango.db "SELECT InitSpatialMetaData();" the SPATIAL_REF_SYS table already contains some row(s) @@ -643,10 +643,6 @@ Invoke the Django shell from your project and execute the >>> from django.contrib.gis.utils import add_srs_entry >>> add_srs_entry(900913) -.. note:: - - In Django 1.1 the name of this function is ``add_postgis_srs``. - This adds an entry for the 900913 SRID to the ``spatial_ref_sys`` (or equivalent) table, making it possible for the spatial database to transform coordinates in this projection. You only need to execute this command *once* per spatial database. diff --git a/docs/ref/contrib/gis/testing.txt b/docs/ref/contrib/gis/testing.txt index 78663b967c..18ffbdd7d8 100644 --- a/docs/ref/contrib/gis/testing.txt +++ b/docs/ref/contrib/gis/testing.txt @@ -121,7 +121,8 @@ Settings Only relevant when using a SpatiaLite version older than 3.0. -By default, the GeoDjango test runner looks for the SpatiaLite SQL in the +By default, the GeoDjango test runner looks for the :ref:`file containing the +SpatiaLite dababase-initialization SQL code ` in the same directory where it was invoked (by default the same directory where ``manage.py`` is located). To use a different location, add the following to your settings:: -- cgit v1.3 From 30bdf22bc7a262858b02b65b0ed154dec46d911d Mon Sep 17 00:00:00 2001 From: Simon Meers Date: Mon, 20 Aug 2012 13:42:57 +1000 Subject: Fixed #18799 -- Improved index links for CBV documentation. Thanks anthonyb. --- docs/index.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/index.txt b/docs/index.txt index 011ecdb0bc..ca08301dad 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -104,9 +104,11 @@ to know about views via the links below: :doc:`Custom storage ` * **Class-based views:** - :doc:`Overview` | - :doc:`Built-in class-based views` | - :doc:`Built-in view mixins` + :doc:`Overview ` | + :doc:`Built-in display views ` | + :doc:`Built-in editing views ` | + :doc:`Using mixins ` | + :doc:`API reference ` * **Advanced:** :doc:`Generating CSV ` | -- cgit v1.3 From 4e82d614002f3fb46a3504b3a4fa2dcbf7f53dab Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 20 Aug 2012 15:01:57 +0200 Subject: Added links in URLs doc for consistency. --- docs/topics/http/urls.txt | 6 ++++++ docs/topics/http/views.txt | 4 ++++ 2 files changed, 10 insertions(+) (limited to 'docs') diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index 4e75dfe55f..819ce24b91 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -314,6 +314,9 @@ that should be called if none of the URL patterns match. By default, this is ``'django.views.defaults.page_not_found'``. That default value should suffice. +See the documentation about :ref:`the 404 (HTTP Not Found) view +` for more information. + handler500 ---------- @@ -326,6 +329,9 @@ have runtime errors in view code. By default, this is ``'django.views.defaults.server_error'``. That default value should suffice. +See the documentation about :ref:`the 500 (HTTP Internal Server Error) view +` for more information. + Notes on capturing text in URLs =============================== diff --git a/docs/topics/http/views.txt b/docs/topics/http/views.txt index a8c85ddf6e..c4bd15e72e 100644 --- a/docs/topics/http/views.txt +++ b/docs/topics/http/views.txt @@ -127,6 +127,8 @@ called ``404.html`` and located in the top level of your template tree. Customizing error views ======================= +.. _http_not_found_view: + The 404 (page not found) view ----------------------------- @@ -167,6 +169,8 @@ Four things to note about 404 views: your 404 view will never be used, and your URLconf will be displayed instead, with some debug information. +.. _http_internal_server_error_view: + The 500 (server error) view ---------------------------- -- cgit v1.3 From 1288572d9203472be8aa28e956027899fec3ed92 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 20 Aug 2012 18:23:17 +0200 Subject: Made an example more readable in the URLs docs. --- docs/topics/http/urls.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'docs') diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index 819ce24b91..7297184ed3 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -574,10 +574,8 @@ For example:: (r'^blog/(?P\d{4})/$', 'year_archive', {'foo': 'bar'}), ) -In this example, for a request to ``/blog/2005/``, Django will call the -``blog.views.year_archive()`` view, passing it these keyword arguments:: - - year='2005', foo='bar' +In this example, for a request to ``/blog/2005/``, Django will call +``blog.views.year_archive(year='2005', foo='bar')``. This technique is used in the :doc:`syndication framework ` to pass metadata and -- cgit v1.3 From 610746f6cb89c13b364c27fc41ecc9592cbe1605 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Mon, 20 Aug 2012 21:21:00 +0200 Subject: Fixed #18023 -- Documented simplejson issues. Thanks Luke Plant for reporting the issue and Alex Ogier for thoroughly investigating it. --- docs/releases/1.5.txt | 62 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) (limited to 'docs') diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 4f25919d79..6042569cea 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -172,6 +172,54 @@ If you were using the ``data`` parameter in a PUT request without a ``content_type``, you must encode your data before passing it to the test client and set the ``content_type`` argument. +.. _simplejson-incompatibilities: + +System version of :mod:`simplejson` no longer used +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:ref:`As explained below `, Django 1.5 deprecates +:mod:`django.utils.simplejson` in favor of Python 2.6's built-in :mod:`json` +module. In theory, this change is harmless. Unfortunately, because of +incompatibilities between versions of :mod:`simplejson`, it may trigger errors +in some circumstances. + +JSON-related features in Django 1.4 always used :mod:`django.utils.simplejson`. +This module was actually: + +- A system version of :mod:`simplejson`, if one was available (ie. ``import + simplejson`` works), if it was more recent than Django's built-in copy or it + had the C speedups, or +- The :mod:`json` module from the standard library, if it was available (ie. + Python 2.6 or greater), or +- A built-in copy of version 2.0.7 of :mod:`simplejson`. + +In Django 1.5, those features use Python's :mod:`json` module, which is based +on version 2.0.9 of :mod:`simplejson`. + +There are no known incompatibilities between Django's copy of version 2.0.7 and +Python's copy of version 2.0.9. However, there are some incompatibilities +between other versions of :mod:`simplejson`: + +- While the :mod:`simplejson` API is documented as always returning unicode + strings, the optional C implementation can return a byte string. This was + fixed in Python 2.7. +- :class:`simplejson.JSONEncoder` gained a ``namedtuple_as_object`` keyword + argument in version 2.2. + +More information on these incompatibilities is available in `ticket #18023`_. + +The net result is that, if you have installed :mod:`simplejson` and your code +uses Django's serialization internals directly -- for instance +:class:`django.core.serializers.json.DjangoJSONEncoder`, the switch from +:mod:`simplejson` to :mod:`json` could break your code. (In general, changes to +internals aren't documented; we're making an exception here.) + +At this point, the maintainers of Django believe that using :mod:`json` from +the standard library offers the strongest guarantee of backwards-compatibility. +They recommend to use it from now on. + +.. _ticket #18023: https://code.djangoproject.com/ticket/18023#comment:10 + String types of hasher method parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -282,13 +330,21 @@ Miscellaneous Features deprecated in 1.5 ========================== +.. _simplejson-deprecation: + ``django.utils.simplejson`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Since Django 1.5 drops support for Python 2.5, we can now rely on the -:mod:`json` module being in Python's standard library -- so we've removed -our own copy of ``simplejson``. You can safely change any use of -:mod:`django.utils.simplejson` to :mod:`json`. +:mod:`json` module being available in Python's standard library, so we've +removed our own copy of :mod:`simplejson`. You should now import :mod:`json` +instead :mod:`django.utils.simplejson`. + +Unfortunately, this change might have unwanted side-effects, because of +incompatibilities between versions of :mod:`simplejson` -- see the +:ref:`backwards-incompatible changes ` section. +If you rely on features added to :mod:`simplejson` after it became Python's +:mod:`json`, you should import :mod:`simplejson` explicitly. ``itercompat.product`` ~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.3 From 831f2846dd9b3a93344e3c5cb3ea57d76317ae2c Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Mon, 20 Aug 2012 14:27:28 -0700 Subject: Fixed #18819 -- fixed some typos in the auth docs --- docs/topics/auth.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'docs') diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt index 307691bd4a..a4e0f677b5 100644 --- a/docs/topics/auth.txt +++ b/docs/topics/auth.txt @@ -460,7 +460,7 @@ algorithm. Increasing the work factor ~~~~~~~~~~~~~~~~~~~~~~~~~~ -The PDKDF2 and bcrypt algorithms use a number of iterations or rounds of +The PBKDF2 and bcrypt algorithms use a number of iterations or rounds of hashing. This deliberately slows down attackers, making attacks against hashed passwords harder. However, as computing power increases, the number of iterations needs to be increased. We've chosen a reasonable default (and will @@ -468,7 +468,7 @@ increase it with each release of Django), but you may wish to tune it up or down, depending on your security needs and available processing power. To do so, you'll subclass the appropriate algorithm and override the ``iterations`` parameters. For example, to increase the number of iterations used by the -default PDKDF2 algorithm: +default PBKDF2 algorithm: 1. Create a subclass of ``django.contrib.auth.hashers.PBKDF2PasswordHasher``:: -- cgit v1.3 From 32ffcb21a00e08eb478cc287bc3254ee765d815d Mon Sep 17 00:00:00 2001 From: Michael Johnson Date: Tue, 21 Aug 2012 09:01:11 -0400 Subject: Fixed #17069 -- Added log filter example to docs. Added an example of filtering admin error emails (to exclude UnreadablePostErrors) to the docs. --- docs/topics/logging.txt | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'docs') diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt index 9bdd706355..b54a9475ae 100644 --- a/docs/topics/logging.txt +++ b/docs/topics/logging.txt @@ -516,6 +516,35 @@ logging module. through the filter. Handling of that record will not proceed if the callback returns False. + For instance, to filter out :class:`~django.http.UnreadablePostError` + (raised when a user cancels an upload) from the admin emails, you would + create a filter function:: + + from django.http import UnreadablePostError + + def skip_unreadable_post(record): + if record.exc_info: + exc_type, exc_value = record.exc_info[:2] + if isinstance(exc_value, UnreadablePostError): + return False + return True + + and then add it to your logging config:: + + 'filters': { + 'skip_unreadable_posts': { + '()': 'django.utils.log.CallbackFilter', + 'callback': skip_unreadable_post, + } + }, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['skip_unreadable_posts'], + 'class': 'django.utils.log.AdminEmailHandler' + } + }, + .. class:: RequireDebugFalse() .. versionadded:: 1.4 -- cgit v1.3 From 3fd89d99036696ba08dd2dd7e20a5b375f85d23b Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Tue, 21 Aug 2012 17:32:53 -0400 Subject: Fixed #14885 - Clarified that ModelForm cleaning may not fully complete if the form is invalid. Thanks Ben Sturmfels for the patch. --- docs/topics/forms/modelforms.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt index 0ca37745c7..8159c8850c 100644 --- a/docs/topics/forms/modelforms.txt +++ b/docs/topics/forms/modelforms.txt @@ -200,7 +200,9 @@ The first time you call ``is_valid()`` or access the ``errors`` attribute of a well as :ref:`model validation `. This has the side-effect of cleaning the model you pass to the ``ModelForm`` constructor. For instance, calling ``is_valid()`` on your form will convert any date fields on your model -to actual date objects. +to actual date objects. If form validation fails, only some of the updates +may be applied. For this reason, you'll probably want to avoid reusing the +model instance. The ``save()`` method -- cgit v1.3 From 13d47c3f338e1e9a5da943b97b5334c0523d2e2c Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Tue, 21 Aug 2012 16:29:16 -0400 Subject: Fixed #18637 - Updated some documentation for aspects of models that are ModelForm specific, not admin specific. Thanks Ben Sturmfels for the patch. --- docs/ref/models/fields.txt | 90 +++++++++++++++++++++++----------------------- docs/topics/db/models.txt | 28 +++++++-------- 2 files changed, 58 insertions(+), 60 deletions(-) (limited to 'docs') diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index a43163c5e9..29cde19c78 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -70,8 +70,8 @@ If ``True``, the field is allowed to be blank. Default is ``False``. Note that this is different than :attr:`~Field.null`. :attr:`~Field.null` is purely database-related, whereas :attr:`~Field.blank` is validation-related. If -a field has ``blank=True``, validation on Django's admin site will allow entry -of an empty value. If a field has ``blank=False``, the field will be required. +a field has ``blank=True``, form validation will allow entry of an empty value. +If a field has ``blank=False``, the field will be required. .. _field-choices: @@ -81,14 +81,11 @@ of an empty value. If a field has ``blank=False``, the field will be required. .. attribute:: Field.choices An iterable (e.g., a list or tuple) of 2-tuples to use as choices for this -field. +field. If this is given, the default form widget will be a select box with +these choices instead of the standard text field. -If this is given, Django's admin will use a select box instead of the standard -text field and will limit choices to the choices given. - -A choices list is an iterable of 2-tuples; the first element in each -tuple is the actual value to be stored, and the second element is the -human-readable name. For example:: +The first element in each tuple is the actual value to be stored, and the +second element is the human-readable name. For example:: YEAR_IN_SCHOOL_CHOICES = ( ('FR', 'Freshman'), @@ -203,8 +200,8 @@ callable it will be called every time a new object is created. .. attribute:: Field.editable -If ``False``, the field will not be editable in the admin or via forms -automatically generated from the model class. Default is ``True``. +If ``False``, the field will not be displayed in the admin or any other +:class:`~django.forms.ModelForm`. Default is ``True``. ``error_messages`` ------------------ @@ -224,11 +221,11 @@ the `Field types`_ section below. .. attribute:: Field.help_text -Extra "help" text to be displayed under the field on the object's admin form. -It's useful for documentation even if your object doesn't have an admin form. +Extra "help" text to be displayed with the form widget. It's useful for +documentation even if your field isn't used on a form. -Note that this value is *not* HTML-escaped when it's displayed in the admin -interface. This lets you include HTML in :attr:`~Field.help_text` if you so +Note that this value is *not* HTML-escaped in automatically-generated +forms. This lets you include HTML in :attr:`~Field.help_text` if you so desire. For example:: help_text="Please use the following format: YYYY-MM-DD." @@ -259,7 +256,7 @@ Only one primary key is allowed on an object. If ``True``, this field must be unique throughout the table. -This is enforced at the database level and at the Django admin-form level. If +This is enforced at the database level and by model validation. If you try to save a model with a duplicate value in a :attr:`~Field.unique` field, a :exc:`django.db.IntegrityError` will be raised by the model's :meth:`~django.db.models.Model.save` method. @@ -279,7 +276,7 @@ For example, if you have a field ``title`` that has ``unique_for_date="pub_date"``, then Django wouldn't allow the entry of two records with the same ``title`` and ``pub_date``. -This is enforced at the Django admin-form level but not at the database level. +This is enforced by model validation but not at the database level. ``unique_for_month`` -------------------- @@ -337,7 +334,7 @@ otherwise. See :ref:`automatic-primary-key-fields`. A 64 bit integer, much like an :class:`IntegerField` except that it is guaranteed to fit numbers from -9223372036854775808 to 9223372036854775807. The -admin represents this as an ```` (a single-line input). +default form widget for this field is a :class:`~django.forms.TextInput`. ``BooleanField`` @@ -347,7 +344,8 @@ admin represents this as an ```` (a single-line input). A true/false field. -The admin represents this as a checkbox. +The default form widget for this field is a +:class:`~django.forms.CheckboxInput`. If you need to accept :attr:`~Field.null` values then use :class:`NullBooleanField` instead. @@ -361,7 +359,7 @@ A string field, for small- to large-sized strings. For large amounts of text, use :class:`~django.db.models.TextField`. -The admin represents this as an ```` (a single-line input). +The default form widget for this field is a :class:`~django.forms.TextInput`. :class:`CharField` has one extra required argument: @@ -414,9 +412,10 @@ optional arguments: for creation of timestamps. Note that the current date is *always* used; it's not just a default value that you can override. -The admin represents this as an ```` with a JavaScript -calendar, and a shortcut for "Today". Includes an additional ``invalid_date`` -error message key. +The default form widget for this field is a +:class:`~django.forms.TextInput`. The admin adds a JavaScript calendar, +and a shortcut for "Today". Includes an additional ``invalid_date`` error +message key. .. note:: As currently implemented, setting ``auto_now`` or ``auto_now_add`` to @@ -431,8 +430,9 @@ error message key. A date and time, represented in Python by a ``datetime.datetime`` instance. Takes the same extra arguments as :class:`DateField`. -The admin represents this as two ```` fields, with -JavaScript shortcuts. +The default form widget for this field is a single +:class:`~django.forms.TextInput`. The admin uses two separate +:class:`~django.forms.TextInput` widgets with JavaScript shortcuts. ``DecimalField`` ---------------- @@ -461,7 +461,7 @@ decimal places:: models.DecimalField(..., max_digits=19, decimal_places=10) -The admin represents this as an ```` (a single-line input). +The default form widget for this field is a :class:`~django.forms.TextInput`. .. note:: @@ -539,8 +539,8 @@ Also has one optional argument: Optional. A storage object, which handles the storage and retrieval of your files. See :doc:`/topics/files` for details on how to provide this object. -The admin represents this field as an ```` (a file-upload -widget). +The default form widget for this field is a +:class:`~django.forms.widgets.FileInput`. Using a :class:`FileField` or an :class:`ImageField` (see below) in a model takes a few steps: @@ -725,7 +725,7 @@ can change the maximum length using the :attr:`~CharField.max_length` argument. A floating-point number represented in Python by a ``float`` instance. -The admin represents this as an ```` (a single-line input). +The default form widget for this field is a :class:`~django.forms.TextInput`. .. _floatfield_vs_decimalfield: @@ -776,16 +776,16 @@ length using the :attr:`~CharField.max_length` argument. .. class:: IntegerField([**options]) -An integer. The admin represents this as an ```` (a -single-line input). +An integer. The default form widget for this field is a +:class:`~django.forms.TextInput`. ``IPAddressField`` ------------------ .. class:: IPAddressField([**options]) -An IP address, in string format (e.g. "192.0.2.30"). The admin represents this -as an ```` (a single-line input). +An IP address, in string format (e.g. "192.0.2.30"). The default form widget +for this field is a :class:`~django.forms.TextInput`. ``GenericIPAddressField`` ------------------------- @@ -795,8 +795,8 @@ as an ```` (a single-line input). .. versionadded:: 1.4 An IPv4 or IPv6 address, in string format (e.g. ``192.0.2.30`` or -``2a02:42fe::4``). The admin represents this as an ```` -(a single-line input). +``2a02:42fe::4``). The default form widget for this field is a +:class:`~django.forms.TextInput`. The IPv6 address normalization follows :rfc:`4291#section-2.2` section 2.2, including using the IPv4 format suggested in paragraph 3 of that section, like @@ -823,8 +823,8 @@ are converted to lowercase. .. class:: NullBooleanField([**options]) Like a :class:`BooleanField`, but allows ``NULL`` as one of the options. Use -this instead of a :class:`BooleanField` with ``null=True``. The admin represents -this as a ``