Fixed #30573 -- Rephrased documentation to avoid words that minimise the involved difficulty.

This patch does not remove all occurrences of the words in question.
Rather, I went through all of the occurrences of the words listed
below, and judged if they a) suggested the reader had some kind of
knowledge/experience, and b) if they added anything of value (including
tone of voice, etc). I left most of the words alone. I looked at the
following words:

- simply/simple
- easy/easier/easiest
- obvious
- just
- merely
- straightforward
- ridiculous

Thanks to Carlton Gibson for guidance on how to approach this issue, and
to Tim Bell for providing the idea. But the enormous lion's share of
thanks go to Adam Johnson for his patient and helpful review.
This commit is contained in:
Tobias Kunze 2019-06-17 16:54:55 +02:00 committed by Mariusz Felisiak
parent addabc492b
commit 4a954cfd11
149 changed files with 1101 additions and 1157 deletions

View file

@ -111,8 +111,8 @@ Using ``REMOTE_USER`` on login pages only
The ``RemoteUserMiddleware`` authentication middleware assumes that the HTTP
request header ``REMOTE_USER`` is present with all authenticated requests. That
might be expected and practical when Basic HTTP Auth with ``htpasswd`` or other
simple mechanisms are used, but with Negotiate (GSSAPI/Kerberos) or other
might be expected and practical when Basic HTTP Auth with ``htpasswd`` or
similar mechanisms are used, but with Negotiate (GSSAPI/Kerberos) or other
resource intensive authentication methods, the authentication in the front-end
HTTP server is usually only set up for one or a few login URLs, and after
successful authentication, the application is supposed to maintain the

View file

@ -9,10 +9,10 @@ filtering (for example, ``exact`` and ``icontains``). This documentation
explains how to write custom lookups and how to alter the working of existing
lookups. For the API references of lookups, see the :doc:`/ref/models/lookups`.
A simple lookup example
=======================
A lookup example
================
Let's start with a simple custom lookup. We will write a custom lookup ``ne``
Let's start with a small custom lookup. We will write a custom lookup ``ne``
which works opposite to ``exact``. ``Author.objects.filter(name__ne='Jack')``
will translate to the SQL:
@ -24,8 +24,7 @@ This SQL is backend independent, so we don't need to worry about different
databases.
There are two steps to making this work. Firstly we need to implement the
lookup, then we need to tell Django about it. The implementation is quite
straightforward::
lookup, then we need to tell Django about it::
from django.db.models import Lookup
@ -38,10 +37,10 @@ straightforward::
params = lhs_params + rhs_params
return '%s <> %s' % (lhs, rhs), params
To register the ``NotEqual`` lookup we will just need to call
``register_lookup`` on the field class we want the lookup to be available. In
this case, the lookup makes sense on all ``Field`` subclasses, so we register
it with ``Field`` directly::
To register the ``NotEqual`` lookup we will need to call ``register_lookup`` on
the field class we want the lookup to be available for. In this case, the lookup
makes sense on all ``Field`` subclasses, so we register it with ``Field``
directly::
from django.db.models.fields import Field
Field.register_lookup(NotEqual)
@ -94,8 +93,8 @@ Finally we combine the parts into an SQL expression with ``<>``, and supply all
the parameters for the query. We then return a tuple containing the generated
SQL string and the parameters.
A simple transformer example
============================
A transformer example
=====================
The custom lookup above is great, but in some cases you may want to be able to
chain lookups together. For example, let's suppose we are building an
@ -241,10 +240,10 @@ want the transformation to be applied to both the left-hand side and the
right-hand side. For instance, if you want to filter a queryset based on the
equality of the left and right-hand side insensitively to some SQL function.
Let's examine the simple example of case-insensitive transformation here. This
transformation isn't very useful in practice as Django already comes with a bunch
of built-in case-insensitive lookups, but it will be a nice demonstration of
bilateral transformations in a database-agnostic way.
Let's examine case-insensitive transformations here. This transformation isn't
very useful in practice as Django already comes with a bunch of built-in
case-insensitive lookups, but it will be a nice demonstration of bilateral
transformations in a database-agnostic way.
We define an ``UpperCase`` transformer which uses the SQL function ``UPPER()`` to
transform the values before comparison. We define

