{"id":20105464,"url":"https://github.com/nnseva/django-import","last_synced_at":"2025-05-06T09:31:29.153Z","repository":{"id":44874392,"uuid":"260053530","full_name":"nnseva/django-import","owner":"nnseva","description":"Customizable and extendable import for any django model from the admin and any API","archived":false,"fork":false,"pushed_at":"2023-02-19T19:56:22.000Z","size":143,"stargazers_count":13,"open_issues_count":4,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-06T20:52:25.720Z","etag":null,"topics":["admin-import","csv","csv-import","django","excel","excel-import","import","import-admin","importer","imports","model-import","pandas"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nnseva.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-29T21:50:20.000Z","updated_at":"2024-01-11T03:51:58.000Z","dependencies_parsed_at":"2022-08-25T15:41:13.796Z","dependency_job_id":null,"html_url":"https://github.com/nnseva/django-import","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnseva%2Fdjango-import","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnseva%2Fdjango-import/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnseva%2Fdjango-import/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nnseva%2Fdjango-import/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nnseva","download_url":"https://codeload.github.com/nnseva/django-import/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224499237,"owners_count":17321596,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admin-import","csv","csv-import","django","excel","excel-import","import","import-admin","importer","imports","model-import","pandas"],"created_at":"2024-11-13T17:47:07.434Z","updated_at":"2024-11-13T17:47:08.128Z","avatar_url":"https://github.com/nnseva.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Tests](https://github.com/nnseva/django-import/actions/workflows/test.yml/badge.svg)](https://github.com/nnseva/django-import/actions/workflows/test.yml)\n\n# Django Import\n\nThe [Django Import](https://github.com/nnseva/django-import) package provides a rich Django models import feature for the admin interface as well as using any API.\n\nThe following options are present:\n\n- synchronous as well as asynchronous (using [Celery](http://www.celeryproject.org/)) import procedure depending on the project-wide settings\n- customizing project-wide storage settings to store imported files\n- import any model present in the project\n- restricting project-wide list of models available to import\n- wide range of input file formats (with help of [pandas](https://pandas.pydata.org/docs/reference/io.html) package)\n- online customizing column-to-field reflection\n- wide and extendable set of conversion and checking functions for imported values\n\nThe idea of the package has been *inspired* by the [django-csvimport](https://github.com/edcrewe/django-csvimport) package which unfortunately was\ntoo hard to modify because of very long history.\n\n## Installation\n\nFor the stable version:\n\n*Stable version* from the PyPi package repository\n```bash\npip install django-import\n```\n\n*Last development version* from the GitHub source version control system\n```\npip install git+git://github.com/nnseva/django-import.git\n```\n\n*Note* that the [Django Import](https://github.com/nnseva/django-import) package itself doesn't depend on the [Celery](http://www.celeryproject.org/) project.\n\nif you are planning to start import procedures *asynchronously*, install [Celery](http://www.celeryproject.org/) accordingly installation\ninstructions in the documentation.\n\n## Usage scenario\n\nThe user should create or save an instance of the `ImportJob` model to start the import. It may be done using any available way - from the code,\nfrom the admin page, etc.\n\nThe `ImportJob` instance has an `upload_file` attribute which contains an imported file, `options` attribute to provide import customization options,\nand `model` atrribute which references to the `ContentType` instance determining a model whose instances will be created by the import procedure.\n\n### Start importing from the admin interface\n\nJust add a new `Import Job`, fill the record appropriately, and save the instance. You will see an import log entry below.\n\nYou can repeat the import procedure. Every time when the `Import Job` is saved, the import procedure is repeated. You can change any attribute\nof the `Import Job` between calls to save.\n\nDelete unnecessary import log entries between `Import Job` saves just switching the delete flag `on` to the right of the import log entry.\n\nEvery start of the import procedure generates a new instance of the `ImportLog` model where you can find timestamp and detailed log of the import.\n\n### Start importing from the arbitrary tool\n\nEvery time when you create or update the `ImportJob`, the import procedure is started, and an instance of the `ImportLog` is created.\n\n## Customizing the import\n\nIf you don't provide any options for the `ImportJob`, the import procedure will:\n\n- open and read file using passed `mode`, or `rb` if not set\n- import all data and headers from the file using the passed `format`, or `csv` if not set\n- search columns in the input file corresponding to the field names\n- create model instances filling the instance field values by the data found in the correspondent input data columns\n\nEvery of these steps is customizable.\n\n### Set the opening mode explicitly\n\n`options` attribute value:\n```js\n{\n    ...\n    \"mode\": \"rt\",\n    ...\n}\n```\n\nThe `mode` option allows to set the file opening mode explicitly. It can receive either `rb` (read binary), or `rt` (read text) value.\n\nThe default value is `rb`.\n\nIn case of read text mode, the `utf-8` encoding is assumed. You are free to detect and convert the text input file encoding\nyourself using some external tools like iconv, enconv, etc. Some of formats, as modern XML, are self-descriptive, so you can try\nto import the file unchanged without explicit mode parameter.\n\n### Set the format explicitly\n\n`options` attribute value:\n```js\n{\n    ...\n    \"format\": \"csv\"\n    ...\n}\n```\n\nYou can set the `format` value to any appropriate suffix for the [`pandas.read_*`](https://pandas.pydata.org/docs/reference/io.html) function. Tested examples are `csv` with different formatting (using additional parameters), and `excel` - for `xls` and `xlsx` files. Note that you can use additional parameters to select a sheet from the excel file to import.\n\n### Force header names\n\n`options` attribute value:\n```js\n{\n    ...\n    \"headers\": [\n        \"id\",\n        \"name\",\n        \"code\",\n        ...\n    ]\n    ...\n}\n```\n\nThis `headers` option overrides header names. Note that number of headers in the option should correspond to the number of columns in the input file.\n\nWhen the `headers` option is not set, and no any header names are recognized for some reason, headers like sequential numbers with leading zeros are used\nin the following importing steps: \"0001\", \"0002\", \"0003\", ...\nmodel\n\n### Reflecting values\n\nThe most poweful option of the import procedure is customizing where and how the input values should appear in the output instances.\n\nThis customization is called *reflection* and appears in the `reflections` section of the `options` job attribute:\n\n```js\n{\n    ...\n    \"reflections\": {\n        \"name\": \"direct\",\n        \"surname\": {\n            \"function\": \"direct\",\n            \"parameters\": {\n                \"column\": \"second name\"\n            }\n        },\n        \"code\": {\n            \"function\": \"clean\",\n            \"parameters\": {\n                \"column\": \"passcode\"\n            }\n        },\n        \"access_level\": {\n            \"function\": \"constant\",\n            \"parameters\": {\n                \"value\": 1\n            }\n        },\n        \"nationality\": \"avoid\",\n        \"address\": {\n            \"function\": \"format\",\n            \"parameters\": {\n                \"format\": \"%(post_code)s, %(building)s-%(apartment)s, %(street), %(city)s, %(country)s\"\n            }\n        },\n        \"has_car\": {\n            \"function\": \"enum\",\n            \"parameters\": {\n                \"column\": \"car\",\n                \"mapping\": {\n                    \"yes\": true,\n                    \"no\": false\n                }\n            }\n        }\n        ...\n    }\n    ...\n}\n```\n\nEvery *reflection* in the `reflections` option is a key-value pair, where the key is a *field name*,\nwhile the value is a *reflection option* which determines a way how to reflect data row\nvalues to this field value.\n\nThe reflection option determines a *reflection function*, and optional parameters section\nfor this function.\n\nThe easiest *reflection option* is a string. If the *reflection option* is a string, it corresponds\nto the *reflection function* name, and assumes that the data contains a column having the name\nequal to the field name (see the `name` reflection in the example above).\n\nAlternatively, *reflection option* may have a form of the subsection, having the following structure:\n\n- `function` key determines a *reflection function* name\n- `parameters` key determines a subsection of additional reflection function parameters\n\nA default *reflection function* is `direct`, it is used if the field is not listed in the\n`reflections` section (the same reflection function is assigned for the `name` reflection\nin the example above explicitly).\n\nWhen the data row is got, all reflection functions are called, and returned values are collected.\n\nThe returned values may be used in two stages:\n\n- create - an instance is created or updated from these values using `update_or_create()` function call\n- update - every value collected for this stage is assigned to the correspondent\n  instance attribute, and after the all, the instance is saved again\n\nEvery *reflection function* returns data for create and update stages separately.\n\nThe most *reflection functions* return data for the create stage. The update stage may be used when the\nexistence of the instance is important, for some custom field types for example.\n\nList of core reflection functions:\n\n- `direct` - sends data column value directly to the field on the instance *create* stage, parameters:\n    - `column` - name of the data column to get a value, field name by defalut\n- `update` - sends data column value to the field on the instance *update* stage. This reflection, as a rule, is used to update instance attributes which are not fields really, f.e. properties, or some custom fields. Parameters:\n    - `column` - name of the data column to get a value, field name by defalut\n- `constant` - sends a constant value to the field on the instance create stage, parameters:\n    - `value` - value to be sent, None by default\n- `avoid` - forces ignore of the field by it's name, avoiding a default direct reflection\n- `clean` - sends data column value to the field on the instance create stage, cleaning it\n  by the field clean method, parameters:\n    - `column` - name of the data column to get a value, field name by defalut\n- `substr` - sends substring of the data column value to the field on the instance create stage, parameters:\n    - `column` - name of the data column to get a value, field name by defalut\n    - `start` - start index in the string, 0 by default\n    - `length` - maximal number of characters to get from the value, max_length attribute\n      of the field by default\n- `replace` - sends a value from the data column with `search` value replaced by the `replace` value, parameters:\n    - `column` - column name where to find a value, field name by default\n    - `search` - substring to be searched\n    - `replace` - value to replace\n    - `count` - number of occurences to replace, all by default\n- `format` - sends a formatted value collected from the data row to the field on the instance create stage, parameters:\n    - `format` - %-style format to create a value from the dictionary of data with column names as keys\n- `xformat` - sends a formatted value collected from the data row to the field on the instance create stage, parameters:\n    - `format` - {}-style format to create a value from the dictionary of data with column names as keys\n- `enum` - sends a enum value accordingly to the mapping from data value to the field value, parameters:\n    - `column` - column name where to find a value, field name by default\n    - `mapping` - column-to-field value mapping (column value as a key)\n- `combine` - applies several reflections sequentially, parameters:\n    - `reflections` - a list of reflections to be applied\n- `lookup` - lookups the column value in the `lookup_field` on the opposite side of the reference, parameters:\n    - `column` - column name where to find a value, field name by default\n    - `lookup_field` - field name of the opposide side model with the optional lookup suffix, 'pk' by default\n\nYou can see the detailed help with the actual list of all registered reflections at the change page of the `ImportJob` instance.\n\n### Customizing a field list to be imported\n\nThe field list to be imported is determined as a union of two name sets:\n- a model field names set\n- a reflection names set\n\nEvery Django model determines a list of fields. Every such field has a name. The package tries to import all fields found in the model.\n\nThe import options contain a set of reflections [see before](#reflecting-values), with unique names. Every such a name also determines a model field or property which should be imported.\n\nThe reflection whose name is equal to the model field name, determines a reflection for this field.\n\nThe default import column name is equal to the field name.\n\nWhen the import file contains a column whose name is equal to the field name, it is imported even the reflection is not declared. To avoid such an import, use the special `avoid` reflection with the name equal to the column name.\n\n\n### Identify instances\n\n`options` attribute value:\n```js\n{\n    ...\n    \"identity\": [\n        \"name\",\n        \"surname\",\n        \"birthday\"\n    ]\n    ...\n}\n```\n\nWhen the import is evaluated several times, it is important to find and update the existent instance imported before\ninstead of creating a new one. The `identity` option allows to select list of fields which will be used to find an existent\ninstance.\n\nAny existent model fields may be included into this identity list. But note that only those of them which are really imported\nwill be used for the instance identification in the particular import procedure.\n\n## Settings\n\n### Asynchronous import procedure\n\nThe import procedure is started synchronously by default. It also can be started asynchronously\nin the separate [Celery](http://www.celeryproject.org/) worker,\ndepending on the `async` value in the `DJANGO_IMPORT` attribute of the `settings` file:\n\n`settings.py`\n```python\nDJANGO_IMPORT = {\n    ...\n    \"async\": True\n    ...\n}\n```\n\nThe `django_import.tasks` module contains a definition of the `run_import` shared task, if you need to register it yourself instead\nof the automatic procedure.\n\nYou can check whether the procedure is finished using a special `is_finished` flag of the `ImportLog` instance.\n\n*Note* that as minimum one [Celery](http://www.celeryproject.org/) Worker process should be started\nin order to start asynchronous import procedure. If no Workers are started, the import procedure will never finished.\n\n### Models list allowed to import\n\nTwo keys containing lists in settings, `models` and `except` mean, what models are allowed to import.\n\nIf `models` key is not empty, only those models, which are included into this list, are allowed.\n\n`settings.py`\n```python\nDJANGO_IMPORT = {\n    ...\n    \"models\": [\n       'accounting.Debit',\n       'accounting.Credit',\n       'accounting.Orders',\n       'accounting.Accounts',\n       ...\n    ],\n    \"except\": []\n    ...\n}\n```\n\nElse, the `except` key enlists models which are forbidden to import using this packet. All models except enlisted are allowed to import in this case.\n\n`settings.py`\n```python\nDJANGO_IMPORT = {\n    ...\n    \"models\": [],\n    \"except\": [\n       'contenttypes.ContentType',\n       'admin.LogEntry',\n       'auth.Permission',\n       'sessions.Session',\n       'django_import.ImportJob',\n       'django_import.ImportLog',\n       ...\n    ]\n    ...\n}\n```\n\n### Setting up storage configuration to store files to be imported\n\nStorage configuration is configured using the `storage` key.\n\nThe `class` key is used to setup a storage class.\n\nThe `settings` key is used to setup keyword parameters to call the storage instance constructor.\n\nThe `upload_to` key is used to setup `upload_to` keyword parameter of the `ImportJob.upload_file` field.\n\nThere is a sample of the custom storage settings for the package below:\n\n`settings.py`\n```python\nDJANGO_IMPORT = {\n    ...\n    'storage': {\n        'class': 'storages.backends.s3boto3.S3Boto3Storage',\n        'settings': {\n            'access_key': PROJECT_AWS_ACCESS_KEY_ID,\n            'secret_key': PROJECT_AWS_SECRET_ACCESS_KEY,\n            'bucket_name': PROJECT_AWS_STORAGE_BUCKET_NAME,\n            'querystring_auth': PROJECT_AWS_QUERYSTRING_AUTH,\n            'use_ssl': PROJECT_AWS_S3_USE_SSL,\n            'default_acl': PROJECT_AWS_DEFAULT_ACL,\n            'location': PROJECT_AWS_LOCATION,\n            'custom_domain': PROJECT_AWS_S3_CUSTOM_DOMAIN,\n        },\n        'upload_to': 'imports',\n    ...\n}\n```\n\n### Default settings\n\nThe default settings are the folowing:\n\n```python\nDJANGO_IMPORT = {\n    'storage': {\n        'class': 'django.core.files.storage.FileSystemStorage',\n        'settings': {\n            'location': '',\n        },\n        'upload_to': 'uploads',\n    },\n    'models': [],\n    'except': [\n        'contenttypes.ContentType',\n        'admin.LogEntry',\n        'auth.Permission',\n        'sessions.Session',\n        'django_import.ImportJob',\n        'django_import.ImportLog',\n    ],\n    'sync': True,\n}\n```\n\nNote that the default options are united with the custom options from the `settings.py` file to get the actual value, like:\n\n```python\noptions = {\n    **default_options,\n    **custom_options\n}\n```\n\n## Import examples\n\n### Prerequisites\n\nWe would like to import files into the model ImportModel which has the following structure:\n\n```python\nclass ImportExample(models.Model):\n    \"\"\" Import Example \"\"\"\n\n    name = models.CharField(\n        max_length=128,\n        verbose_name=_('Name'),\n    )\n    quantity = models.IntegerField(\n        null=True, blank=True,\n        verbose_name=_('Quantity'),\n    )\n    weight = models.FloatField(\n        null=True, blank=True,\n        verbose_name=_('Weight'),\n    )\n    price = models.DecimalField(\n        max_digits=15,\n        decimal_places=2,\n        null=True, blank=True,\n        verbose_name=_('Price'),\n    )\n    kind = models.CharField(\n        max_length=32,\n        choices=[\n            ('wood', _('Wood')),\n            ('steel', _('Steel')),\n            ('oil', _('Oil')),\n        ],\n        verbose_name=_('Kind'),\n    )\n    user = models.ForeignKey(\n        settings.AUTH_USER_MODEL,\n        on_delete=models.CASCADE,\n        related_name='examples',\n        null=True, blank=True,\n        verbose_name=_('User'),\n        help_text=_('User who regards to this example'),\n    )\n\n    def __str__(self):\n        return self.name\n\n    class Meta:\n        verbose_name = _('Import Example')\n        verbose_name_plural = _('Import Examples')\n```\n\nWe also will assume that we have (as minimum) two users in the User model with usernames `u1` and `u2`.\n\nThe `Import Example` model exists in the `tests` application of the package if you have cloned it locally using:\n\n```bash\ngit clone git@github.com:nnseva/django-import.git\n```\n\nor download from the repository https://github.com/nnseva/django-import/archive/master.zip and unpack it.\n\nThe package itself should be installed as described at the top here.\n\nJust go to the `dev` subdirectory and start the following sequentially:\n\n```\npython manage.py migrate\npython manage.py createsuperuser\npython manage.py runserver\n```\n\nInput superuser name and password, and see the WEB admin interface on the `http://127.0.0.1:8000/admin/` URL.\n\n### Try a simplest import\n\nLet we have a CSV file like this (you will find it in the `dev/tests/data` directory).\n\n```csv\n\"name\",\"quantity\",\"weight\",\"price\",\"type\",\"user\"\n\"etewrt\",123,10.3,11.11,S,\"u1\"\n\"cvbncv\",112,54.333,34.12,W,\"u2\"\n```\n\nCreate an `ImportJob` instance without options, select `ImportExample` model, upload the file and try to save it.\n\nYou will see the following log in the import log:\n\n```\n2020-05-12 08:29:49.712041+00:00: [INFO(4)] Starting import for: uploads/test_WyIyugj.csv\n2020-05-12 08:29:50.051297+00:00: [INFO(4)] Trying to import uploads/test_WyIyugj.csv\n2020-05-12 08:29:50.068780+00:00: [INFO(4)] Import file has been recognized, 6 columns, 2 rows: uploads/test_WyIyugj.csv\n2020-05-12 08:29:50.071130+00:00: [ERROR(2)] Unexpected error: Cannot assign \"'u1'\": \"ImportExample.user\" must be a \"User\" instance.\n2020-05-12 08:29:50.071589+00:00: [INFO(4)] Finished\n```\n\nThe `ERROR` string means that we can not import for some reason, which is explained in this line: the `user` column is a user reference, and import package doesn't know, how to import it.\n\n### Avoid importing unnecessary fields\n\nLet we exclude a `user` column a while to make import possible. You can find an `avoid` reflection in the help page below the record which does exactly what we need now.\n\nChange view of the `options` field from the `Tree` to the `Code` and input:\n\n```js\n{\n  \"reflections\": {\n    \"user\": \"avoid\"\n  }\n}\n```\n\nthen press \"Save and continue editing\" button at the bottom.\n\nYou will find a new import log record at the bottom with the following log text:\n\n```\n2020-05-12 09:02:23.176985+00:00: [INFO(4)] Starting import for: uploads/test_WyIyugj.csv\n2020-05-12 09:02:23.178720+00:00: [INFO(4)] Trying to import uploads/test_WyIyugj.csv\n2020-05-12 09:02:23.182253+00:00: [INFO(4)] Import file has been recognized, 6 columns, 2 rows: uploads/test_WyIyugj.csv\n2020-05-12 09:02:23.184719+00:00: [INFO(4)] Import has been processed, 2 rows successfully imported\n2020-05-12 09:02:23.185278+00:00: [INFO(4)] Finished\n```\n\nThe text `Import has been processed, 2 rows successfully imported` means that all 2 records are successfully imported.\n\nYou can ensure it looking to the `Import Example` instance list of the admin.\n\n### Import columns with the `choices` option\n\nWe can see that neither `user` (which has been avoided), nor `kind` column have been imported.\n\nLooking into the model, we can see that the `kind` column has a choice selector. Let we convert the `type` CSV column to the `kind` model field. Searching in the help on the admin page, we can see a `enum` reflection which does what we would like to have.\n\n```js\n{\n  \"reflections\": {\n    \"user\": \"avoid\",\n    \"kind\": {\n        \"function\": \"enum\",\n        \"parameters\": {\n            \"column\": \"type\",\n            \"mapping\": {\n                \"S\": \"steel\",\n                \"W\": \"wood\",\n                \"O\": \"oil\"\n            }\n        }\n    }\n  }\n}\n```\n\nAfter saving the job, we can see success log. Check the imported instances. We can see, that instances have been imported successfully, but duplicated.\n\n### Avoid duplication on the repeated import\n\nRemove all imported instances of the `Import Example` model. We should avoid such a problem in a future, the `identity` option will help us to do it. Change options appropriately:\n\n```js\n{\n  \"reflections\": {\n    \"user\": \"avoid\",\n    \"kind\": {\n        \"function\": \"enum\",\n        \"parameters\": {\n            \"column\": \"type\",\n            \"mapping\": {\n                \"S\": \"steel\",\n                \"W\": \"wood\",\n                \"O\": \"oil\"\n            }\n        }\n    }\n  },\n  \"identity\": [ \"name\" ]\n}\n```\n\n### Delete unnecessary logs\n\nYou can also notice that there are too many log records in the interface. Just select every of them using the flag `Delete` on the right side of every such a record.\n\nThen we will save the `Import Job` and see what happens.\n\nThe import happens successfully, all import log entries have been deleted, and a new one created with the log of the last import.\n\nLet we save the job again and check the `Import Example` admin page. We will see that no any additional records are added, so the `identity` option helps us as we expected.\n\n### Import a reference field\n\nNow it's a time to make a reference to the user in the imported records. We will replace the `avoid` reflection by the special `lookup` reflection which does what we would like to have.\n\n```js\n{\n  \"reflections\": {\n    \"user\": {\n      \"function\": \"lookup\",\n      \"parameters\": {\n        \"lookup_field\": \"username\"\n      }\n    },\n    \"kind\": {\n        \"function\": \"enum\",\n        \"parameters\": {\n            \"column\": \"type\",\n            \"mapping\": {\n                \"S\": \"steel\",\n                \"W\": \"wood\",\n                \"O\": \"oil\"\n            }\n        }\n    }\n  },\n  \"identity\": [ \"name\" ]\n}\n```\n\n### Import unusual format\n\nLet we have a different format of the CSV, like the `Excel` application produces if the Russian localization is a default in the system (you will find an example in the `dev/tests/data/test-ru.csv` file).\n\n```CSV\n\"name\";\"quantity\";\"weight\";\"price\";\"type\";\"user\"\n\"etewrt\";123;10,3;11,11;S;\"u1\"\n\"cvbncv\";112;54,333;34,12;W;\"u2\"\n```\n\nWe can pass explicit CSV convertor parameters to the conversion function (all available parameters for the CSV conversion are on the [pandas page](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html):\n\n```js\n{\n  \"reflections\": {\n    \"user\": {\n      \"function\": \"lookup\",\n      \"parameters\": {\n        \"lookup_field\": \"username\"\n      }\n    },\n    \"kind\": {\n        \"function\": \"enum\",\n        \"parameters\": {\n            \"column\": \"type\",\n            \"mapping\": {\n                \"S\": \"steel\",\n                \"W\": \"wood\",\n                \"O\": \"oil\"\n            }\n        }\n    }\n  },\n  \"identity\": [ \"name\" ],\n  \"format\": \"csv\",\n  \"parameters\": {\n    \"sep\": \";\",\n    \"decimal\": \",\"\n  }\n}\n```\n\nDelete all previously imported records and save the job again. You will see that all records are imported successfully.\n\n### Import an excel file\n\nYou can import the `Excel` file directly (the example is present in the `/dev/tests/data/test.xls` file). Upload a file and use the appropriate format in the options:\n\n```js\n{\n  \"reflections\": {\n    \"user\": {\n      \"function\": \"lookup\",\n      \"parameters\": {\n        \"lookup_field\": \"username\"\n      }\n    },\n    \"kind\": {\n        \"function\": \"enum\",\n        \"parameters\": {\n            \"column\": \"type\",\n            \"mapping\": {\n                \"S\": \"steel\",\n                \"W\": \"wood\",\n                \"O\": \"oil\"\n            }\n        }\n    }\n  },\n  \"identity\": [ \"name\" ],\n  \"format\": \"excel\"\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnnseva%2Fdjango-import","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnnseva%2Fdjango-import","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnnseva%2Fdjango-import/lists"}