# django-model-import
[![PyPI version](https://badge.fury.io/py/django-model-import.svg)](https://badge.fury.io/py/django-model-import)
Django Model Import is a light weight CSV importer built for speed.
It uses a standard Django `ModelForm` to parse each row, giving you a familiar API to work with
for data validation and model instantiation. In most cases, if you already have a `ModelForm`
for the `ContentType` you are importing you do not need to create an import specific form.
To present feedback to the end-user running the import you can easily generate a preview
of the imported data by toggling the `commit` parameter.
It also provides some import optimized fields for ForeignKey's, allowing preloading all
possible values, or caching each lookup as it occurs, or looking up a model where multiple
fields are needed to uniquely identify a resource.
## Installation
```bash
poetry add django-model-import
```
## Quickstart
```python
import djangomodelimport
class BookImporter(djangomodelimport.ImporterModelForm):
name = forms.CharField()
author = CachedChoiceField(queryset=Author.objects.all(), to_field='name')
class Meta:
model = Book
fields = (
'name',
'author',
)
with default_storage.open('books.csv', 'rb') as fh:
data = fh.read().decode("utf-8")
# Use tablib
parser = djangomodelimport.TablibCSVImportParser(BookImporter)
headers, rows = parser.parse(data)
# Process
importer = djangomodelimport.ModelImporter(BookImporter)
preview = importer.process(headers, rows, commit=False)
errors = preview.get_errors()
if errors:
print(errors)
importresult = importer.process(headers, rows, commit=True)
for result in importresult.get_results():
print(result.instance)
```
## Composite key lookups
Often a relationship cannot be referenced via a single unique string. For this we can use
a `CachedChoiceField` with a `CompositeLookupWidget`. The widget looks for the values
under the `type` and `variant` columns in the source CSV, and does a unique lookup
with the field names specified in `to_field`, e.g. `queryset.get(type__name=type, name=variant)`.
The results of each `get` are cached internally for the remainder of the import minimising
any database access.
```python
class AssetImporter(ImporterModelForm):
site = djangomodelimport.CachedChoiceField(queryset=Site.objects.active(), to_field='ref')
type = djangomodelimport.CachedChoiceField(queryset=AssetType.objects.filter(is_active=True), to_field='name')
type_variant = djangomodelimport.CachedChoiceField(
queryset=InspectionItemTypeVariant.objects.filter(is_active=True),
required=False,
widget=djangomodelimport.CompositeLookupWidget(source=('type', 'variant')),
to_field=('type__name', 'name'),
)
contractor = djangomodelimport.CachedChoiceField(queryset=Contractor.objects.active(), to_field='name')
```
## Flat related fields
Often you'll have a OneToOneField or just a ForeignKey to another model, but you want to be able to
create/update that other model via this one. You can flatten all of the related model's fields onto
this importer using `FlatRelatedField`.
```python
class ClientImporter(ImporterModelForm):
primary_contact = FlatRelatedField(
queryset=ContactDetails.objects.all(),
fields={
'contact_name': {'to_field': 'name', 'required': True},
'email': {'to_field': 'email'},
'email_cc': {'to_field': 'email_cc'},
'mobile': {'to_field': 'mobile'},
'phone_bh': {'to_field': 'phone_bh'},
'phone_ah': {'to_field': 'phone_ah'},
'fax': {'to_field': 'fax'},
},
)
class Meta:
model = Client
fields = (
'name',
'ref',
'is_active',
'account',
'primary_contact',
)
```
## Tests
Run tests with `python example/manage.py test testapp`
Raw data
{
"_id": null,
"home_page": "https://github.com/uptick/django-model-import",
"name": "django-model-import",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.10,<4.0",
"maintainer_email": "",
"keywords": "",
"author": "Aidan Lister",
"author_email": "aidan@uptickhq.com",
"download_url": "https://files.pythonhosted.org/packages/2e/a4/80e22160f866a1ad793159b6676ed97f653f5c558cd6b317217c8624034e/django_model_import-0.7.3.tar.gz",
"platform": null,
"description": "# django-model-import\n\n[![PyPI version](https://badge.fury.io/py/django-model-import.svg)](https://badge.fury.io/py/django-model-import)\n\nDjango Model Import is a light weight CSV importer built for speed.\n\nIt uses a standard Django `ModelForm` to parse each row, giving you a familiar API to work with\nfor data validation and model instantiation. In most cases, if you already have a `ModelForm`\nfor the `ContentType` you are importing you do not need to create an import specific form.\n\nTo present feedback to the end-user running the import you can easily generate a preview\nof the imported data by toggling the `commit` parameter.\n\nIt also provides some import optimized fields for ForeignKey's, allowing preloading all\npossible values, or caching each lookup as it occurs, or looking up a model where multiple\nfields are needed to uniquely identify a resource.\n\n\n## Installation\n\n```bash\npoetry add django-model-import\n```\n\n\n## Quickstart\n\n```python\nimport djangomodelimport\n\nclass BookImporter(djangomodelimport.ImporterModelForm):\n name = forms.CharField()\n author = CachedChoiceField(queryset=Author.objects.all(), to_field='name')\n\n class Meta:\n model = Book\n fields = (\n 'name',\n 'author',\n )\n\nwith default_storage.open('books.csv', 'rb') as fh:\n data = fh.read().decode(\"utf-8\")\n\n# Use tablib\nparser = djangomodelimport.TablibCSVImportParser(BookImporter)\nheaders, rows = parser.parse(data)\n\n# Process\nimporter = djangomodelimport.ModelImporter(BookImporter)\npreview = importer.process(headers, rows, commit=False)\nerrors = preview.get_errors()\n\nif errors:\n print(errors)\n\nimportresult = importer.process(headers, rows, commit=True)\nfor result in importresult.get_results():\n print(result.instance)\n```\n\n\n## Composite key lookups\n\nOften a relationship cannot be referenced via a single unique string. For this we can use\na `CachedChoiceField` with a `CompositeLookupWidget`. The widget looks for the values\nunder the `type` and `variant` columns in the source CSV, and does a unique lookup\nwith the field names specified in `to_field`, e.g. `queryset.get(type__name=type, name=variant)`.\n\nThe results of each `get` are cached internally for the remainder of the import minimising\nany database access.\n\n```python\nclass AssetImporter(ImporterModelForm):\n site = djangomodelimport.CachedChoiceField(queryset=Site.objects.active(), to_field='ref')\n type = djangomodelimport.CachedChoiceField(queryset=AssetType.objects.filter(is_active=True), to_field='name')\n type_variant = djangomodelimport.CachedChoiceField(\n queryset=InspectionItemTypeVariant.objects.filter(is_active=True),\n required=False,\n widget=djangomodelimport.CompositeLookupWidget(source=('type', 'variant')),\n to_field=('type__name', 'name'),\n )\n contractor = djangomodelimport.CachedChoiceField(queryset=Contractor.objects.active(), to_field='name')\n```\n\n\n## Flat related fields\n\nOften you'll have a OneToOneField or just a ForeignKey to another model, but you want to be able to\ncreate/update that other model via this one. You can flatten all of the related model's fields onto\nthis importer using `FlatRelatedField`.\n\n```python\nclass ClientImporter(ImporterModelForm):\n primary_contact = FlatRelatedField(\n queryset=ContactDetails.objects.all(),\n fields={\n 'contact_name': {'to_field': 'name', 'required': True},\n 'email': {'to_field': 'email'},\n 'email_cc': {'to_field': 'email_cc'},\n 'mobile': {'to_field': 'mobile'},\n 'phone_bh': {'to_field': 'phone_bh'},\n 'phone_ah': {'to_field': 'phone_ah'},\n 'fax': {'to_field': 'fax'},\n },\n )\n\n class Meta:\n model = Client\n fields = (\n 'name',\n 'ref',\n 'is_active',\n 'account',\n\n 'primary_contact',\n )\n```\n\n## Tests\nRun tests with `python example/manage.py test testapp`\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A Django library for importing CSVs and other structured data quickly using Django's ModelForm for validation and deserialisation into an instance.",
"version": "0.7.3",
"project_urls": {
"Documentation": "https://github.com/uptick/django-model-import/tree/master/tests/djangoexample",
"Homepage": "https://github.com/uptick/django-model-import",
"Repository": "https://github.com/uptick/django-model-import"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "1312e24f2a7e89add621dcf27fda209ca96374b481e04fc048bfb4f348418402",
"md5": "e102ac0764022389579d0d083b8ecd43",
"sha256": "d6f577a262af777009d38d3046f6d30e58f5316d8b7f62aea1b0d345008f5422"
},
"downloads": -1,
"filename": "django_model_import-0.7.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "e102ac0764022389579d0d083b8ecd43",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10,<4.0",
"size": 18336,
"upload_time": "2024-03-06T00:49:33",
"upload_time_iso_8601": "2024-03-06T00:49:33.229569Z",
"url": "https://files.pythonhosted.org/packages/13/12/e24f2a7e89add621dcf27fda209ca96374b481e04fc048bfb4f348418402/django_model_import-0.7.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "2ea480e22160f866a1ad793159b6676ed97f653f5c558cd6b317217c8624034e",
"md5": "61743e3ab93b5d55a8cbcf456563368a",
"sha256": "8ca42daad8153bed4f7eb935e9b89df879d82540a5f43f596bdf1fcda9460894"
},
"downloads": -1,
"filename": "django_model_import-0.7.3.tar.gz",
"has_sig": false,
"md5_digest": "61743e3ab93b5d55a8cbcf456563368a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10,<4.0",
"size": 15630,
"upload_time": "2024-03-06T00:49:34",
"upload_time_iso_8601": "2024-03-06T00:49:34.647666Z",
"url": "https://files.pythonhosted.org/packages/2e/a4/80e22160f866a1ad793159b6676ed97f653f5c558cd6b317217c8624034e/django_model_import-0.7.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-03-06 00:49:34",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "uptick",
"github_project": "django-model-import",
"travis_ci": true,
"coveralls": false,
"github_actions": false,
"lcname": "django-model-import"
}