View file

@ -10,9 +10,9 @@ distributing. In this document, we will be building a custom ``closepoll``
command for the ``polls`` application from the
:doc:`tutorial</intro/tutorial01>`.
To do this, just add a ``management/commands`` directory to the application.
Django will register a ``manage.py`` command for each Python module in that
directory whose name doesn't begin with an underscore. For example::
To do this, add a ``management/commands`` directory to the application. Django
will register a ``manage.py`` command for each Python module in that directory
whose name doesn't begin with an underscore. For example::
polls/
__init__.py

View file

@ -50,7 +50,7 @@ something like this::
.. _Bridge: https://en.wikipedia.org/wiki/Contract_bridge
This is just an ordinary Python class, with nothing Django-specific about it.
This is an ordinary Python class, with nothing Django-specific about it.
We'd like to be able to do things like this in our models (we assume the
``hand`` attribute on the model is an instance of ``Hand``)::
@ -81,11 +81,12 @@ Background theory
Database storage
----------------
The simplest way to think of a model field is that it provides a way to take a
normal Python object -- string, boolean, ``datetime``, or something more
complex like ``Hand`` -- and convert it to and from a format that is useful
when dealing with the database (and serialization, but, as we'll see later,
that falls out fairly naturally once you have the database side under control).
Let's start with model fields. If you break it down, a model field provides a
way to take a normal Python object -- string, boolean, ``datetime``, or
something more complex like ``Hand`` -- and convert it to and from a format
that is useful when dealing with the database. (Such a format is also useful
for serialization, but as we'll see later, that is easier once you have the
database side under control).
Fields in a model must somehow be converted to fit into an existing database
column type. Different databases provide different sets of valid column types,
@ -94,8 +95,7 @@ with. Anything you want to store in the database must fit into one of
those types.
Normally, you're either writing a Django field to match a particular database
column type, or there's a fairly straightforward way to convert your data to,
say, a string.
column type, or you will need a way to convert your data to, say, a string.
For our ``Hand`` example, we could convert the card data to a string of 104
characters by concatenating all the cards together in a pre-determined order --
@ -180,16 +180,16 @@ card values plus their suits; 104 characters in total.
with. For example, you can pass both
:attr:`~django.db.models.Field.editable` and
:attr:`~django.db.models.DateField.auto_now` to a
:class:`django.db.models.DateField` and it will simply ignore the
:class:`django.db.models.DateField` and it will ignore the
:attr:`~django.db.models.Field.editable` parameter
(:attr:`~django.db.models.DateField.auto_now` being set implies
``editable=False``). No error is raised in this case.
This behavior simplifies the field classes, because they don't need to
check for options that aren't necessary. They just pass all the options to
check for options that aren't necessary. They pass all the options to
the parent class and then don't use them later on. It's up to you whether
you want your fields to be more strict about the options they select, or to
use the simpler, more permissive behavior of the current fields.
use the more permissive behavior of the current fields.
The ``Field.__init__()`` method takes the following parameters:
@ -241,11 +241,11 @@ then there's no need to write a new ``deconstruct()`` method. If, however,
you're changing the arguments passed in ``__init__()`` (like we are in
``HandField``), you'll need to supplement the values being passed.
The contract of ``deconstruct()`` is simple; it returns a tuple of four items:
the field's attribute name, the full import path of the field class, the
positional arguments (as a list), and the keyword arguments (as a dict). Note
this is different from the ``deconstruct()`` method :ref:`for custom classes
<custom-deconstruct-method>` which returns a tuple of three things.
``deconstruct()`` returns a tuple of four items: the field's attribute name,
the full import path of the field class, the positional arguments (as a list),
and the keyword arguments (as a dict). Note this is different from the
``deconstruct()`` method :ref:`for custom classes <custom-deconstruct-method>`
which returns a tuple of three things.
As a custom field author, you don't need to care about the first two values;
the base ``Field`` class has all the code to work out the field's attribute
@ -307,8 +307,8 @@ mind that people will be reconstructing your field from the serialized version
for quite a while (possibly years), depending how long your migrations live for.
You can see the results of deconstruction by looking in migrations that include
the field, and you can test deconstruction in unit tests by just deconstructing
and reconstructing the field::
the field, and you can test deconstruction in unit tests by deconstructing and
reconstructing the field::
name, path, args, kwargs = my_field_instance.deconstruct()
new_instance = MyField(*args, **kwargs)
@ -349,10 +349,10 @@ As always, you should document your field type, so users will know what it is.
In addition to providing a docstring for it, which is useful for developers,
you can also allow users of the admin app to see a short description of the
field type via the :doc:`django.contrib.admindocs
</ref/contrib/admin/admindocs>` application. To do this simply provide
descriptive text in a :attr:`~Field.description` class attribute of your custom
field. In the above example, the description displayed by the ``admindocs``
application for a ``HandField`` will be 'A hand of cards (bridge style)'.
</ref/contrib/admin/admindocs>` application. To do this provide descriptive
text in a :attr:`~Field.description` class attribute of your custom field. In
the above example, the description displayed by the ``admindocs`` application
for a ``HandField`` will be 'A hand of cards (bridge style)'.
In the :mod:`django.contrib.admindocs` display, the field description is
interpolated with ``field.__dict__`` which allows the description to
@ -393,8 +393,8 @@ Once you have ``MytypeField``, you can use it in any model, just like any other
If you aim to build a database-agnostic application, you should account for
differences in database column types. For example, the date/time column type
in PostgreSQL is called ``timestamp``, while the same column in MySQL is called
``datetime``. The simplest way to handle this in a :meth:`~Field.db_type`
method is to check the ``connection.settings_dict['ENGINE']`` attribute.
``datetime``. You can handle this in a :meth:`~Field.db_type` method by
checking the ``connection.settings_dict['ENGINE']`` attribute.
For example::
@ -431,7 +431,7 @@ sense to have a ``CharMaxlength25Field``, shown here::
my_field = CharMaxlength25Field()
The better way of doing this would be to make the parameter specifiable at run
time -- i.e., when the class is instantiated. To do that, just implement
time -- i.e., when the class is instantiated. To do that, implement
``Field.__init__()``, like so::
# This is a much more flexible example.
@ -730,7 +730,7 @@ accessed, and what methods are available. It lives at
:doc:`file documentation </ref/files/file>`.
Once a subclass of ``File`` is created, the new ``FileField`` subclass must be
told to use it. To do so, simply assign the new ``File`` subclass to the special
told to use it. To do so, assign the new ``File`` subclass to the special
``attr_class`` attribute of the ``FileField`` subclass.
A few suggestions

View file

@ -89,7 +89,7 @@ an application.
Writing custom template filters
===============================
Custom filters are just Python functions that take one or two arguments:
Custom filters are Python functions that take one or two arguments:
* The value of the variable (input) -- not necessarily a string.
* The value of the argument -- this can have a default value, or be left
@ -117,8 +117,8 @@ And here's an example of how that filter would be used:
{{ somevariable|cut:"0" }}
Most filters don't take arguments. In this case, just leave the argument out of
your function. Example::
Most filters don't take arguments. In this case, leave the argument out of your
function::
def lower(value): # Only one argument.
"""Converts a string into all lowercase"""
@ -312,11 +312,11 @@ Template filter code falls into one of two situations:
that our function will know whether automatic escaping is in effect when the
filter is called. We use ``autoescape`` to decide whether the input data
needs to be passed through ``django.utils.html.conditional_escape`` or not.
(In the latter case, we just use the identity function as the "escape"
function.) The ``conditional_escape()`` function is like ``escape()`` except
it only escapes input that is **not** a ``SafeData`` instance. If a
``SafeData`` instance is passed to ``conditional_escape()``, the data is
returned unchanged.
(In the latter case, we use the identity function as the "escape" function.)
The ``conditional_escape()`` function is like ``escape()`` except it only
escapes input that is **not** a ``SafeData`` instance. If a ``SafeData``
instance is passed to ``conditional_escape()``, the data is returned
unchanged.
Finally, in the above example, we remember to mark the result as safe
so that our HTML is inserted directly into the template without further
@ -428,7 +428,7 @@ A few things to note about the ``simple_tag`` helper function:
* Checking for the required number of arguments, etc., has already been
done by the time our function is called, so we don't need to do that.
* The quotes around the argument (if any) have already been stripped away,
so we just receive a plain string.
so we receive a plain string.
* If the argument was a template variable, our function is passed the
current value of the variable, not the variable itself.
@ -539,7 +539,7 @@ for the template fragment. Example::
Next, create the template used to render the tag's output. This template is a
fixed feature of the tag: the tag writer specifies it, not the template
designer. Following our example, the template is very simple:
designer. Following our example, the template is very short:
.. code-block:: html+django
@ -645,10 +645,10 @@ the rendering works.
When Django compiles a template, it splits the raw template text into
''nodes''. Each node is an instance of ``django.template.Node`` and has
a ``render()`` method. A compiled template is, simply, a list of ``Node``
objects. When you call ``render()`` on a compiled template object, the template
calls ``render()`` on each ``Node`` in its node list, with the given context.
The results are all concatenated together to form the output of the template.
a ``render()`` method. A compiled template is a list of ``Node`` objects. When
you call ``render()`` on a compiled template object, the template calls
``render()`` on each ``Node`` in its node list, with the given context. The
results are all concatenated together to form the output of the template.
Thus, to define a custom template tag, you specify how the raw template tag is
converted into a ``Node`` (the compilation function), and what the node's
@ -661,7 +661,7 @@ For each template tag the template parser encounters, it calls a Python
function with the tag contents and the parser object itself. This function is
responsible for returning a ``Node`` instance based on the contents of the tag.
For example, let's write a full implementation of our simple template tag,
For example, let's write a full implementation of our template tag,
``{% current_time %}``, that displays the current date/time, formatted according
to a parameter given in the tag, in :func:`~time.strftime` syntax. It's a good
idea to decide the tag syntax before anything else. In our case, let's say the
@ -715,7 +715,7 @@ Notes:
arguments.
* The function returns a ``CurrentTimeNode`` with everything the node needs
to know about this tag. In this case, it just passes the argument --
to know about this tag. In this case, it passes the argument --
``"%Y-%m-%d %I:%M %p"``. The leading and trailing quotes from the
template tag are removed in ``format_string[1:-1]``.
@ -853,7 +853,7 @@ the same time:
The CycleNode is iterating, but it's iterating globally. As far as Thread 1
and Thread 2 are concerned, it's always returning the same value. This is
obviously not what we want!
not what we want!
To address this problem, Django provides a ``render_context`` that's associated
with the ``context`` of the template that is currently being rendered. The
@ -965,9 +965,8 @@ You also have to change the renderer to retrieve the actual contents of the
``date_updated`` property of the ``blog_entry`` object. This can be
accomplished by using the ``Variable()`` class in ``django.template``.
To use the ``Variable`` class, simply instantiate it with the name of the
variable to be resolved, and then call ``variable.resolve(context)``. So,
for example::
To use the ``Variable`` class, instantiate it with the name of the variable to
be resolved, and then call ``variable.resolve(context)``. So, for example::
class FormatTimeNode(template.Node):
def __init__(self, date_to_be_formatted, format_string):
@ -987,11 +986,11 @@ cannot resolve the string passed to it in the current context of the page.
Setting a variable in the context
---------------------------------
The above examples simply output a value. Generally, it's more flexible if your
The above examples output a value. Generally, it's more flexible if your
template tags set template variables instead of outputting values. That way,
template authors can reuse the values that your template tags create.
To set a variable in the context, just use dictionary assignment on the context
To set a variable in the context, use dictionary assignment on the context
object in the ``render()`` method. Here's an updated version of
``CurrentTimeNode`` that sets a template variable ``current_time`` instead of
outputting it::
@ -1116,7 +1115,7 @@ After ``parser.parse()`` is called, the parser hasn't yet "consumed" the
``{% endcomment %}`` tag, so the code needs to explicitly call
``parser.delete_first_token()``.
``CommentNode.render()`` simply returns an empty string. Anything between
``CommentNode.render()`` returns an empty string. Anything between
``{% comment %}`` and ``{% endcomment %}`` is ignored.
Parsing until another block tag, and saving contents

View file

@ -5,14 +5,14 @@ How to use Django with Gunicorn
.. highlight:: bash
Gunicorn_ ('Green Unicorn') is a pure-Python WSGI server for UNIX. It has no
dependencies and is easy to install and use.
dependencies and can be installed using ``pip``.
.. _Gunicorn: https://gunicorn.org/
Installing Gunicorn
===================
Installing gunicorn is as easy as ``python -m pip install gunicorn``. For more
Install gunicorn by running ``python -m pip install gunicorn``. For more
details, see the `gunicorn documentation`_.
.. _gunicorn documentation: https://docs.gunicorn.org/en/latest/install.html
@ -21,10 +21,9 @@ Running Django in Gunicorn as a generic WSGI application
========================================================
When Gunicorn is installed, a ``gunicorn`` command is available which starts
the Gunicorn server process. At its simplest, gunicorn just needs to be called
with the location of a module containing a WSGI application object named
`application`. So for a typical Django project, invoking gunicorn would look
like::
the Gunicorn server process. The simplest invocation of gunicorn is to pass the
location of a module containing a WSGI application object named
``application``, which for a typical Django project would look like::
gunicorn myproject.wsgi

View file

@ -7,7 +7,7 @@ servers and applications.
.. _WSGI: https://wsgi.readthedocs.io/en/latest/
Django's :djadmin:`startproject` management command sets up a simple default
Django's :djadmin:`startproject` management command sets up a minimal default
WSGI configuration for you, which you can tweak as needed for your project,
and direct any WSGI-compliant application server to use.
@ -70,8 +70,8 @@ If this variable isn't set, the default :file:`wsgi.py` sets it to
Applying WSGI middleware
========================
To apply `WSGI middleware`_ you can simply wrap the application object. For
instance you could add these lines at the bottom of :file:`wsgi.py`::
To apply `WSGI middleware`_ you can wrap the application object. For instance
you could add these lines at the bottom of :file:`wsgi.py`::
from helloworld.wsgi import HelloWorldApplication
application = HelloWorldApplication(application)

View file

@ -57,8 +57,8 @@ virtualenv guide`_ for more details.
The ``WSGIPythonPath`` line ensures that your project package is available for
import on the Python path; in other words, that ``import mysite`` works.
The ``<Directory>`` piece just ensures that Apache can access your
:file:`wsgi.py` file.
The ``<Directory>`` piece ensures that Apache can access your :file:`wsgi.py`
file.
Next we'll need to ensure this :file:`wsgi.py` with a WSGI application object
exists. As of Django version 1.4, :djadmin:`startproject` will have created one

View file

@ -8,9 +8,9 @@ also prevent malicious users from seeing details of your application that can be
revealed by the error pages.
However, running with :setting:`DEBUG` set to ``False`` means you'll never see
errors generated by your site -- everyone will just see your public error pages.
You need to keep track of errors that occur in deployed sites, so Django can be
configured to create reports with details about those errors.
errors generated by your site -- everyone will instead see your public error
pages. You need to keep track of errors that occur in deployed sites, so Django
can be configured to create reports with details about those errors.
Email reports
=============
@ -63,7 +63,7 @@ not found" errors). Django sends emails about 404 errors when:
If those conditions are met, Django will email the users listed in the
:setting:`MANAGERS` setting whenever your code raises a 404 and the request has
a referer. It doesn't bother to email for 404s that don't have a referer --
those are usually just people typing in broken URLs or broken Web bots. It also
those are usually people typing in broken URLs or broken Web bots. It also
ignores 404s when the referer is equal to the requested URL, since this
behavior is from broken Web bots too.

View file

@ -32,8 +32,8 @@ Or, you can write fixtures by hand; fixtures can be written as JSON, XML or YAML
.. _PyYAML: https://pyyaml.org/
As an example, though, here's what a fixture for a simple ``Person`` model might
look like in JSON:
As an example, though, here's what a fixture for a ``Person`` model might look
like in JSON:
.. code-block:: js
@ -73,7 +73,7 @@ And here's that same fixture as YAML:
You'll store this data in a ``fixtures`` directory inside your app.
Loading data is easy: just call :djadmin:`manage.py loaddata <loaddata>`
You can load data by calling :djadmin:`manage.py loaddata <loaddata>`
``<fixturename>``, where ``<fixturename>`` is the name of the fixture file
you've created. Each time you run :djadmin:`loaddata`, the data will be read
from the fixture and re-loaded into the database. Note this means that if you

