Как возвращать данные в формате csv в своем API на Python?
Есть json файл, и есть ручка, в которой нужно выводить его содержимое в csv формате
например
"ip","segment"
"1.1.1.1","google"
"6.6.6.6","hell"
пробовал разные варианты с библиотекой csv, но выдает различные ошибки вывода.
Мой код:
import json
from fastapi import FastAPI
import csv
app = FastAPI()
data = open('list.json', 'r')
fieldnames = ['ip', 'type']
writer = csv.DictWriter(data, fieldnames=fieldnames)
list = json.loads(data)
@app.get("/")
async def root():
writer.writeheader()
for ip in list:
writer.writerow(ip)
Ответы (1 шт):
Проблемы:
json.loadsразбирает json из строки, а не из файла. Нужно использовать илиjson.loads(data.read())(прочитать файл в строку, потом разобрать данные из строки), или использовать специальную функциюjson.load(data)Вы writer инициализируете для записи в тот же файл, из которого вы читаете. Нужно писать в другой файл.
В целом, для ответа с сервера лучше использовать объект
io.StringIO- это объект, который с точки зрения Python ведет себя как файл, но файл фактически нигде не создается, данные хранятся просто в памяти. После записи данных нужно получить значение из него, и вернуть с сервера.Имеет смысл читать из файла и формировать ответ в одном месте - или сразу при старте (снаружи всех функций), тогда сервер всегда будет возвращать одни и те же данные; либо все делать в функции, тогда если файл изменится, и после этого данные будут запрошены, будет возвращен соответствующий новый результат.
Результат нужно с сервера возвращать через
return, плюс еще обернуть его в объектPlainTextResponse, иначе FastAPI обернет его в JSON-строкуДополнительно в параметрах writer-а нужно указать
quoting=csv.QUOTE_ALL, чтобы все значения оборачивались в кавычки, либоquoting=csv.QUOTE_NONNUMERIC- тогда оборачиваться будут только не числовые значения
Итого получается примерно такой код:
Файл JSON
[
{
"ip": "1.1.1.1",
"type": "google"
},
{
"ip": "6.6.6.6",
"type": "hell"
}
]
Код сервиса:
import csv
import io
import json
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse
app = FastAPI()
@app.get("/")
async def root():
with open('list.json', 'r') as file:
data = json.load(file)
fieldnames = ['ip', 'type']
csv_in_memory = io.StringIO(newline="")
writer = csv.DictWriter(
csv_in_memory,
fieldnames=fieldnames,
quoting=csv.QUOTE_ALL
)
writer.writeheader()
for row in data:
writer.writerow(row)
return PlainTextResponse(csv_in_memory.getvalue())
Результат:
Если нужно, чтобы браузер предлагал сохранить файл, то нужно вернуть результат как класс Response, с указанием имени файла в headers и media_type соответствующим типу csv:
@app.get("/")
async def root():
...
headers = {"Content-Disposition": "filename=file.csv"}
return Response(
csv_in_memory.getvalue(),
headers=headers,
media_type="file/csv"
)

