From 4a8af989559afbdf0d666f2f706aea0b42dd38cc Mon Sep 17 00:00:00 2001
From: Jannis <jvajen@gmail.com>
Date: Thu, 3 Mar 2016 15:56:38 +0100
Subject: [PATCH] Improve docs.

---
 README.rst                 |  15 ++-
 docs/api_admin.rst         |   2 +
 docs/api_tmp_storages.rst  |   6 +-
 docs/api_widgets.rst       |   3 +
 docs/changelog.rst         |  11 ++-
 docs/conf.py               |   3 +-
 docs/configuration.rst     |  16 ---
 docs/contributing.rst      |  10 --
 docs/example_app.rst       |   9 --
 docs/getting_started.rst   | 127 +++++++++++++-----------
 docs/import_workflow.rst   | 193 ++++++++++++++++++-------------------
 docs/index.rst             |  36 +++----
 docs/installation.rst      |  73 ++++++++++++--
 docs/settings.rst          |  22 -----
 docs/todo.rst              |   4 -
 import_export/admin.py     |   3 +-
 import_export/resources.py | 143 +++++++++++++++++++--------
 import_export/widgets.py   |  84 ++++++++++------
 18 files changed, 431 insertions(+), 329 deletions(-)
 delete mode 100644 docs/configuration.rst
 delete mode 100644 docs/contributing.rst
 delete mode 100644 docs/example_app.rst
 delete mode 100644 docs/settings.rst
 delete mode 100644 docs/todo.rst

diff --git a/README.rst b/README.rst
index e8c98dbcd..48d782806 100644
--- a/README.rst
+++ b/README.rst
@@ -4,10 +4,19 @@ django-import-export
 
 .. image:: https://travis-ci.org/django-import-export/django-import-export.svg?branch=master
     :target: https://travis-ci.org/django-import-export/django-import-export
+    :alt: Build status on Travis-CI
+
 .. image:: https://img.shields.io/pypi/v/django-import-export.svg
     :target: https://pypi.python.org/pypi/django-import-export
+    :alt: Current version on PyPi
+
 .. image:: https://img.shields.io/pypi/dm/django-import-export.svg
-        :target: https://pypi.python.org/pypi/django-import-export    
+    :target: https://pypi.python.org/pypi/django-import-export
+    :alt: Downloads per month on PyPi
+
+.. image:: http://readthedocs.org/projects/django-import-export/badge/?version=latest
+    :target: http://django-import-export.rtfd.org
+    :alt: Docmentation
 
 django-import-export is a Django application and library for importing
 and exporting data with included admin integration.
@@ -56,6 +65,10 @@ If you'd like to contribute, simply fork `the repository`_, commit your
 changes to the **develop** branch (or branch off of it), and send a pull
 request. Make sure you add yourself to AUTHORS_.
 
+As most projects, we try to follow PEP8_ as closely as possible. Please bear
+in mind that most pull requests will be rejected without proper unit testing.
+
+.. _`PEP8`: https://www.python.org/dev/peps/pep-0008/
 .. _`tablib`: https://github.com/kennethreitz/tablib
 .. _`the repository`: https://github.com/django-import-export/django-import-export/
 .. _AUTHORS: https://github.com/django-import-export/django-import-export/blob/master/AUTHORS
diff --git a/docs/api_admin.rst b/docs/api_admin.rst
index d0b6514dc..67b8ca0fd 100644
--- a/docs/api_admin.rst
+++ b/docs/api_admin.rst
@@ -2,5 +2,7 @@
 Admin
 =====
 
+For instructions on how to use the models and mixins in this module, please refer to :ref:`admin-integration`.
+
 .. automodule:: import_export.admin
    :members:
diff --git a/docs/api_tmp_storages.rst b/docs/api_tmp_storages.rst
index 2dbabed6d..fd60c8e5f 100644
--- a/docs/api_tmp_storages.rst
+++ b/docs/api_tmp_storages.rst
@@ -1,6 +1,6 @@
-============
-Tmp storages
-============
+==================
+Temporary storages
+==================
 
 .. currentmodule:: import_export.tmp_storages
 
diff --git a/docs/api_widgets.rst b/docs/api_widgets.rst
index d020ec331..cb567bbb5 100644
--- a/docs/api_widgets.rst
+++ b/docs/api_widgets.rst
@@ -23,6 +23,9 @@ Widgets
 .. autoclass:: import_export.widgets.TimeWidget
   :members:
 
+.. autoclass:: import_export.widgets.DateTimeWidget
+   :members:
+
 .. autoclass:: import_export.widgets.ForeignKeyWidget
    :members:
 
diff --git a/docs/changelog.rst b/docs/changelog.rst
index b44858ba5..b9873241e 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,5 +1,5 @@
-Changelog for django-import-export
-==================================
+Changelog
+=========
 
 0.4.3 (unreleased)
 ------------------
@@ -12,6 +12,7 @@ Changelog for django-import-export
 
 - Add support for django.db.models.TimeField (#381)
 
+
 0.4.2 (2015-12-18)
 ------------------
 
@@ -89,7 +90,7 @@ Changelog for django-import-export
 
 - added use of get_diff_headers method into import.html template (#158)
 
-- Try to use OrderedDict instead of SortedDict, which is deprecated in 
+- Try to use OrderedDict instead of SortedDict, which is deprecated in
   Django 1.7 (#157)
 
 - fixed #105 unicode import
@@ -112,9 +113,9 @@ Changelog for django-import-export
 
 - Fixed XLS import on python 3. Optimized loop
 
-- Fixed properly skipping row marked as skipped when importing data from 
+- Fixed properly skipping row marked as skipped when importing data from
   the admin interface.
-  
+
 - Allow Resource.export to accept iterables as well as querysets
 
 - Improve error messages
diff --git a/docs/conf.py b/docs/conf.py
index 1d9283d0f..07e6bebe4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -35,7 +35,7 @@
 
 # General information about the project.
 project = u'django-import-export'
-copyright = u'2012, Bojan Mihelac'
+copyright = u'2012–2016, Bojan Mihelac'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -90,6 +90,7 @@
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 html_theme = 'default'
+html_theme = "sphinx_rtd_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
diff --git a/docs/configuration.rst b/docs/configuration.rst
deleted file mode 100644
index e5bccb2e6..000000000
--- a/docs/configuration.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-Configuration
-=============
-
-You only need to perform this configuration step if you use django-import-export in the admin.
-
-Add ``import_export`` to your ``INSTALLED_APPS``:
-
-    INSTALLED_APPS = [
-        # ...
-
-        'import_export',
-    ]
-
-Deploy static files:
-
-    $ python manage.py collectstatic
diff --git a/docs/contributing.rst b/docs/contributing.rst
deleted file mode 100644
index 2ebd1c91f..000000000
--- a/docs/contributing.rst
+++ /dev/null
@@ -1,10 +0,0 @@
-=============
-Contributing
-=============
-
-Code guidelines
----------------
-
-* As most projects, we try to follow PEP8 as closely as possible
-
-* Most pull requests will be rejected without proper unit testing
diff --git a/docs/example_app.rst b/docs/example_app.rst
deleted file mode 100644
index de39b8328..000000000
--- a/docs/example_app.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-===========
-Example app
-===========
-
-::
-
-    cd tests && ./manage.py runserver
-
-Username and password for admin are 'admin', 'password'.
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
index 92d829908..42961081d 100644
--- a/docs/getting_started.rst
+++ b/docs/getting_started.rst
@@ -3,7 +3,9 @@ Getting started
 ===============
 
 For example purposes, we'll use a simplified book app. Here is our
-``core.models.py``::
+``models.py``::
+
+    # app/models.py
 
     class Author(models.Model):
         name = models.CharField(max_length=100)
@@ -25,8 +27,7 @@ For example purposes, we'll use a simplified book app. Here is our
         author_email = models.EmailField('Author email', max_length=75, blank=True)
         imported = models.BooleanField(default=False)
         published = models.DateField('Published', blank=True, null=True)
-        price = models.DecimalField(max_digits=10, decimal_places=2, null=True,
-                blank=True)
+        price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
         categories = models.ManyToManyField(Category, blank=True)
 
         def __unicode__(self):
@@ -36,27 +37,27 @@ For example purposes, we'll use a simplified book app. Here is our
 .. _base-modelresource:
 
 Creating import-export resource
--------------------------------
+===============================
 
-To integrate `django-import-export` with ``Book`` model, we will create
-a resource class in ``admin.py`` that will describe how this resource can be imported or
-exported.
+To integrate `django-import-export` with our ``Book`` model, we will create a
+:class:`~import_export.resources.ModelResource` class in ``admin.py`` that will
+describe how this resource can be imported or exported::
 
-::
+    # app/admin.py
 
     from import_export import resources
     from core.models import Book
 
-
     class BookResource(resources.ModelResource):
 
         class Meta:
             model = Book
 
 Exporting data
---------------
+==============
 
-Now that we have defined a resource class, we can export books::
+Now that we have defined a :class:`~import_export.resources.ModelResource` class,
+we can export books::
 
     >>> dataset = BookResource().export()
     >>> print dataset.csv
@@ -64,11 +65,11 @@ Now that we have defined a resource class, we can export books::
     2,Some book,1,,0,2012-12-05,8.85,1
 
 Customize resource options
---------------------------
+==========================
 
-By default ``ModelResource`` introspects model fields and creates
-``import_export.fields.Field`` attributes with an appropriate widget
-for each field.
+By default :class:`~import_export.resources.ModelResource` introspects model
+fields and creates :class:`~import_export.fields.Field`-attributes with an
+appropriate :class:`~import_export.widgets.Widget` for each field.
 
 To affect which model fields will be included in an import-export
 resource, use the ``fields`` option to whitelist fields::
@@ -105,7 +106,7 @@ The default field for object identification is ``id``, you can optionally set wh
             import_id_fields = ('isbn',)
             fields = ('isbn', 'name', 'author', 'price',)
 
-When defining ``ModelResource`` fields it is possible to follow
+When defining :class:`~import_export.resources.ModelResource` fields it is possible to follow
 model relationships::
 
     class BookResource(resources.ModelResource):
@@ -135,10 +136,10 @@ whether skipped records will show in the import preview page::
 .. seealso::
 
     :doc:`/api_resources`
-        
+
 
 Declaring fields
-----------------
+================
 
 It is possible to override a resource field to change some of its
 options::
@@ -147,14 +148,14 @@ options::
 
     class BookResource(resources.ModelResource):
         published = fields.Field(column_name='published_date')
-        
+
         class Meta:
             model = Book
 
 Other fields that don't exist in the target model may be added::
 
     from import_export import fields
-    
+
     class BookResource(resources.ModelResource):
         myfield = fields.Field(column_name='myfield')
 
@@ -168,7 +169,7 @@ Other fields that don't exist in the target model may be added::
 
 
 Advanced data manipulation
---------------------------
+==========================
 
 Not all data can be easily extracted from an object/model attribute.
 In order to turn complicated data model into a (generally simpler) processed
@@ -178,7 +179,7 @@ data structure, ``dehydrate_<fieldname>`` method should be defined::
 
     class BookResource(resources.ModelResource):
         full_title = fields.Field()
-        
+
         class Meta:
             model = Book
 
@@ -187,11 +188,11 @@ data structure, ``dehydrate_<fieldname>`` method should be defined::
 
 
 Customize widgets
------------------
+=================
 
-``ModelResource`` creates a field with a default widget for a given field
-type. If the widget should be initialized with different arguments, set the
-``widgets`` dict.
+A :class:`~import_export.resources.ModelResource` creates a field with a
+default widget for a given field type. If the widget should be initialized
+with different arguments, set the ``widgets`` dict.
 
 In this example widget, the ``published`` field is overriden to use a
 different date format. This format will be used both for importing
@@ -200,7 +201,7 @@ and exporting resource.
 ::
 
     class BookResource(resources.ModelResource):
-        
+
         class Meta:
             model = Book
             widgets = {
@@ -213,66 +214,78 @@ and exporting resource.
         available widget types and options.
 
 Importing data
---------------
+==============
 
 Let's import data::
 
     >>> import tablib
     >>> from import_export import resources
     >>> from core.models import Book
-    >>> book_resource = resources.modelresource_factory(model=Book)()
-    >>> dataset = tablib.Dataset(['', 'New book'], headers=['id', 'name'])
+    >>> book_resource = resources.modelresource_factory(model=Book)()  # Line 4
+    >>> dataset = tablib.Dataset(                                      # Line 5
+    ...     ['', 'New book'], headers=['id', 'name']
+    ... )
     >>> result = book_resource.import_data(dataset, dry_run=True)
     >>> print result.has_errors()
     False
     >>> result = book_resource.import_data(dataset, dry_run=False)
 
-In 4th line we use ``modelresource_factory`` to create a default
-``ModelResource``. ModelResource class created this way is equal
-as in :ref:`base-modelresource`.
+In the fourth line we use :func:`~import_export.resources.modelresource_factory`
+to create a default :class:`~import_export.resources.ModelResource`.
+The ModelResource class created this way is equal to the one shown in the
+example in section :ref:`base-modelresource`.
 
-In 5th line a ``Dataset`` with subset of ``Book`` fields is created.
+In fifth line a :class:`~tablib.Dataset` with columns ``id`` and ``name``, and one book entry, are created. A field for a primary key field (in this case, ``id``) always needs to be present.
 
-In rest of code we first pretend to import data with ``dry_run`` set, then
-check for any errors and import data.
+In the rest of the code we first pretend to import data using
+:meth:`~import_export.resources.Resource.import_data` and ``dry_run`` set,
+then check for any errors and actually import data this time.
 
 .. seealso::
 
     :doc:`/import_workflow`
-        for detailed import workflow descripton and customization options.
+        for a detailed description of the import workflow and its customization options.
+
 
 Deleting data
-^^^^^^^^^^^^^
+-------------
 
-To delete objects during import, implement ``for_delete`` method on resource
-class.
+To delete objects during import, implement the
+:meth:`~import_export.resources.Resource.for_delete` method on
+your :class:`~import_export.resources.Resource` class.
 
-Example resource with ``delete`` field::
+The following is an example resource which expects a ``delete`` field in the
+dataset. An import using this resource will delete model instances for rows
+that have their column ``delete`` set to ``1``::
 
     class BookResource(resources.ModelResource):
         delete = fields.Field(widget=widgets.BooleanWidget())
 
         def for_delete(self, row, instance):
             return self.fields['delete'].clean(row)
-        
+
         class Meta:
             model = Book
 
-Import of this resource will delete model instances for rows
-that have column ``delete`` set to ``1``.
+
+.. _admin-integration:
 
 Admin integration
------------------
+=================
 
-Admin integration is achieved by subclassing (in ``admin.py``)
-``ImportExportModelAdmin`` or one of the available mixins (``ImportMixin``, 
-``ExportMixin``, or ``ImportExportMixin``)::
+Exporting via list filters
+--------------------------
 
-    from import_export.admin import ImportExportModelAdmin
+Admin integration is achieved by subclassing
+:class:`~import_export.admin.ImportExportModelAdmin` or one of the available
+mixins (:class:`~import_export.admin.ImportMixin`,
+:class:`~import_export.admin.ExportMixin`,
+:class:`~import_export.admin.ImportExportMixin`)::
 
+    # app/admin.py
+    from import_export.admin import ImportExportModelAdmin
 
     class BookAdmin(ImportExportModelAdmin):
-        resource_class = BookResource
         pass
 
 .. figure:: _static/images/django-import-export-change.png
@@ -287,18 +300,19 @@ Admin integration is achieved by subclassing (in ``admin.py``)
 
    A screenshot of the confirm import view.
 
-|
+
+Exporting via admin action
+--------------------------
 
 Another approach to exporting data is by subclassing
-``ImportExportActionModelAdmin`` which implements export as an admin action.
-As a result it's possible to export a list of objects selected on the change
-list page::
+:class:`~import_export.admin.ImportExportActionModelAdmin` which implements
+export as an admin action. As a result it's possible to export a list of
+objects selected on the change list page::
 
+    # app/admin.py
     from import_export.admin import ImportExportActionModelAdmin
 
-
     class BookAdmin(ImportExportActionModelAdmin):
-        resource_class = BookResource
         pass
 
 
@@ -306,7 +320,6 @@ list page::
 
    A screenshot of the change view with Import and Export as an admin action.
 
-|
 
 .. seealso::
 
diff --git a/docs/import_workflow.rst b/docs/import_workflow.rst
index 40f3b9fee..7df39e18c 100644
--- a/docs/import_workflow.rst
+++ b/docs/import_workflow.rst
@@ -2,117 +2,112 @@
 Import data workflow
 ====================
 
-This document describes import data workflow, with hooks that enable
-customization of import process.
+This document describes the import data workflow in detail, with hooks that enable
+customization of the import process. The central aspect of the import process is a resource's
+:meth:`~import_export.resources.Resource.import_data` method which is explained below.
 
-``import_data`` method arguments
---------------------------------
+.. function:: import_data(dataset, dry_run=False, raise_errors=False)
 
-``import_data`` method of :class:`import_export.resources.Resource` class is
-responsible for import data from given `dataset`.
+    The :meth:`~import_export.resources.Resource.import_data` method of
+    :class:`~import_export.resources.Resource` is responsible for importing data
+    from a given dataset.
 
-``import_data`` expect following arguments:
+    ``dataset`` is required and expected to be a :class:`tablib.Dataset` with
+    a header row.
 
-:attr:`dataset`
-    REQUIRED.
-    should be Tablib `Dataset`_ object with header row.
+    ``dry_run`` is a Boolean which determines if changes to the database are
+    made or if the import is only simulated. It defaults to ``False``.
 
-:attr:`dry_run`
-    If ``True``, import should not change database. Default is ``False``.
+    ``raise_errors`` is a Boolean. If ``True``, import should raise errors.
+    The default is ``False``, which means that eventual errors and traceback
+    will be saved in ``Result`` instance.
 
-:attr:`raise_errors`
-    If ``True``, import should raise errors. Default is ``False``, which
-    means that eventual errors and traceback will be saved in ``Result``
-    instance.
 
-``import_data`` method workflow
--------------------------------
+This is what happens when the method is invoked:
 
-#. ``import_data`` intialize new :class:`import_export.results.Result`
-   instance. ``Result`` instance holds errors and other information
-   gathered during import.
+#. First, a new :class:`~import_export.results.Result` instance, which holds
+   errors and other information gathered during the import, is initialized.
 
-#. ``InstanceLoader`` responsible for loading existing instances
-   is intitalized.
+   Then, an :class:`~import_export.instance_loaders.InstanceLoader` responsible for loading existing instances
+   is intitalized. A different :class:`~import_export.instance_loaders.BaseInstanceLoader` can be specified via
+   :class:`~import_export.resources.ResourceOptions`'s ``instance_loader_class`` attribute.
+   A :class:`~import_export.instance_loaders.CachedInstanceLoader` can be used to
+   reduce number of database queries.
+   See the `source <https://github.com/django-import-export/django-import-export/blob/master/import_export/instance_loaders.py>`_ for available implementations.
 
-   Different ``InstanceLoader`` class
-   can be specified with ``instance_loader_class``
-   option of :class:`import_export.resources.ResourceOptions`.
+#. The :meth:`~import_export.resources.Resource.before_import` hook is called.
+   By implementing this method in your resource, you can customize the import process.
 
-   :class:`import_export.instance_loaders.CachedInstanceLoader` can be used to
-   reduce number of database queries.
+#. Each row of the to-be-imported dataset is processed according to the following steps:
+
+   #. :meth:`~import_export.resources.Resource.get_or_init_instance` is called
+      with current :class:`~import_export.instance_loaders.BaseInstanceLoader`
+      and current row of the dataset, returning an object and a Boolean
+      declaring if the object is newly created or not.
+
+      If no object can be found for the current row,
+      :meth:`~import_export.resources.Resource.init_instance` is invoked to
+      initialize an object.
+
+      As always, you can override the implementation of
+      :meth:`~import_export.resources.Resource.init_instance` to customized
+      how the new object is created (i.e. set default values).
+
+   #. :meth:`~import_export.resources.Resource.for_delete` is called to determine if the passed ``instance``
+      should be deleted. In this case, the import process for the current row is stopped at this point.
+
+   #. If the instance was not deleted in the previous step,
+      :meth:`~import_export.resources.Resource.import_obj` is called with the
+      ``instance`` as current object, ``row`` as current row and ``dry run``.
+
+      :meth:`~import_export.resources.Resource.import_field` is called for
+      each field in :class:`~import_export.resources.Resource` skipping many-
+      to-many fields. Many-to-many fields are skipped because they require
+      instances to have a primary key and therefore assignment is postponed to
+      when the object has already been saved.
+
+      :meth:`~import_export.resources.Resource.import_field` in turn calls
+      :meth:`~import_export.fields.Field.save`, if ``Field.attribute`` is set
+      and ``Field.column_name`` exists in the given row.
+
+   #. It then is determined whether the newly imported object is different
+      from the already present object and if therefore the given row should be
+      skipped or not. This is handled by calling
+      :meth:`~import_export.resources.Resource.skip_row` with ``original`` as
+      the original object and ``instance`` as the current object from the dataset.
+
+      If the current row is to be skipped, ``row_result.import_type`` is set
+      to ``IMPORT_TYPE_SKIP``.
+
+   #. If the current row is not to be skipped,
+      :meth:`~import_export.resources.Resource.save_instance` is called and
+      actually saves the instance when ``dry_run`` is not set.
+
+      There are two hook methods (that by default do nothing) giving you the option to customize the
+      import process:
+
+        * :meth:`~import_export.resources.Resource.before_save_instance`
+        * :meth:`~import_export.resources.Resource.after_save_instance`
+
+      Both methods receive ``instance`` and ``dry_run`` arguments.
+
+   #. :meth:`~import_export.resources.Resource.save_m2m` is called to save
+      many to many fields.
+
+   #. :class:`~import_export.results.RowResult` is assigned with a diff
+      between the original and the imported object fields, as well as and
+      ``import_type`` attribute which states whether the row is new, updated,
+      skipped or deleted.
+
+      If an exception is raised during row processing and
+      :meth:`~import_export.resources.Resource.import_data` was invoked with
+      ``raise_errors=False`` (which is the default) the particular traceback
+      is appended to :class:`~import_export.results.RowResult` as well.
 
-   See :mod:`import_export.instance_loaders` for available implementations.
-
-#. ``import_data`` calls the ``before_import`` hook method which by default does 
-   not do anything but can be overriden to customize the import process. The 
-   method receives the ``dataset`` and ``dry_run`` arguments as well as any
-   additional keyword arguments passed to ``import_data`` in a ``kwargs`` dict.
-
-#. Process each `row` in ``dataset``
-
-   #. ``get_or_init_instance`` method is called with current ``InstanceLoader``
-      and current `row` returning object `instance` and `Boolean` variable
-      that indicates if object instance is new.
-
-      ``get_or_init_instance`` tries to load instance for current `row` or
-      calls ``init_instance`` to init object if object does not exists yet.
-
-      Default ``ModelResource.init_instance`` initialize Django Model without
-      arguments. You can override ``init_instance`` method to manipulate how
-      new objects are initialized (ie: to set default values).
-
-   #. ``for_delete`` method is called to determine if current `instance`
-      should be deleted:
-
-      #. current `instance` is deleted
- 
-      OR
- 
-      #. ``import_obj`` method is called with the current object ``instance`` and
-         current ``row`` and ``dry run`` arguments.
- 
-         ``import_obj`` loop through all `Resource` `fields`, skipping
-         many to many fields and calls ``import_field`` for each. (Many to many
-         fields require that instance have a primary key, this is why assigning
-         them is postponed, after object is saved).
- 
-         ``import_field`` calls ``field.save`` method, if ``field`` has
-         both `attribute` and field `column_name` exists in given row.
- 
-      #. ``skip_row`` method is called with current object ``instance`` and
-         original object ``original`` to determine if the row should be skipped
- 
-         #. ``row_result.import_type`` is set to ``IMPORT_TYPE_SKIP``
-         
-         OR
-     
-         #. ``save_instance`` method is called.
-     
-            ``save_instance`` receives ``dry_run`` argument and actually saves
-             instance only when ``dry_run`` is False.
-     
-             ``save_instance`` calls two hooks methods that by default does not
-             do anything but can be overriden to customize import process:
-     
-             * ``before_save_instance``
-     
-             * ``after_save_instance``
-     
-             Both methods receive ``instance`` and ``dry_run`` arguments.
-     
-          #. ``save_m2m`` method is called to save many to many fields.
- 
-   #. ``RowResult`` is assigned with diff between original and imported
-       object fields as well as import type(new, updated, skipped).
- 
-       If exception is raised inside row processing, and ``raise_errors`` is
-       ``False`` (default), traceback is appended to ``RowResult``.
-       
-       If the row was not skipped or the `Resource` is configured to report
-       skipped rows the ``RowResult`` is appended to the ``result``
-
-#. ``result`` is returned.
+      If either the row was not skipped or the
+      :class:`~import_export.resources.Resource` is configured to report
+      skipped rows, the :class:`~import_export.results.RowResult` is appended to the :class:`~import_export.results.Result`
+#. The :class:`~import_export.results.Result` is returned.
 
 Transaction support
 -------------------
diff --git a/docs/index.rst b/docs/index.rst
index dd11ae0eb..d98e05abf 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -5,52 +5,44 @@ Django import / export
 django-import-export is a Django application and library for importing
 and exporting data with included admin integration.
 
-Features:
+**Features:**
 
-* support multiple formats (Excel, CSV, JSON, ...
-  and everything else that `tablib`_ supports)
+   * support multiple formats (Excel, CSV, JSON, ...
+     and everything else that `tablib`_ supports)
 
-* admin integration for importing
+   * admin integration for importing
 
-* preview import changes
+   * preview import changes
 
-* admin integration for exporting
+   * admin integration for exporting
 
-* export data respecting admin filters
+   * export data respecting admin filters
 
-.. figure:: _static/images/django-import-export-change.png
+   .. figure:: _static/images/django-import-export-change.png
 
-   A screenshot of the change view with Import and Export buttons.
+      A screenshot of the change view with Import and Export buttons.
 
-User Guide
-----------
 
 .. toctree::
    :maxdepth: 2
+   :caption: User Guide
 
    installation
-   configuration
    getting_started
    import_workflow
-   example_app
-   settings
-   todo
-   contributing
    changelog
 
-API documentation
------------------
-
 .. toctree::
    :maxdepth: 2
+   :caption: API documentation
 
+   api_admin
    api_resources
-   api_fields
    api_widgets
+   api_fields
    api_instance_loaders
-   api_admin
-   api_results
    api_tmp_storages
+   api_results
 
 
 .. _`tablib`: https://github.com/kennethreitz/tablib
diff --git a/docs/installation.rst b/docs/installation.rst
index 8c1698939..dec387425 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -1,10 +1,69 @@
-============
-Installation
-============
+==============================
+Installation and configuration
+==============================
 
-django-import-export is on the Python Package Index (PyPI),
-so it can be installed with standard Python tools like pip or easy_install:
-
-::
+django-import-export is available on the Python Package Index (PyPI), so it
+can be installed with standard Python tools like ``pip`` or ``easy_install``::
 
     $ pip install django-import-export
+
+Alternatively, you can install the git repository directly to obtain the
+development version::
+
+    $ pip install -e git+https://github.com/django-import-export/django-import-export.git#egg=django-import-export
+
+Now, you're good to go, unless you want to use django-import-export from the
+admin as well. In this case, you need to add it to your ``INSTALLED_APPS`` and
+let Django collect its static files.
+
+.. code-block:: python
+
+    # settings.py
+    INSTALLED_APPS = (
+        ...
+        'import_export',
+    )
+
+.. code-block:: shell
+
+    $ python manage.py collectstatic
+
+All prequisites are set up! See :doc:`getting_started` to learn how to use django-import-export in your project.
+
+
+
+Settings
+========
+
+You can use the following directives in your settings file:
+
+``IMPORT_EXPORT_USE_TRANSACTIONS``
+    Global setting controls if resource importing should use database
+    transactions. Default is ``False``.
+
+``IMPORT_EXPORT_SKIP_ADMIN_LOG``
+    Global setting controls if creating log entries for
+    the admin changelist should be skipped when importing resource.
+    The `skip_admin_log` attribute of `ImportMixin` is checked first,
+    which defaults to ``None``. If not found, this global option is used.
+    This will speed up importing large datasets, but will lose
+    changing logs in the admin changelist view.  Default is ``False``.
+
+``IMPORT_EXPORT_TMP_STORAGE_CLASS``
+    Global setting for the class to use to handle temporary storage
+    of the uploaded file when importing from the admin using an
+    `ImportMixin`.  The `tmp_storage_class` attribute of `ImportMixin`
+    is checked first, which defaults to ``None``. If not found, this
+    global option is used. Default is ``TempFolderStorage``.
+
+
+
+Example app
+===========
+
+There's an example application that showcases what django-import-export can do. You can run it via::
+
+    cd tests
+    ./manage.py runserver
+
+Username and password for admin are ``admin`` and ``password``.
diff --git a/docs/settings.rst b/docs/settings.rst
deleted file mode 100644
index 20276f016..000000000
--- a/docs/settings.rst
+++ /dev/null
@@ -1,22 +0,0 @@
-========
-Settings
-========
-
-``IMPORT_EXPORT_USE_TRANSACTIONS``
-    Global setting controls if resource importing should use database
-    transactions. Default is ``False``.
-
-``IMPORT_EXPORT_SKIP_ADMIN_LOG``
-    Global setting controls if creating log entries for
-    the admin changelist should be skipped when importing resource.
-    The `skip_admin_log` attribute of `ImportMixin` is checked first,
-    which defaults to ``None``. If not found, this global option is used.
-    This will speed up importing large datasets, but will lose
-    changing logs in the admin changelist view.  Default is ``False``.
-
-``IMPORT_EXPORT_TMP_STORAGE_CLASS``
-    Global setting for the class to use to handle temporary storage
-    of the uploaded file when importing from the admin using an
-    `ImportMixin`.  The `tmp_storage_class` attribute of `ImportMixin`
-    is checked first, which defaults to ``None``. If not found, this
-    global option is used. Default is ``TempFolderStorage``.
diff --git a/docs/todo.rst b/docs/todo.rst
deleted file mode 100644
index 00fc9341b..000000000
--- a/docs/todo.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-====
-TODO
-====
-
diff --git a/import_export/admin.py b/import_export/admin.py
index 0a21fee84..687e398e6 100644
--- a/import_export/admin.py
+++ b/import_export/admin.py
@@ -48,7 +48,8 @@
         msg = "Could not import '%s' for import_export setting 'IMPORT_EXPORT_TMP_STORAGE_CLASS'" % TMP_STORAGE_CLASS
         raise ImportError(msg)
 
-#: import / export formats
+#: These are the default formats for import and export. Whether they can be
+#: used or not is depending on their implementation in the tablib library.
 DEFAULT_FORMATS = (
     base_formats.CSV,
     base_formats.XLS,
diff --git a/import_export/resources.py b/import_export/resources.py
index 88ab431d2..0d0583ad2 100644
--- a/import_export/resources.py
+++ b/import_export/resources.py
@@ -64,47 +64,65 @@ class ResourceOptions(object):
     """
     The inner Meta class allows for class-level configuration of how the
     Resource should behave. The following options are available:
+    """
 
-    * ``fields`` - Controls what introspected fields the Resource
-      should include. A whitelist of fields.
-
-    * ``exclude`` - Controls what introspected fields the Resource should
-      NOT include. A blacklist of fields.
-
-    * ``model`` - Django Model class. It is used to introspect available
-      fields.
-
-    * ``instance_loader_class`` - Controls which class instance will take
-      care of loading existing objects.
-
-    * ``import_id_fields`` - Controls which object fields will be used to
-      identify existing instances.
-
-    * ``export_order`` - Controls export order for columns.
-
-    * ``widgets`` - dictionary defines widget kwargs for fields.
-
-    * ``use_transactions`` - Controls if import should use database
-      transactions. Default value is ``None`` meaning
-      ``settings.IMPORT_EXPORT_USE_TRANSACTIONS`` will be evaluated.
-
-    * ``skip_unchanged`` - Controls if the import should skip unchanged
-      records. Default value is False
-
-    * ``report_skipped`` - Controls if the result reports skipped rows
-      Default value is True
+    model = None
+    """
+    Django Model class. It is used to introspect available
+    fields.
 
     """
     fields = None
-    model = None
+    """
+    Controls what introspected fields the Resource should include. A whitelist
+    of fields.
+    """
+
     exclude = None
+    """
+    Controls what introspected fields the Resource should
+    NOT include. A blacklist of fields.
+    """
+
     instance_loader_class = None
+    """
+    Controls which class instance will take
+    care of loading existing objects.
+    """
+
     import_id_fields = ['id']
+    """
+    Controls which object fields will be used to
+    identify existing instances.
+    """
+
     export_order = None
+    """
+    Controls export order for columns.
+    """
+
     widgets = None
+    """
+    This dictionary defines widget kwargs for fields.
+    """
+
     use_transactions = None
+    """
+    Controls if import should use database transactions. Default value is
+    ``None`` meaning ``settings.IMPORT_EXPORT_USE_TRANSACTIONS`` will be
+    evaluated.
+    """
+
     skip_unchanged = False
+    """
+    Controls if the import should skip unchanged records. Default value is
+    False
+    """
+
     report_skipped = True
+    """
+    Controls if the result reports skipped rows Default value is True
+    """
 
 
 class DeclarativeMetaclass(type):
@@ -161,14 +179,15 @@ def get_use_transactions(self):
 
     def get_fields(self):
         """
-        Returns fields in ``export_order`` order.
+        Returns fields sorted according to
+        :attr:`~import_export.resources.ResourceOptions.export_order`.
         """
         return [self.fields[f] for f in self.get_export_order()]
 
     @classmethod
     def get_field_name(cls, field):
         """
-        Returns field name for given field.
+        Returns the field name for a given field.
         """
         for field_name, f in cls.fields.items():
             if f == field:
@@ -180,9 +199,15 @@ def init_instance(self, row=None):
         raise NotImplementedError()
 
     def get_instance(self, instance_loader, row):
+        """
+        Calls the :doc:`InstanceLoader <api_instance_loaders>`.
+        """
         return instance_loader.get_instance(row)
 
     def get_or_init_instance(self, instance_loader, row):
+        """
+        Either fetches an already existing instance or initializes a new one.
+        """
         instance = self.get_instance(instance_loader, row)
         if instance:
             return (instance, False)
@@ -190,6 +215,12 @@ def get_or_init_instance(self, instance_loader, row):
             return (self.init_instance(row), True)
 
     def save_instance(self, instance, dry_run=False):
+        """
+        Takes care of saving the object to the database.
+
+        Keep in mind that this is done by calling ``instance.save()``, so
+        objects are not created in bulk!
+        """
         self.before_save_instance(instance, dry_run)
         if not dry_run:
             instance.save()
@@ -197,17 +228,20 @@ def save_instance(self, instance, dry_run=False):
 
     def before_save_instance(self, instance, dry_run):
         """
-        Override to add additional logic.
+        Override to add additional logic. Does nothing by default.
         """
         pass
 
     def after_save_instance(self, instance, dry_run):
         """
-        Override to add additional logic.
+        Override to add additional logic. Does nothing by default.
         """
         pass
 
     def delete_instance(self, instance, dry_run=False):
+        """
+        Calls :meth:`instance.delete` as long as ``dry_run`` is not set.
+        """
         self.before_delete_instance(instance, dry_run)
         if not dry_run:
             instance.delete()
@@ -215,22 +249,28 @@ def delete_instance(self, instance, dry_run=False):
 
     def before_delete_instance(self, instance, dry_run):
         """
-        Override to add additional logic.
+        Override to add additional logic. Does nothing by default.
         """
         pass
 
     def after_delete_instance(self, instance, dry_run):
         """
-        Override to add additional logic.
+        Override to add additional logic. Does nothing by default.
         """
         pass
 
     def import_field(self, field, obj, data):
+        """
+        Calls :meth:`import_export.fields.Field.save` if ``Field.attribute``
+        and ``Field.column_name`` are found in ``data``.
+        """
         if field.attribute and field.column_name in data:
             field.save(obj, data)
 
     def import_obj(self, obj, data, dry_run):
         """
+        Traverses every field in this Resource and calls
+        :meth:`~import_export.resources.Resource.import_field`.
         """
         for field in self.get_fields():
             if isinstance(field.widget, widgets.ManyToManyWidget):
@@ -308,7 +348,12 @@ def get_diff_headers(self):
 
     def before_import(self, dataset, dry_run, **kwargs):
         """
-        Override to add additional logic.
+        Override to add additional logic. Does nothing by default.
+
+        This method receives the ``dataset`` that's going to be imported, the
+        ``dry_run`` parameter which determines whether changes are saved to
+        the database, and any additional keyword arguments passed to
+        ``import_data`` in a ``kwargs`` dict.
         """
         pass
 
@@ -316,12 +361,19 @@ def before_import(self, dataset, dry_run, **kwargs):
     def import_data(self, dataset, dry_run=False, raise_errors=False,
                     use_transactions=None, **kwargs):
         """
-        Imports data from ``dataset``.
+        Imports data from ``tablib.Dataset``. Refer to :doc:`import_workflow`
+        for a more complete description of the whole import process.
+
+        :param dataset: A ``tablib.Dataset``
+
+        :param raise_errors: Whether errors should be printed to the end user
+            or raised regularly.
 
-        ``use_transactions``
-            If ``True`` import process will be processed inside transaction.
-            If ``dry_run`` is set, or error occurs, transaction will be rolled
-            back.
+        :param use_transactions: If ``True`` import process will be processed
+            inside transaction.
+
+        :param dry_run: If ``dry_run`` is set, or error occurs, transaction
+            will be rolled back.
         """
         result = Result()
         result.diff_headers = self.get_diff_headers()
@@ -600,12 +652,21 @@ def field_from_django_field(self, field_name, django_field, readonly):
         return field
 
     def get_import_id_fields(self):
+        """
+        """
         return self._meta.import_id_fields
 
     def get_queryset(self):
+        """
+        Returns a queryset of all objects for this model. Override this if you
+        want to limit the returned queryset.
+        """
         return self._meta.model.objects.all()
 
     def init_instance(self, row=None):
+        """
+        Initializes a new Django model.
+        """
         return self._meta.model()
 
 
diff --git a/import_export/widgets.py b/import_export/widgets.py
index af971cfc2..6a7cd3513 100644
--- a/import_export/widgets.py
+++ b/import_export/widgets.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
 from decimal import Decimal
@@ -14,29 +15,40 @@
 
 class Widget(object):
     """
-    Widget takes care of converting between import and export representations.
+    A Widget takes care of converting between import and export representations.
 
-    Widget objects have two functions:
-
-    * converts object field value to export representation
-
-    * converts import value and converts it to appropriate python
-      representation
+    This is achieved by the two methods,
+    :meth:`~import_export.widgets.Widget.clean` and
+    :meth:`~import_export.widgets.Widget.render`.
     """
+
     def clean(self, value):
         """
-        Returns appropriate python objects for import value.
+        Returns an appropriate Python object for an imported value.
+
+        For example, if you import a value from a spreadsheet,
+        :meth:`~import_export.widgets.Widget.clean` handles conversion
+        of this value into the corresponding Python object.
+
+        Numbers or dates can be *cleaned* to their respective data types and
+        don't have to be imported as Strings.
         """
         return value
 
     def render(self, value):
         """
-        Returns export representation of python value.
+        Returns an export representation of a Python value.
+
+        For example, if you have an object you want to export,
+        :meth:`~import_export.widgets.Widget.render` takes care of converting
+        the object's field to a value that can be written to a spreadsheet.
         """
         return force_text(value)
 
 
 class NumberWidget(Widget):
+    """
+    """
 
     def is_empty(self, value):
         # 0 is not empty
@@ -135,7 +147,8 @@ class DateTimeWidget(Widget):
     """
     Widget for converting date fields.
 
-    Takes optional ``format`` parameter.
+    Takes optional ``format`` parameter. If none is set, either
+    ``settings.DATETIME_INPUT_FORMATS`` or ``"%Y-%m-%d %H:%M:%S"`` is used.
     """
 
     def __init__(self, format=None):
@@ -205,25 +218,38 @@ def render(self, value):
 
 class ForeignKeyWidget(Widget):
     """
-    Widget for ``ForeignKey`` which looks up a related model.
+    Widget for a ``ForeignKey`` field which looks up a related model using
+    "natural keys" in both export an import.
+
+    The lookup field defaults to using the primary key (``pk``) as lookup
+    criterion but can be customised to use any field on the related model.
+
+    Unlike specifying a related field in your resource like so…
+
+    ::
 
-    The lookup field defaults to using the primary key (``pk``), but
-    can be customised to use any field on the related model.
+        class Meta:
+            fields = ('author__name',)
 
-    e.g. To use a lookup field other than ``pk``, rather than specifying a
-    field in your Resource as ``class Meta: fields = ('author__name', ...)``,
-    you would specify it in your Resource like so:
+    …using a :class:`~import_export.widgets.ForeignKeyWidget` has the
+    advantage that it can not only be used for exporting, but also importing
+    data with foreign key relationships.
+
+    Here's an example on how to use
+    :class:`~import_export.widgets.ForeignKeyWidget` to lookup related objects
+    using ``Author.name`` instead of ``Author.pk``::
 
         class BookResource(resources.ModelResource):
-            author = fields.Field(column_name='author', attribute='author', \
+            author = fields.Field(
+                column_name='author',
+                attribute='author',
                 widget=ForeignKeyWidget(Author, 'name'))
-            class Meta: fields = ('author', ...)
 
-    This will allow you to use "natural keys" for both import and export.
+            class Meta:
+                fields = ('author',)
 
-    Parameters:
-        ``model`` should be the Model instance for this ForeignKey (required).
-        ``field`` should be the lookup field on the related model.
+    :param model: The Model the ForeignKey refers to (required).
+    :param field: A field on the related model used for looking up a particular object.
     """
     def __init__(self, model, field='pk', *args, **kwargs):
         self.model = model
@@ -242,16 +268,12 @@ def render(self, value):
 
 class ManyToManyWidget(Widget):
     """
-    Widget for ``ManyToManyField`` model field that represent m2m field
-    as values that identify many-to-many relationship.
-
-    Requires a positional argument: the class to which the field is related.
-
-    Optional keyword arguments are:
-
-        separator - default ","
+    Widget that converts between representations of a ManyToMany relationships
+    as a list and an actual ManyToMany field.
 
-        field - field of related model, default ``pk``
+    :param model: The model the ManyToMany field refers to (required).
+    :param separator: Defaults to ``','``.
+    :param field: A field on the related model. Default is ``pk``.
     """
 
     def __init__(self, model, separator=None, field=None, *args, **kwargs):