View file

@ -62,7 +62,7 @@ each table's creation, modification, and deletion::
If you do want to allow Django to manage the table's lifecycle, you'll need to
change the :attr:`~django.db.models.Options.managed` option above to ``True``
(or simply remove it because ``True`` is its default value).
(or remove it because ``True`` is its default value).
Install the core Django tables
==============================

View file

@ -41,17 +41,16 @@ mention:
contains the name of the CSV file. This filename is arbitrary; call it
whatever you want. It'll be used by browsers in the "Save as..." dialog, etc.
* Hooking into the CSV-generation API is easy: Just pass ``response`` as the
first argument to ``csv.writer``. The ``csv.writer`` function expects a
file-like object, and :class:`~django.http.HttpResponse` objects fit the
bill.
* You can hook into the CSV-generation API by passing ``response`` as the first
argument to ``csv.writer``. The ``csv.writer`` function expects a file-like
object, and :class:`~django.http.HttpResponse` objects fit the bill.
* For each row in your CSV file, call ``writer.writerow``, passing it an
:term:`iterable`.
* The CSV module takes care of quoting for you, so you don't have to worry
about escaping strings with quotes or commas in them. Just pass
``writerow()`` your raw strings, and it'll do the right thing.
about escaping strings with quotes or commas in them. Pass ``writerow()``
your raw strings, and it'll do the right thing.
.. _streaming-csv-files:
@ -137,9 +136,9 @@ Then, create the template ``my_template_name.txt``, with this template code:
{% for row in data %}"{{ row.0|addslashes }}", "{{ row.1|addslashes }}", "{{ row.2|addslashes }}", "{{ row.3|addslashes }}", "{{ row.4|addslashes }}"
{% endfor %}
This template is quite basic. It just iterates over the given data and displays
a line of CSV for each row. It uses the :tfilter:`addslashes` template filter to
ensure there aren't any problems with quotes.
This short template iterates over the given data and displays a line of CSV for
each row. It uses the :tfilter:`addslashes` template filter to ensure there
aren't any problems with quotes.
Other text-based formats
========================

