mirror of
https://github.com/django/django.git
synced 2025-11-19 03:08:59 +00:00
Merged master changes.
This commit is contained in:
commit
7e82e83d67
146 changed files with 2278 additions and 875 deletions
|
|
@ -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``::
|
||||
|
||||
|
|
@ -1170,24 +1170,25 @@ includes a few other useful built-in views located in
|
|||
:file:`registration/password_reset_form.html` if not supplied.
|
||||
|
||||
* ``email_template_name``: The full name of a template to use for
|
||||
generating the email with the new password. Defaults to
|
||||
generating the email with the reset password link. Defaults to
|
||||
:file:`registration/password_reset_email.html` if not supplied.
|
||||
|
||||
* ``subject_template_name``: The full name of a template to use for
|
||||
the subject of the email with the new password. Defaults
|
||||
the subject of the email with the reset password link. Defaults
|
||||
to :file:`registration/password_reset_subject.txt` if not supplied.
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
* ``password_reset_form``: Form that will be used to set the password.
|
||||
Defaults to :class:`~django.contrib.auth.forms.PasswordResetForm`.
|
||||
* ``password_reset_form``: Form that will be used to get the email of
|
||||
the user to reset the password for. Defaults to
|
||||
:class:`~django.contrib.auth.forms.PasswordResetForm`.
|
||||
|
||||
* ``token_generator``: Instance of the class to check the password. This
|
||||
will default to ``default_token_generator``, it's an instance of
|
||||
* ``token_generator``: Instance of the class to check the one time link.
|
||||
This will default to ``default_token_generator``, it's an instance of
|
||||
``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
|
||||
|
||||
* ``post_reset_redirect``: The URL to redirect to after a successful
|
||||
password change.
|
||||
password reset request.
|
||||
|
||||
* ``from_email``: A valid email address. By default Django uses
|
||||
the :setting:`DEFAULT_FROM_EMAIL`.
|
||||
|
|
@ -1218,7 +1219,7 @@ includes a few other useful built-in views located in
|
|||
|
||||
* ``uid``: The user's id encoded in base 36.
|
||||
|
||||
* ``token``: Token to check that the password is valid.
|
||||
* ``token``: Token to check that the reset link is valid.
|
||||
|
||||
Sample ``registration/password_reset_email.html`` (email body template):
|
||||
|
||||
|
|
|
|||
|
|
@ -864,7 +864,7 @@ key version to provide a final cache key. By default, the three parts
|
|||
are joined using colons to produce a final string::
|
||||
|
||||
def make_key(key, key_prefix, version):
|
||||
return ':'.join([key_prefix, str(version), smart_bytes(key)])
|
||||
return ':'.join([key_prefix, str(version), key])
|
||||
|
||||
If you want to combine the parts in different ways, or apply other
|
||||
processing to the final key (e.g., taking a hash digest of the key
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ to structure your views and reuse code by harnessing inheritance and
|
|||
mixins. There are also some generic views for simple tasks which we'll
|
||||
get to later, but you may want to design your own structure of
|
||||
reusable views which suits your use case. For full details, see the
|
||||
:doc:`class-based views reference
|
||||
documentation</ref/class-based-views/index>`.
|
||||
:doc:`class-based views reference documentation</ref/class-based-views/index>`.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
|
@ -32,19 +31,35 @@ redirect, and :class:`~django.views.generic.base.TemplateView` extends the base
|
|||
to make it also render a template.
|
||||
|
||||
|
||||
Simple usage
|
||||
============
|
||||
Simple usage in your URLconf
|
||||
============================
|
||||
|
||||
Class-based generic views (and any class-based views that inherit from
|
||||
the base classes Django provides) can be configured in two
|
||||
ways: subclassing, or passing in arguments directly in the URLconf.
|
||||
The simplest way to use generic views is to create them directly in your
|
||||
URLconf. If you're only changing a few simple attributes on a class-based view,
|
||||
you can simply pass them into the ``as_view`` method call itself::
|
||||
|
||||
When you subclass a class-based view, you can override attributes
|
||||
(such as the ``template_name``) or methods (such as ``get_context_data``)
|
||||
in your subclass to provide new values or methods. Consider, for example,
|
||||
a view that just displays one template, ``about.html``. Django has a
|
||||
generic view to do this - :class:`~django.views.generic.base.TemplateView` -
|
||||
so we can just subclass it, and override the template name::
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', TemplateView.as_view(template_name="about.html")),
|
||||
)
|
||||
|
||||
Any arguments given will override the ``template_name`` on the
|
||||
A similar overriding pattern can be used for the ``url`` attribute on
|
||||
:class:`~django.views.generic.base.RedirectView`.
|
||||
|
||||
|
||||
Subclassing generic views
|
||||
=========================
|
||||
|
||||
The second, more powerful way to use generic views is to inherit from an
|
||||
existing view and override attributes (such as the ``template_name``) or
|
||||
methods (such as ``get_context_data``) in your subclass to provide new values
|
||||
or methods. Consider, for example, a view that just displays one template,
|
||||
``about.html``. Django has a generic view to do this -
|
||||
:class:`~django.views.generic.base.TemplateView` - so we can just subclass it,
|
||||
and override the template name::
|
||||
|
||||
# some_app/views.py
|
||||
from django.views.generic import TemplateView
|
||||
|
|
@ -52,9 +67,10 @@ so we can just subclass it, and override the template name::
|
|||
class AboutView(TemplateView):
|
||||
template_name = "about.html"
|
||||
|
||||
Then, we just need to add this new view into our URLconf. As the class-based
|
||||
views themselves are classes, we point the URL to the ``as_view`` class method
|
||||
instead, which is the entry point for class-based views::
|
||||
Then we just need to add this new view into our URLconf.
|
||||
`~django.views.generic.base.TemplateView` is a class, not a function, so we
|
||||
point the URL to the ``as_view`` class method instead, which provides a
|
||||
function-like entry to class-based views::
|
||||
|
||||
# urls.py
|
||||
from django.conf.urls import patterns, url, include
|
||||
|
|
@ -64,104 +80,6 @@ instead, which is the entry point for class-based views::
|
|||
(r'^about/', AboutView.as_view()),
|
||||
)
|
||||
|
||||
Alternatively, if you're only changing a few simple attributes on a
|
||||
class-based view, you can simply pass the new attributes into the ``as_view``
|
||||
method call itself::
|
||||
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', TemplateView.as_view(template_name="about.html")),
|
||||
)
|
||||
|
||||
A similar overriding pattern can be used for the ``url`` attribute on
|
||||
:class:`~django.views.generic.base.RedirectView`.
|
||||
|
||||
.. _jsonresponsemixin-example:
|
||||
|
||||
More than just HTML
|
||||
-------------------
|
||||
|
||||
Where class based views shine is when you want to do the same thing many times.
|
||||
Suppose you're writing an API, and every view should return JSON instead of
|
||||
rendered HTML.
|
||||
|
||||
We can create a mixin class to use in all of our views, handling the
|
||||
conversion to JSON once.
|
||||
|
||||
For example, a simple JSON mixin might look something like this::
|
||||
|
||||
import json
|
||||
from django.http import HttpResponse
|
||||
|
||||
class JSONResponseMixin(object):
|
||||
"""
|
||||
A mixin that can be used to render a JSON response.
|
||||
"""
|
||||
response_class = HttpResponse
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
"""
|
||||
Returns a JSON response, transforming 'context' to make the payload.
|
||||
"""
|
||||
response_kwargs['content_type'] = 'application/json'
|
||||
return self.response_class(
|
||||
self.convert_context_to_json(context),
|
||||
**response_kwargs
|
||||
)
|
||||
|
||||
def convert_context_to_json(self, context):
|
||||
"Convert the context dictionary into a JSON object"
|
||||
# Note: This is *EXTREMELY* naive; in reality, you'll need
|
||||
# to do much more complex handling to ensure that arbitrary
|
||||
# objects -- such as Django model instances or querysets
|
||||
# -- can be serialized as JSON.
|
||||
return json.dumps(context)
|
||||
|
||||
Now we mix this into the base TemplateView::
|
||||
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class JSONView(JSONResponseMixin, TemplateView):
|
||||
pass
|
||||
|
||||
Equally we could use our mixin with one of the generic views. We can make our
|
||||
own version of :class:`~django.views.generic.detail.DetailView` by mixing
|
||||
:class:`JSONResponseMixin` with the
|
||||
:class:`~django.views.generic.detail.BaseDetailView` -- (the
|
||||
:class:`~django.views.generic.detail.DetailView` before template
|
||||
rendering behavior has been mixed in)::
|
||||
|
||||
class JSONDetailView(JSONResponseMixin, BaseDetailView):
|
||||
pass
|
||||
|
||||
This view can then be deployed in the same way as any other
|
||||
:class:`~django.views.generic.detail.DetailView`, with exactly the
|
||||
same behavior -- except for the format of the response.
|
||||
|
||||
If you want to be really adventurous, you could even mix a
|
||||
:class:`~django.views.generic.detail.DetailView` subclass that is able
|
||||
to return *both* HTML and JSON content, depending on some property of
|
||||
the HTTP request, such as a query argument or a HTTP header. Just mix
|
||||
in both the :class:`JSONResponseMixin` and a
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
|
||||
and override the implementation of :func:`render_to_response()` to defer
|
||||
to the appropriate subclass depending on the type of response that the user
|
||||
requested::
|
||||
|
||||
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
|
||||
def render_to_response(self, context):
|
||||
# Look for a 'format=json' GET argument
|
||||
if self.request.GET.get('format','html') == 'json':
|
||||
return JSONResponseMixin.render_to_response(self, context)
|
||||
else:
|
||||
return SingleObjectTemplateResponseMixin.render_to_response(self, context)
|
||||
|
||||
Because of the way that Python resolves method overloading, the local
|
||||
``render_to_response()`` implementation will override the versions provided by
|
||||
:class:`JSONResponseMixin` and
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
||||
|
||||
For more information on how to use the built in generic views, consult the next
|
||||
topic on :doc:`generic class based views</topics/class-based-views/generic-display>`.
|
||||
|
|
@ -171,16 +89,15 @@ Decorating class-based views
|
|||
|
||||
.. highlightlang:: python
|
||||
|
||||
The extension of class-based views isn't limited to using mixins. You
|
||||
can use also use decorators.
|
||||
Since class-based views aren't functions, decorating them works differently
|
||||
depending on if you're using ``as_view`` or creating a subclass.
|
||||
|
||||
Decorating in URLconf
|
||||
---------------------
|
||||
|
||||
The simplest way of decorating class-based views is to decorate the
|
||||
result of the :meth:`~django.views.generic.base.View.as_view` method.
|
||||
The easiest place to do this is in the URLconf where you deploy your
|
||||
view::
|
||||
The easiest place to do this is in the URLconf where you deploy your view::
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.views.generic import TemplateView
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ interface to working with templates in class-based views.
|
|||
add more members to the dictionary.
|
||||
|
||||
Building up Django's generic class-based views
|
||||
===============================================
|
||||
==============================================
|
||||
|
||||
Let's look at how two of Django's generic class-based views are built
|
||||
out of mixins providing discrete functionality. We'll consider
|
||||
|
|
@ -222,8 +222,7 @@ we'll want the functionality provided by
|
|||
:class:`~django.views.generic.detail.SingleObjectMixin`.
|
||||
|
||||
We'll demonstrate this with the publisher modelling we used in the
|
||||
:doc:`generic class-based views
|
||||
introduction<generic-display>`.
|
||||
:doc:`generic class-based views introduction<generic-display>`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
|
@ -233,11 +232,11 @@ introduction<generic-display>`.
|
|||
from django.views.generic import View
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from books.models import Author
|
||||
|
||||
|
||||
class RecordInterest(View, SingleObjectMixin):
|
||||
"""Records the current user's interest in an author."""
|
||||
model = Author
|
||||
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated():
|
||||
return HttpResponseForbidden()
|
||||
|
|
@ -256,9 +255,7 @@ we're interested in, which it just does with a simple call to
|
|||
``self.get_object()``. Everything else is taken care of for us by the
|
||||
mixin.
|
||||
|
||||
We can hook this into our URLs easily enough:
|
||||
|
||||
.. code-block:: python
|
||||
We can hook this into our URLs easily enough::
|
||||
|
||||
# urls.py
|
||||
from books.views import RecordInterest
|
||||
|
|
@ -294,8 +291,6 @@ object. In order to do this, we need to have two different querysets:
|
|||
We'll figure that out ourselves in :meth:`get_queryset()` so we
|
||||
can take into account the Publisher we're looking at.
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
.. note::
|
||||
|
||||
We have to think carefully about :meth:`get_context_data()`.
|
||||
|
|
@ -311,15 +306,15 @@ Now we can write a new :class:`PublisherDetail`::
|
|||
from django.views.generic import ListView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from books.models import Publisher
|
||||
|
||||
|
||||
class PublisherDetail(SingleObjectMixin, ListView):
|
||||
paginate_by = 2
|
||||
template_name = "books/publisher_detail.html"
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['publisher'] = self.object
|
||||
return super(PublisherDetail, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
self.object = self.get_object(Publisher.objects.all())
|
||||
return self.object.book_set.all()
|
||||
|
|
@ -339,26 +334,26 @@ have to create lots of books to see the pagination working! Here's the
|
|||
template you'd want to use::
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h2>Publisher {{ publisher.name }}</h2>
|
||||
|
||||
|
||||
<ol>
|
||||
{% for book in page_obj %}
|
||||
<li>{{ book.title }}</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
|
||||
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<span class="current">
|
||||
Page {{ page_obj.number }} of {{ paginator.num_pages }}.
|
||||
</span>
|
||||
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}">next</a>
|
||||
{% endif %}
|
||||
|
|
@ -428,9 +423,9 @@ code so that on ``POST`` the form gets called appropriately.
|
|||
the views implement :meth:`get()`, and things would get much more
|
||||
confusing.
|
||||
|
||||
Our new :class:`AuthorDetail` looks like this:
|
||||
.. highlightlang:: python
|
||||
|
||||
.. code-block:: python
|
||||
Our new :class:`AuthorDetail` looks like this::
|
||||
|
||||
# CAUTION: you almost certainly do not want to do this.
|
||||
# It is provided as part of a discussion of problems you can
|
||||
|
|
@ -455,7 +450,7 @@ Our new :class:`AuthorDetail` looks like this:
|
|||
'author-detail',
|
||||
kwargs = {'pk': self.object.pk},
|
||||
)
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
form_class = self.get_form_class()
|
||||
form = self.get_form(form_class)
|
||||
|
|
@ -464,7 +459,7 @@ Our new :class:`AuthorDetail` looks like this:
|
|||
}
|
||||
context.update(kwargs)
|
||||
return super(AuthorDetail, self).get_context_data(**context)
|
||||
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form_class = self.get_form_class()
|
||||
form = self.get_form(form_class)
|
||||
|
|
@ -472,14 +467,14 @@ Our new :class:`AuthorDetail` looks like this:
|
|||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.is_authenticated():
|
||||
return HttpResponseForbidden()
|
||||
self.object = self.get_object()
|
||||
# record the interest using the message in form.cleaned_data
|
||||
return super(AuthorDetail, self).form_valid(form)
|
||||
|
||||
|
||||
:meth:`get_success_url()` is just providing somewhere to redirect to,
|
||||
which gets used in the default implementation of
|
||||
:meth:`form_valid()`. We have to provide our own :meth:`post()` as
|
||||
|
|
@ -525,21 +520,21 @@ write our own :meth:`get_context_data()` to make the
|
|||
from django.views.generic import DetailView
|
||||
from django import forms
|
||||
from books.models import Author
|
||||
|
||||
|
||||
class AuthorInterestForm(forms.Form):
|
||||
message = forms.CharField()
|
||||
|
||||
|
||||
class AuthorDisplay(DetailView):
|
||||
|
||||
|
||||
queryset = Author.objects.all()
|
||||
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'form': AuthorInterestForm(),
|
||||
}
|
||||
context.update(kwargs)
|
||||
return super(AuthorDisplay, self).get_context_data(**context)
|
||||
|
||||
|
||||
Then the :class:`AuthorInterest` is a simple :class:`FormView`, but we
|
||||
have to bring in :class:`SingleObjectMixin` so we can find the author
|
||||
we're talking about, and we have to remember to set
|
||||
|
|
@ -550,7 +545,7 @@ template as :class:`AuthorDisplay` is using on ``GET``.
|
|||
|
||||
from django.views.generic import FormView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
|
||||
|
||||
class AuthorInterest(FormView, SingleObjectMixin):
|
||||
template_name = 'books/author_detail.html'
|
||||
form_class = AuthorInterestForm
|
||||
|
|
@ -561,13 +556,13 @@ template as :class:`AuthorDisplay` is using on ``GET``.
|
|||
'object': self.get_object(),
|
||||
}
|
||||
return super(AuthorInterest, self).get_context_data(**context)
|
||||
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(
|
||||
'author-detail',
|
||||
kwargs = {'pk': self.object.pk},
|
||||
)
|
||||
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.is_authenticated():
|
||||
return HttpResponseForbidden()
|
||||
|
|
@ -588,13 +583,13 @@ using a different template.
|
|||
.. code-block:: python
|
||||
|
||||
from django.views.generic import View
|
||||
|
||||
|
||||
class AuthorDetail(View):
|
||||
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
view = AuthorDisplay.as_view()
|
||||
return view(request, *args, **kwargs)
|
||||
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
view = AuthorInterest.as_view()
|
||||
return view(request, *args, **kwargs)
|
||||
|
|
@ -603,3 +598,88 @@ This approach can also be used with any other generic class-based
|
|||
views or your own class-based views inheriting directly from
|
||||
:class:`View` or :class:`TemplateView`, as it keeps the different
|
||||
views as separate as possible.
|
||||
|
||||
.. _jsonresponsemixin-example:
|
||||
|
||||
More than just HTML
|
||||
===================
|
||||
|
||||
Where class based views shine is when you want to do the same thing many times.
|
||||
Suppose you're writing an API, and every view should return JSON instead of
|
||||
rendered HTML.
|
||||
|
||||
We can create a mixin class to use in all of our views, handling the
|
||||
conversion to JSON once.
|
||||
|
||||
For example, a simple JSON mixin might look something like this::
|
||||
|
||||
import json
|
||||
from django.http import HttpResponse
|
||||
|
||||
class JSONResponseMixin(object):
|
||||
"""
|
||||
A mixin that can be used to render a JSON response.
|
||||
"""
|
||||
response_class = HttpResponse
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
"""
|
||||
Returns a JSON response, transforming 'context' to make the payload.
|
||||
"""
|
||||
response_kwargs['content_type'] = 'application/json'
|
||||
return self.response_class(
|
||||
self.convert_context_to_json(context),
|
||||
**response_kwargs
|
||||
)
|
||||
|
||||
def convert_context_to_json(self, context):
|
||||
"Convert the context dictionary into a JSON object"
|
||||
# Note: This is *EXTREMELY* naive; in reality, you'll need
|
||||
# to do much more complex handling to ensure that arbitrary
|
||||
# objects -- such as Django model instances or querysets
|
||||
# -- can be serialized as JSON.
|
||||
return json.dumps(context)
|
||||
|
||||
Now we mix this into the base TemplateView::
|
||||
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class JSONView(JSONResponseMixin, TemplateView):
|
||||
pass
|
||||
|
||||
Equally we could use our mixin with one of the generic views. We can make our
|
||||
own version of :class:`~django.views.generic.detail.DetailView` by mixing
|
||||
:class:`JSONResponseMixin` with the
|
||||
:class:`~django.views.generic.detail.BaseDetailView` -- (the
|
||||
:class:`~django.views.generic.detail.DetailView` before template
|
||||
rendering behavior has been mixed in)::
|
||||
|
||||
class JSONDetailView(JSONResponseMixin, BaseDetailView):
|
||||
pass
|
||||
|
||||
This view can then be deployed in the same way as any other
|
||||
:class:`~django.views.generic.detail.DetailView`, with exactly the
|
||||
same behavior -- except for the format of the response.
|
||||
|
||||
If you want to be really adventurous, you could even mix a
|
||||
:class:`~django.views.generic.detail.DetailView` subclass that is able
|
||||
to return *both* HTML and JSON content, depending on some property of
|
||||
the HTTP request, such as a query argument or a HTTP header. Just mix
|
||||
in both the :class:`JSONResponseMixin` and a
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
|
||||
and override the implementation of :func:`render_to_response()` to defer
|
||||
to the appropriate subclass depending on the type of response that the user
|
||||
requested::
|
||||
|
||||
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
|
||||
def render_to_response(self, context):
|
||||
# Look for a 'format=json' GET argument
|
||||
if self.request.GET.get('format','html') == 'json':
|
||||
return JSONResponseMixin.render_to_response(self, context)
|
||||
else:
|
||||
return SingleObjectTemplateResponseMixin.render_to_response(self, context)
|
||||
|
||||
Because of the way that Python resolves method overloading, the local
|
||||
``render_to_response()`` implementation will override the versions provided by
|
||||
:class:`JSONResponseMixin` and
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ determine a few things:
|
|||
|
||||
* The database column type (e.g. ``INTEGER``, ``VARCHAR``).
|
||||
|
||||
* The :doc:`widget </ref/forms/widgets>` to use in Django's admin interface,
|
||||
if you care to use it (e.g. ``<input type="text">``, ``<select>``).
|
||||
* The default :doc:`widget </ref/forms/widgets>` to use when rendering a form
|
||||
field (e.g. ``<input type="text">``, ``<select>``).
|
||||
|
||||
* The minimal validation requirements, used in Django's admin and in
|
||||
automatically-generated forms.
|
||||
|
|
@ -143,13 +143,13 @@ ones:
|
|||
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
|
||||
:attr:`blank=True <Field.blank>`, validation on Django's admin site will
|
||||
:attr:`blank=True <Field.blank>`, form validation will
|
||||
allow entry of an empty value. If a field has :attr:`blank=False
|
||||
<Field.blank>`, the field will be required.
|
||||
|
||||
:attr:`~Field.choices`
|
||||
An iterable (e.g., a list or tuple) of 2-tuples to use as choices for
|
||||
this field. If this is given, Django's admin will use a select box
|
||||
this field. If this is given, the default form widget will be a select box
|
||||
instead of the standard text field and will limit choices to the choices
|
||||
given.
|
||||
|
||||
|
|
@ -164,7 +164,7 @@ ones:
|
|||
)
|
||||
|
||||
The first element in each tuple is the value that will be stored in the
|
||||
database, the second element will be displayed by the admin interface,
|
||||
database, the second element will be displayed by the default form widget
|
||||
or in a ModelChoiceField. Given an instance of a model object, the
|
||||
display value for a choices field can be accessed using the
|
||||
``get_FOO_display`` method. For example::
|
||||
|
|
@ -195,9 +195,8 @@ ones:
|
|||
created.
|
||||
|
||||
:attr:`~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.
|
||||
|
||||
:attr:`~Field.primary_key`
|
||||
If ``True``, this field is the primary key for the model.
|
||||
|
|
@ -360,13 +359,12 @@ It doesn't matter which model has the
|
|||
:class:`~django.db.models.ManyToManyField`, but you should only put it in one
|
||||
of the models -- not both.
|
||||
|
||||
Generally, :class:`~django.db.models.ManyToManyField` instances should go in the
|
||||
object that's going to be edited in the admin interface, if you're using
|
||||
Django's admin. In the above example, ``toppings`` is in ``Pizza`` (rather than
|
||||
``Topping`` having a ``pizzas`` :class:`~django.db.models.ManyToManyField` )
|
||||
because it's more natural to think about a pizza having toppings than a
|
||||
topping being on multiple pizzas. The way it's set up above, the ``Pizza`` admin
|
||||
form would let users select the toppings.
|
||||
Generally, :class:`~django.db.models.ManyToManyField` instances should go in
|
||||
the object that's going to be edited on a form. In the above example,
|
||||
``toppings`` is in ``Pizza`` (rather than ``Topping`` having a ``pizzas``
|
||||
:class:`~django.db.models.ManyToManyField` ) because it's more natural to think
|
||||
about a pizza having toppings than a topping being on multiple pizzas. The way
|
||||
it's set up above, the ``Pizza`` form would let users select the toppings.
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
|
|
|||
|
|
@ -525,7 +525,7 @@ registered with any ``Admin`` instance::
|
|||
admin.site.register(Author, MultiDBModelAdmin)
|
||||
admin.site.register(Publisher, PublisherAdmin)
|
||||
|
||||
othersite = admin.Site('othersite')
|
||||
othersite = admin.AdminSite('othersite')
|
||||
othersite.register(Publisher, MultiDBModelAdmin)
|
||||
|
||||
This example sets up two admin sites. On the first site, the
|
||||
|
|
|
|||
|
|
@ -235,14 +235,14 @@ refinements together. For example::
|
|||
... ).exclude(
|
||||
... pub_date__gte=datetime.now()
|
||||
... ).filter(
|
||||
... pub_date__gte=datetime(2005, 1, 1)
|
||||
... pub_date__gte=datetime(2005, 1, 30)
|
||||
... )
|
||||
|
||||
This takes the initial :class:`~django.db.models.query.QuerySet` of all entries
|
||||
in the database, adds a filter, then an exclusion, then another filter. The
|
||||
final result is a :class:`~django.db.models.query.QuerySet` containing all
|
||||
entries with a headline that starts with "What", that were published between
|
||||
January 1, 2005, and the current day.
|
||||
January 30, 2005, and the current day.
|
||||
|
||||
.. _filtered-querysets-are-unique:
|
||||
|
||||
|
|
@ -900,10 +900,10 @@ possible to easily create new instance with all fields' values copied. In the
|
|||
simplest case, you can just set ``pk`` to ``None``. Using our blog example::
|
||||
|
||||
blog = Blog(name='My blog', tagline='Blogging is easy')
|
||||
blog.save() # post.pk == 1
|
||||
blog.save() # blog.pk == 1
|
||||
|
||||
blog.pk = None
|
||||
blog.save() # post.pk == 2
|
||||
blog.save() # blog.pk == 2
|
||||
|
||||
Things get more complicated if you use inheritance. Consider a subclass of
|
||||
``Blog``::
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ through this property::
|
|||
|
||||
>>> w = CalendarWidget()
|
||||
>>> print(w.media)
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<link href="http://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://static.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
|
||||
|
||||
Here's a list of all possible ``Media`` options. There are no required options.
|
||||
|
||||
|
|
@ -110,9 +110,9 @@ requirements::
|
|||
|
||||
If this last CSS definition were to be rendered, it would become the following HTML::
|
||||
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="screen" rel="stylesheet" />
|
||||
<link href="http://media.example.com/lo_res.css" type="text/css" media="tv,projector" rel="stylesheet" />
|
||||
<link href="http://media.example.com/newspaper.css" type="text/css" media="print" rel="stylesheet" />
|
||||
<link href="http://static.example.com/pretty.css" type="text/css" media="screen" rel="stylesheet" />
|
||||
<link href="http://static.example.com/lo_res.css" type="text/css" media="tv,projector" rel="stylesheet" />
|
||||
<link href="http://static.example.com/newspaper.css" type="text/css" media="print" rel="stylesheet" />
|
||||
|
||||
``js``
|
||||
~~~~~~
|
||||
|
|
@ -140,11 +140,11 @@ basic Calendar widget from the example above::
|
|||
|
||||
>>> w = FancyCalendarWidget()
|
||||
>>> print(w.media)
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
<link href="http://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://static.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://static.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
|
||||
|
||||
The FancyCalendar widget inherits all the media from it's parent widget. If
|
||||
you don't want media to be inherited in this way, add an ``extend=False``
|
||||
|
|
@ -160,8 +160,8 @@ declaration to the media declaration::
|
|||
|
||||
>>> w = FancyCalendarWidget()
|
||||
>>> print(w.media)
|
||||
<link href="http://media.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
<link href="http://static.example.com/fancy.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
|
||||
|
||||
If you require even more control over media inheritance, define your media
|
||||
using a `dynamic property`_. Dynamic properties give you complete control over
|
||||
|
|
@ -253,12 +253,12 @@ to filter out a medium of interest. For example::
|
|||
|
||||
>>> w = CalendarWidget()
|
||||
>>> print(w.media)
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<link href="http://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://static.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
|
||||
|
||||
>>> print(w.media)['css']
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
|
||||
When you use the subscript operator, the value that is returned is a new
|
||||
Media object -- but one that only contains the media of interest.
|
||||
|
|
@ -283,10 +283,10 @@ the resulting Media object contains the union of the media from both files::
|
|||
>>> w1 = CalendarWidget()
|
||||
>>> w2 = OtherWidget()
|
||||
>>> print(w1.media + w2.media)
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
<link href="http://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://static.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
|
||||
|
||||
Media on Forms
|
||||
--------------
|
||||
|
|
@ -306,10 +306,10 @@ of adding the media definitions for all widgets that are part of the form::
|
|||
|
||||
>>> f = ContactForm()
|
||||
>>> f.media
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
<link href="http://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://static.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
|
||||
|
||||
If you want to associate additional media with a form -- for example, CSS for form
|
||||
layout -- simply add a media declaration to the form::
|
||||
|
|
@ -325,8 +325,8 @@ layout -- simply add a media declaration to the form::
|
|||
|
||||
>>> f = ContactForm()
|
||||
>>> f.media
|
||||
<link href="http://media.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://media.example.com/layout.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://media.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://media.example.com/whizbang.js"></script>
|
||||
<link href="http://static.example.com/pretty.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<link href="http://static.example.com/layout.css" type="text/css" media="all" rel="stylesheet" />
|
||||
<script type="text/javascript" src="http://static.example.com/animations.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/actions.js"></script>
|
||||
<script type="text/javascript" src="http://static.example.com/whizbang.js"></script>
|
||||
|
|
|
|||
|
|
@ -200,7 +200,9 @@ The first time you call ``is_valid()`` or access the ``errors`` attribute of a
|
|||
well as :ref:`model validation <validating-objects>`. 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
|
||||
|
|
|
|||
|
|
@ -183,7 +183,9 @@ 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
|
||||
makes this process much easier. The file object will be saved to the location
|
||||
specified by the :attr:`~django.db.models.FileField.upload_to` argument of the
|
||||
corresponding :class:`~django.db.models.FileField` when calling
|
||||
``form.save()``::
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
|
|
|
|||
|
|
@ -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
|
||||
<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
|
||||
<http_internal_server_error_view>` for more information.
|
||||
|
||||
Notes on capturing text in URLs
|
||||
===============================
|
||||
|
||||
|
|
@ -568,10 +574,8 @@ For example::
|
|||
(r'^blog/(?P<year>\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 </ref/contrib/syndication>` to pass metadata and
|
||||
|
|
|
|||
|
|
@ -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
|
||||
----------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -237,9 +237,9 @@ sometimes necessary::
|
|||
|
||||
value = value.encode('ascii', 'ignore').decode('ascii')
|
||||
|
||||
Be cautious if you have to `slice bytestrings`_.
|
||||
Be cautious if you have to `index bytestrings`_.
|
||||
|
||||
.. _slice bytestrings: http://docs.python.org/py3k/howto/pyporting.html#bytes-literals
|
||||
.. _index bytestrings: http://docs.python.org/py3k/howto/pyporting.html#bytes-literals
|
||||
|
||||
Exceptions
|
||||
~~~~~~~~~~
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ Use a dot (``.``) to access attributes of a variable.
|
|||
attempts to loop over a ``collections.defaultdict``::
|
||||
|
||||
{% for k, v in defaultdict.iteritems %}
|
||||
Do something with k and v here...
|
||||
Do something with k and v here...
|
||||
{% endfor %}
|
||||
|
||||
Because dictionary lookup happens first, that behavior kicks in and provides
|
||||
|
|
@ -116,6 +116,10 @@ If you use a variable that doesn't exist, the template system will insert
|
|||
the value of the :setting:`TEMPLATE_STRING_IF_INVALID` setting, which is set
|
||||
to ``''`` (the empty string) by default.
|
||||
|
||||
Note that "bar" in a template expression like ``{{ foo.bar }}`` will be
|
||||
interpreted as a literal string and not using the value of the variable "bar",
|
||||
if one exists in the template context.
|
||||
|
||||
Filters
|
||||
=======
|
||||
|
||||
|
|
|
|||
|
|
@ -2039,7 +2039,7 @@ out the `full reference`_ for more details.
|
|||
self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
|
||||
# Wait until the response is received
|
||||
WebDriverWait(self.selenium, timeout).until(
|
||||
lambda driver: driver.find_element_by_tag_name('body'), timeout=10)
|
||||
lambda driver: driver.find_element_by_tag_name('body'))
|
||||
|
||||
The tricky thing here is that there's really no such thing as a "page load,"
|
||||
especially in modern Web apps that generate HTML dynamically after the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue