Improve docs.

This commit is contained in:
Jannis 2016-03-03 15:56:38 +01:00
parent ec990d8b99
commit 4a8af98955
18 changed files with 426 additions and 324 deletions

View File

@ -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

View File

@ -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:

View File

@ -1,6 +1,6 @@
============
Tmp storages
============
==================
Temporary storages
==================
.. currentmodule:: import_export.tmp_storages

View File

@ -23,6 +23,9 @@ Widgets
.. autoclass:: import_export.widgets.TimeWidget
:members:
.. autoclass:: import_export.widgets.DateTimeWidget
:members:
.. autoclass:: import_export.widgets.ForeignKeyWidget
:members:

View File

@ -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

View File

@ -35,7 +35,7 @@ master_doc = 'index'
# General information about the project.
project = u'django-import-export'
copyright = u'2012, Bojan Mihelac'
copyright = u'20122016, 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 @@ pygments_style = 'sphinx'
# 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

View File

@ -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

View File

@ -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

View File

@ -1,9 +0,0 @@
===========
Example app
===========
::
cd tests && ./manage.py runserver
Username and password for admin are 'admin', 'password'.

View File

@ -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
--------------------------
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::

View File

@ -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.
Different ``InstanceLoader`` class
can be specified with ``instance_loader_class``
option of :class:`import_export.resources.ResourceOptions`.
:class:`import_export.instance_loaders.CachedInstanceLoader` can be used to
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.
See :mod:`import_export.instance_loaders` for available implementations.
#. The :meth:`~import_export.resources.Resource.before_import` hook is called.
By implementing this method in your resource, you can customize the import process.
#. ``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.
#. Each row of the to-be-imported dataset is processed according to the following steps:
#. Process each `row` in ``dataset``
#. :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.
#. ``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.
If no object can be found for the current row,
:meth:`~import_export.resources.Resource.init_instance` is invoked to
initialize an object.
``get_or_init_instance`` tries to load instance for current `row` or
calls ``init_instance`` to init object if object does not exists yet.
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).
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).
#. :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.
#. ``for_delete`` method is called to determine if current `instance`
should be deleted:
#. 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``.
#. 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``
: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.
#. ``result`` is returned.
: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.
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
-------------------

View File

@ -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_resources
api_fields
api_widgets
api_instance_loaders
api_admin
api_results
api_resources
api_widgets
api_fields
api_instance_loaders
api_tmp_storages
api_results
.. _`tablib`: https://github.com/kennethreitz/tablib

View File

@ -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``.

View File

@ -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``.

View File

@ -1,4 +0,0 @@
====
TODO
====

View File

@ -48,7 +48,8 @@ if isinstance(TMP_STORAGE_CLASS, six.string_types):
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,

View File

@ -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 @@ class Resource(six.with_metaclass(DeclarativeMetaclass)):
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 @@ class Resource(six.with_metaclass(DeclarativeMetaclass)):
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 @@ class Resource(six.with_metaclass(DeclarativeMetaclass)):
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 @@ class Resource(six.with_metaclass(DeclarativeMetaclass)):
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 @@ class Resource(six.with_metaclass(DeclarativeMetaclass)):
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 @@ class Resource(six.with_metaclass(DeclarativeMetaclass)):
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 @@ class Resource(six.with_metaclass(DeclarativeMetaclass)):
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.
``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 dataset: A ``tablib.Dataset``
:param raise_errors: Whether errors should be printed to the end user
or raised regularly.
: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 @@ class ModelResource(six.with_metaclass(ModelDeclarativeMetaclass, Resource)):
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()

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from decimal import Decimal
@ -14,29 +15,40 @@ except ImportError:
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 @@ class TimeWidget(Widget):
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``), but
can be customised to use any field on the related model.
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.
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:
Unlike specifying a related field in your resource like so
::
class Meta:
fields = ('author__name',)
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 @@ class ForeignKeyWidget(Widget):
class ManyToManyWidget(Widget):
"""
Widget for ``ManyToManyField`` model field that represent m2m field
as values that identify many-to-many relationship.
Widget that converts between representations of a ManyToMany relationships
as a list and an actual ManyToMany field.
Requires a positional argument: the class to which the field is related.
Optional keyword arguments are:
separator - default ","
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):