View file

@ -88,7 +88,7 @@ mention:
* You can provide an arbitrary ``filename`` parameter. It'll be used by browsers
in the "Save as..." dialog.
* Hooking into the ReportLab API is easy: The same buffer passed as the first
* You can hook into the ReportLab API: The same buffer passed as the first
argument to ``canvas.Canvas`` can be fed to the
:class:`~django.http.FileResponse` class.

View file

@ -12,13 +12,14 @@ Deploying static files
Serving static files in production
==================================
The basic outline of putting static files into production is simple: run the
:djadmin:`collectstatic` command when static files change, then arrange for
the collected static files directory (:setting:`STATIC_ROOT`) to be moved to
the static file server and served. Depending on :setting:`STATICFILES_STORAGE`,
files may need to be moved to a new location manually or the :func:`post_process
<django.contrib.staticfiles.storage.StaticFilesStorage.post_process>` method
of the ``Storage`` class might take care of that.
The basic outline of putting static files into production consists of two
steps: run the :djadmin:`collectstatic` command when static files change, then
arrange for the collected static files directory (:setting:`STATIC_ROOT`) to be
moved to the static file server and served. Depending on
:setting:`STATICFILES_STORAGE`, files may need to be moved to a new location
manually or the :func:`post_process
<django.contrib.staticfiles.storage.StaticFilesStorage.post_process>` method of
the ``Storage`` class might take care of that.
Of course, as with all deployment tasks, the devil's in the details. Every
production setup will be a bit different, so you'll need to adapt the basic
@ -80,11 +81,11 @@ When using these services, the basic workflow would look a bit like the above,
except that instead of using ``rsync`` to transfer your static files to the
server you'd need to transfer the static files to the storage provider or CDN.
There's any number of ways you might do this, but if the provider has an API a
:doc:`custom file storage backend </howto/custom-file-storage>` will make the
process incredibly simple. If you've written or are using a 3rd party custom
storage backend, you can tell :djadmin:`collectstatic` to use it by setting
:setting:`STATICFILES_STORAGE` to the storage engine.
There's any number of ways you might do this, but if the provider has an API,
you can use a :doc:`custom file storage backend </howto/custom-file-storage>`
to integrate the CDN with your Django project. If you've written or are using a
3rd party custom storage backend, you can tell :djadmin:`collectstatic` to use
it by setting :setting:`STATICFILES_STORAGE` to the storage engine.
For example, if you've written an S3 storage backend in
``myproject.storage.S3Storage`` you could use it with::
@ -93,8 +94,8 @@ For example, if you've written an S3 storage backend in
Once that's done, all you have to do is run :djadmin:`collectstatic` and your
static files would be pushed through your storage package up to S3. If you
later needed to switch to a different storage provider, it could be as simple
as changing your :setting:`STATICFILES_STORAGE` setting.
later needed to switch to a different storage provider, you may only have to
change your :setting:`STATICFILES_STORAGE` setting.
For details on how you'd write one of these backends, see
:doc:`/howto/custom-file-storage`. There are 3rd party apps available that

View file

@ -67,7 +67,7 @@ details on how ``staticfiles`` finds your files.
first static file it finds whose name matches, and if you had a static file
with the same name in a *different* application, Django would be unable to
distinguish between them. We need to be able to point Django at the right
one, and the easiest way to ensure this is by *namespacing* them. That is,
one, and the best way to ensure this is by *namespacing* them. That is,
by putting those static files inside *another* directory named for the
application itself.

View file

@ -57,7 +57,7 @@ Install ``virtualenv`` and ``virtualenvwrapper``
`virtualenv`_ and `virtualenvwrapper`_ provide a dedicated environment for
each Django project you create. While not mandatory, this is considered a best
practice and will save you time in the future when you're ready to deploy your
project. Simply type::
project. To do this, run::
...\> py -m pip install virtualenvwrapper-win