{"id":23440786,"url":"https://github.com/dabapps/csv-wrangler","last_synced_at":"2025-08-14T08:35:20.481Z","repository":{"id":45307440,"uuid":"75715208","full_name":"dabapps/csv-wrangler","owner":"dabapps","description":"Statically typed Python 3 CSV generation helpers","archived":false,"fork":false,"pushed_at":"2021-12-22T15:54:11.000Z","size":51,"stargazers_count":3,"open_issues_count":2,"forks_count":0,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-06-02T17:27:08.951Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dabapps.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":"2016-12-06T09:20:46.000Z","updated_at":"2019-09-26T03:15:25.000Z","dependencies_parsed_at":"2022-09-17T04:31:24.517Z","dependency_job_id":null,"html_url":"https://github.com/dabapps/csv-wrangler","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/dabapps/csv-wrangler","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dabapps%2Fcsv-wrangler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dabapps%2Fcsv-wrangler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dabapps%2Fcsv-wrangler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dabapps%2Fcsv-wrangler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dabapps","download_url":"https://codeload.github.com/dabapps/csv-wrangler/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dabapps%2Fcsv-wrangler/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270387458,"owners_count":24575003,"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","status":"online","status_checked_at":"2025-08-14T02:00:10.309Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-12-23T16:18:41.774Z","updated_at":"2025-08-14T08:35:20.406Z","avatar_url":"https://github.com/dabapps.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"CSV Wrangler\n===================\n[![Build Status](https://travis-ci.com/dabapps/csv-wrangler.svg?token=apzD3FKHpTNKHAtAu9xC\u0026branch=master)](https://travis-ci.com/dabapps/csv-wrangler)\n[![pypi release](https://img.shields.io/pypi/v/csv-wrangler.svg)](https://pypi.python.org/pypi/csv-wrangler)\n\nStatically-typed Python 3 CSV generation helpers.\nWrite nicely typed CSV handling logic, with reorderable headers!\n\n## Getting Started\n\n### Installation\n\nInstall with `pip`\n\n    pip install csv-wrangler\n\nAdd `csv_wrangler` to your installed apps\n\n    INSTALLED_APPS = (\n        ...\n        'csv_wrangler',\n    )\n\n## Usage\n\n### Create an Exporter\n\nGenerally, you'll want to subclass `Exporter` and provide two required changes, `headers` and `fetch_records`. You'll also want a way to get data into the exporter - override `__init__` for this.\n\nYou'll also need to specify a type for your headers to run over. We recommend you go with something like a `NamedTuple`, but any blob of data that is meaningful to you will do.\n\nLet's create a `llama_exporter.py`:\n\n```python\nfrom typing import List\nfrom csv_wrangler.exporter import Exporter, Header\nfrom .models import Llama\n\n# We start by defining Llama's type\nLlama = NamedTuple('Llama', [('first_name': str), ('last_name': str), ('fluff_factor': int)])\n\n# We can define a helper function if needed\ndef get_full_name(llama):\n    return \"{} {}\".format(llama.first_name, llama.last_name)\n\n\n# And we'll need our derived class\nclass LlamaExporter(Exporter):\n\n    headers = [\n        Header(label='name', callback=get_full_name),\n        Header(label='fluff_factor', callback=lambda llama: str(llama.fluff_factor)),\n        Header(label='first_name_length', callback=lambda llama: str(len(llama.first_name))),\n    ]  # type: List[Header[Llama]]\n\n    def __init__(self, llamas: List[Llama]) -\u003e None:\n        self.data = llamas\n\n    def fetch_records(self) -\u003e List[Llama]:\n        return self.data\n```\n\nHere, our `fetch_records` is just spitting the data straight to the headers for unpacking. If you have more complex requirements, `fetch_records` is a good place to convert to a list of easily accessed blobs of data.\n\nNote that we specify the type of the header for `headers`. This allows the typechecker to find out quickly if any of your headers are accessing data incorrectly.\n\nWhen you want to access `self` in `headers`, you can use the function `get_headers` instead. (Try to avoid using this function if not necessary, as it will slow things down) For example, the `LlamaExporter` could look like this:\n\n```python\nclass LlamaExporter(Exporter):\n\n    def __init__(self, llamas: List[Llama]) -\u003e None:\n        self.data = llamas\n        self.llamas_are_cute = True\n\n    def fetch_records(self) -\u003e List[Llama]:\n        return self.data\n\n    def get_headers(self) -\u003e List[Header[Llama]]:\n        return [\n            Header(label='name', callback=get_full_name),\n            Header(label='fluff_factor', callback=lambda llama: str(llama.fluff_factor)),\n            Header(label='first_name_length', callback=lambda llama: str(len(llama.first_name))),\n            Header(label='are_llamas_cute?', callback=lambda llama: str(self.llamas_are_cute)),\n        ]\n```\n\n### Use the Exporter\nNow, we can use it. We have several methods for getting data out:\n\n`to_list` will convert the data to a list of lists of strings (allowing you to pass it to whatever other CSV handling options you want):\n\n```python\nfrom .exporters import LlamaExporter\nfrom .models import Llama\n...\n\n\ndef some_function_where_i_want_to_use_csv_data():\n    my_llamas = Llama.objects.all()\n    exporter = LLamaExporter(llamas=my_llamas)\n    data = exporter.to_list()\n    # perhaps pass data to other CSV handling options\n    return\n```\n\nwhereas `as_response` will turn it into a prepared HttpResponse for returning from one of your views:\n\n```python\nfrom django.views import View\nfrom .exporters import LlamaExporter\nfrom .models import Llama\n...\n\n\nclass LlamaCsvExportView(View):\n\n    def get(self, request):\n        my_llamas = Llama.objects.all()\n        exporter = LLamaExporter(llamas=my_llamas)\n        return exporter.as_response(filename='my_llamas')\n```\n\nIf you simply want to get access to the CSV rows as strings, you can use the `as_csv_rows` method, which returns each row of the CSV in a list (as a generator).\n\nWhen you want to setup and endpoint for getting the csv, this'll be as simple as adding the following to `urls.py`\n\n```python\nurl(r'^llamas/csv/$', LlamaCsvExportView.as_view(), name=\"llama-csv\")\n```\n\nOther nice features\n-----------------\n### Streamed Response\n\nIf your CSV is large, and takes a long time to generate, you should use a generator, or stream the response. `to_iter` and `as_streamed_response` are the generator_counterparts to the above methods, working in exactly the same way, just returning a generator and a `HttpStreamedResponse` respectively. By default, `to_list` calls `to_iter`, so if you need to do anything custom, it's best to do it in `to_iter`.\n\n### Save to file-like object\n\nSometimes, you may need to save the CSV rather than return it as a response. To do this, you can pass a file-like object to the `dump` method. The CSV will be dumped into the file, without loading the whole thing into memory first.\n\nThis can be combined with [`StringIO`](https://docs.python.org/3/library/io.html#io.StringIO) to access the entire CSV as a string (not recommended for large CSVs).\n\n### Ordering headers\nYou can also provide an ordering to the headers, if you want.  Simply assign a list of strings to `header_order` and when the data is unpacked, those headers who's labels match these will be placed in that order.\n\nSo for example in your view, you can add:\n\n```python\nexporter = LLamaExporter(llamas=my_llamas)\nexporter.header_order = ['name', 'first_name_length', 'fluff_factor']\n```\n\n### Mulitple CSVs\nIf you end up in a situation where you need to output multiple CSV tables at once, you can use `MultiExporter`\n\n```python\nexporter = MultiExporter(\n    LlamaExporter(my_llamas),\n    AlpacaExporter(my_alpacas)\n)\nexporter.to_list()\n```\n\nThis will append the second CSV after the first, with a single blank line between it.\n\n### Simple Exporter\nWe also provide a `SimpleExporter`, for extracting information from a list of dictionaries.\n\nSay you have a list of dictionaries\n\n```python\ndicts = [\n    {\n        'name': 'Lama glama',\n        'fluff_factor': 9,\n        'is_cute?': 'yes'\n    },{\n        'name': 'Lama guanicoe',\n        'fluff_factor': 2,\n    }\n]\n```\nThen you can create a simple exporter with `headers = ['name', 'fluff_factor', 'is_cute?']` as `exporter = SimpleExporter(headers, dicts)`.\nYour `exporter.to_list()` would be `[['name', 'fluff_factor', 'is_cute?'], ['Lama glama', '9', 'yes'], ['Lama guanicoe', '2', '']]`.\n\n### Passthrough Exporter\nYou can use the `PassthroughExporter` when you already have a List of Lists of Strings.\n\nSo when you have the exact data you want to put in your CSV, you can simply create an exporter like this\n```python\nexporter = PassthroughExporter([\n    ['name', 'fluff factor', 'is cute?'],\n    ['Alpaca LLama', '10', 'yes'],\n    ['Guanaco Llama', '6', 'no'],\n])\n```\n\nHave fun!\n\n## Code of conduct\n\nFor guidelines regarding the code of conduct when contributing to this repository please review [https://www.dabapps.com/open-source/code-of-conduct/](https://www.dabapps.com/open-source/code-of-conduct/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdabapps%2Fcsv-wrangler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdabapps%2Fcsv-wrangler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdabapps%2Fcsv-wrangler/lists"}