Save a writable file into database? - python

I am using django 1.8 and python 3.4 and trying to create a json file and then writing into it, after that I need to save it to my database but on save it returns me an error '_io.TextIOWrapper' object has no attribute '_committed'. Can anyone please help where I am doing wrong?
Here is my models.py
class ConvertedFile(models.Model):
file = models.FileField(upload_to='json/upload', max_length=5000)
created_on = models.DateTimeField(auto_now_add=True)
My views.py is-
def convert_file(request):
url = request.GET.get('q', None)
r = requests.get(url, stream=True)
with open('file.csv', 'wb') as out_file:
shutil.copyfileobj(r.raw, out_file)
csvfile = open("file.csv", "r")
jsonfile = open("file.json", "w")
csv_rows = []
reader = csv.DictReader(csvfile)
title = reader.fieldnames
try:
for row in reader:
csv_rows.extend([{title[i]: row[title[i]] for i in range(len(title))}])
except:
pass
jsonfile.write(json.dumps(csv_rows, sort_keys=False, indent=4, separators=(',', ': '), ensure_ascii=False))
os.remove("file.csv")
jsonfile.close()
new_json = ConvertedFile.objects.create()
new_json.file = jsonfile
new_jsone.save()

The error raises on model.save() in the last line, right? The line above new_json.file = jsonfile is the problem. You pass the reference to a closed (plain python) file object to the FileField from django and it does not know how to deal with it (_commited is missing for example).
Have a look at Django - how to create a file and save it to a model's FileField?

Related

Django : How to upload CSV file in unit test case using APIClient from rest_framework

def test_upload_csv_success(self):
"""Test uploading a csv file"""
with open("innovators.csv", "w") as file:
writer = csv.writer(file)
writer.writerow(["SN", "Name", "Contribution"])
writer.writerow([1, "Linus Torvalds", "Linux Kernel"])
writer.writerow([2, "Tim Berners-Lee", "World Wide Web"])
writer.writerow([3, "Guido van Rossum", "Python Programming"])
with open("innovators.csv", "r") as file:
res = self.client.post(
CSV_URL, {"file": file}, content_type="multipart/form-data"
)
file.close()
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
#self.assertIn('file', res.data)
#self.assertTrue(os.path.exists(self.csv_model.file.path))
Below is the error, I/m getting
System check identified no issues (0 silenced).
.F.
FAIL: test_upload_csv_success (core.tests.test_csv_api.CsvUploadTests)
Test uploading a csv file
Traceback (most recent call last):
File "/Users/rounaktadvi/django_rest_api_projects/csv-store-api/core/tests/test_csv_api.py", line 56, in test_upload_csv_success
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
AssertionError: 400 != 201
I figured it, out here's what i did
#patch("pandas.read_csv")
#patch("pandas.DataFrame.to_sql")
def test_upload_csv_success(self, mock_read_csv, mock_to_sql) -> None:
"""Test uploading a csv file"""
file_name = "test.csv"
# Open file in write mode (Arrange)
with open(file_name, "w") as file:
writer = csv.writer(file)
# Add some rows in csv file
writer.writerow(["name", "area", "country_code2", "country_code3"])
writer.writerow(
["Albania", 28748, "AL", "ALB"],
)
writer.writerow(
["Algeria", 2381741, "DZ", "DZA"],
)
writer.writerow(
["Andorra", 468, "AD", "AND"],
)
# open file in read mode
data = open(file_name, "rb")
# Create a simple uploaded file
data = SimpleUploadedFile(
content=data.read(), name=data.name, content_type="multipart/form-data"
)
# Perform put request (Act)
res = self.client.put(CSV_URL, {"file_name": data}, format="multipart")
# Mock read_csv() and to_sql() functions provided by pandas module
mock_read_csv.return_value = True
mock_to_sql.return_value = True
# Assert
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
self.assertEqual(res.data, "Data set uploaded")
# Delete the test csv file
os.remove(file_name)

Storing the blank file using Django and Python

I need some help. I need to save the .csv file into local folder using Python but its storing the blank file. I am explaining my code below.
views.py:
report = Reactor.objects.all()
filename = str(uuid.uuid4()) + '.csv'
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename='+filename
with open(settings.FILE_PATH + filename, 'w') as csv_file:
file_writer = csv.writer(csv_file)
response_writer = csv.writer(response)
file_writer.writerow(['Name', 'Status', 'Date'])
response_writer.writerow(['Name', 'Status', 'Date'])
for rec in report:
if rec.status == 1:
status = 'Start'
if rec.status == 0:
status = 'Stop'
if rec.status == 2:
status = 'Suspend'
file_writer.writerow([rec.rname, status, rec.date])
response_writer.writerow([rec.rname, status, rec.date])
return response
settings.py:
FILE_PATH = os.getcwd()+'/upload/'
Here I am also downloading the file and I need to save that file into folder but here some blank file is storing. Please help me.
As jasonharper said you are only writing the csv data to the response and not to the file on the disk. Start by creating another writer object:
file_writer = csv.writer(open(settings.FILE_PATH + filename, 'w'))
Now each time you call writerow on writer also do this for the file_writer:
file_writer.writerow(['Name', 'Status', 'Date'])
...
file_writer.writerow([rec.rname, status, rec.date])
It is best to use the with statement to let python automatically close the file:
with open(settings.FILE_PATH + filename, 'w') as csv_file:
file_writer = csv.writer(csv_file)
response_writer = csv.writer(response)
file_writer.writerow(['Name', 'Status', 'Date'])
response_writer.writerow(['Name', 'Status', 'Date'])
for rec in report:
...
file_writer.writerow([rec.rname, status, rec.date])
response_writer.writerow([rec.rname, status, rec.date])
return response

JSON Single Line Parse to Multi-Line CSV with Python

I am a Python noob, and working with the Plaid API to get bank transactions. I would like each transaction to be it's own line, and I only want to pull four values per record: date, _account, name & amount, and populate a CSV file with that data. I have the below code which populates a single line CSV (JSON file also attached). I can't seem to figure out what I am looking for as far as examples on how to do this after a bit of Googling. Any help is much appreciated.
import csv
#Configuration
from plaid import Client
Client.config({
'url': 'https://api.plaid.com'
})
#Connect to Plaid
from plaid import Client
from plaid import errors as plaid_errors
from plaid.utils import json
client = Client(client_id='test_id', secret='test_secret')
account_type = 'suntrust'
try:
response = client.connect(account_type, {
'username': 'plaid_test',
'password': 'plaid_good'
})
except plaid_errors.PlaidError:
pass
else:
connect_data = response.json()
#Get transactions from Plaid
response = client.connect_get()
transactions = response.json()
#Save the transactions JSON response to a csv file in the Python Projects directory
with open('transactions.csv', 'w') as outfile:
json.dump(transactions, outfile)
csvfile = open('transactions.csv', 'r')
jsonfile = open('transactions.json', 'w')
fieldnames = ("date", "_account","name","amount")
reader = csv.DictReader(csvfile, fieldnames)
for row in reader:
json.dump(row, jsonfile)
jsonfile.write('\n')
JSON FILE
I think you are making this over-complicated and confusing JSON with CSV. Hat tip to #thalesmallo who beat me to the punch on using the DictWriter class. Try this:
import csv
from plaid import Client
Client.config({
'url': 'https://api.plaid.com'
})
#Connect to Plaid
from plaid import Client
from plaid import errors as plaid_errors
from plaid.utils import json
client = Client(client_id='test_id', secret='test_secret')
account_type = 'suntrust'
try:
response = client.connect(account_type, {
'username': 'plaid_test',
'password': 'plaid_good'
})
except plaid_errors.PlaidError:
pass
else:
connect_data = response.json()
response = client.connect_get()
data = response.json()
transactions = data['transactions'] # see https://plaid.com/docs/api/#data-overview
#Save the transactions JSON response to a csv file in the Python Projects directory
header = ("date", "_account", "name", "amount")
with open('transactions.csv', 'w') as f:
writer = csv.DictWriter(f, fieldnames=header, extrasaction='ignore')
writer.writeheader()
for x in transactions:
writer.writerow(x)

Exporting items from a model to CSV Django / Python

I'm fairly new to django and Python and want to be able to export a list of items in my model i.e products. I'm looking at the documentation here - https://docs.djangoproject.com/en/dev/howto/outputting-csv/
I'm persuming I need will need to create a variable that stores all the data that I want. But not sure where it would within the snippet of code on the link above.
Apologies as this is a very noobish question but would really Any help at all.
Here is the code to my script so far:
import csv
from products.models import Product
from django.http import HttpResponse
def export_to_csv(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="mytest.csv"'
Have a look at the python csv module.
You'll probably want to get the models fields with
def get_model_fields(model):
return model._meta.fields
Then use
getattr(instance, field.name)
to get the field values (as in this question).
Then you'll want something like
with open('your.csv', 'wb') as csvfile:
writer = csv.writer(csvfile)
# write your header first
for obj in YourModel.objects.all():
row = ""
for field in fields:
row += getattr(obj, field.name) + ","
writer.writerow(row)
It's a bit verbose (and untested), but it should give you an idea. (Oh and don't forget to close your file)
Depending on the scenario - you may want to have a CSV of your model. If you have access to the Django Admin site, you can plug in a generic action for any model displayed as a list (google: django admin actions)
http://djangosnippets.org/snippets/790/
If you're operating with a console (python manage.py ...), you can use such a script, which I just used:
(place it in: yourapp/management/commands/model2csv.py)
"""
Prints CSV of all fields of a model.
"""
from django.core.management.base import BaseCommand, CommandError
import csv
import sys
class Command(BaseCommand):
help = ("Output the specified model as CSV")
args = '[appname.ModelName]'
def handle(self, *app_labels, **options):
from django.db.models import get_model
app_name, model_name = app_labels[0].split('.')
model = get_model(app_name, model_name)
field_names = [f.name for f in model._meta.fields]
writer = csv.writer(sys.stdout, quoting=csv.QUOTE_ALL)
writer.writerow(field_names)
for instance in model.objects.all():
writer.writerow([unicode(getattr(instance, f)).encode('utf-8') for f in field_names])
This does not catch any exceptions etc., but as an Admin you won't cause them to be raised, right?
Use it like:
./manage.py model2csv my_ecommerce.Product > products.csv
You can also make a template to assist in formatting!
The template is a common Django template
from django.template import loader
def export_to_csv(request):
response = HttpResponse(mimetype='text/csv')
response['Content-Disposition'] = 'attachment; filename="products-list.csv"'
template = loader.get_template('templates/products_template.csb')
response.write(template.render(Context({'products': Products.objects.all()})))
return response
Using django.db.models.query.QuerySet.values results in more optimised queries for my use case.
import csv
from datetime import datetime
from django.http import HttpResponse
# Populate this list with your model's fields
# Replace MODEL with your model
fields = [f.name for f in MODEL._meta.fields]
# The following code will live inside your view
timestamp = datetime.now().isoformat()
response = HttpResponse(content_type="text/csv")
response[
"Content-Disposition"
] = f"attachment; filename={timestamp}.csv"
writer = csv.writer(response)
# Write the header row
writer.writerow(fields)
# Replace MODEL with your model
for row in MODEL.objects.values(*fields):
writer.writerow([row[field] for field in fields])
return response
I use this on my code. A function called from view.
It automatically get model fields to make columns.
You can also customize the field list you want to export.
Function
import csv
from django.http import HttpResponse
from .models import Books
def export_qs_to_csv(model_class = None, qs = None, field_names = None):
if model_class and not qs:
qs = model_class.objects.all()
if qs and not model_class:
model_class = qs.model
meta = model_class._meta
if not field_names:
field_names = [field.name for field in meta.fields]
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
writer = csv.writer(response)
writer.writerow(field_names)
for obj in qs:
row = writer.writerow([getattr(obj, field) for field in field_names])
return response
Usage
#user_passes_test(lambda u: u.is_superuser)
def export_books(request):
return export_qs_to_csv(model_class = Books)
# or
return export_qs_to_csv(qs = Books.objects.filter(published = True))
# or
return export_qs_to_csv(
qs = Books.objects.filter(published = True),
field_names = [
"title",
"price",
"publishing_date",
]
)
Original answer
It works, and it needs only to define model class in model_class variable.
This Django view let use downloads CSV. CSV name is Django_app.model_name.csv.
import csv
from django.http import HttpResponse
from .models import Trade
def export_to_csv(request):
# The only line to customize
model_class = Trade
meta = model_class._meta
field_names = [field.name for field in meta.fields]
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
writer = csv.writer(response)
writer.writerow(field_names)
for obj in model_class.objects.all():
row = writer.writerow([getattr(obj, field) for field in field_names])
return response
Here is a potential solution, based on #tomasz-gandor 's answer, but updated to 2020:
"""
Prints CSV of all fields of a model.
"""
import csv
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
help = ("Output the specified model as CSV")
def add_arguments(self, parser):
parser.add_argument('model',
nargs=1,
type=str,
help='Model name to export, like <app.model> or "members.Member"')
parser.add_argument('outfile',
nargs=1,
type=str,
help='Save path, like </path/to/outfile.csv> or "/data/members.csv"')
def handle(self, *app_labels, **options):
from django.apps import apps
app_name, model_name = options['model'][0].split('.')
model = apps.get_model(app_name, model_name)
field_names = [f.name for f in model._meta.fields]
writer = csv.writer(open(options['outfile'][0], 'w'), quoting=csv.QUOTE_ALL, delimiter=',')
writer.writerow(field_names)
for instance in model.objects.all():
writer.writerow([str(getattr(instance, f)) for f in field_names])
Can easily be used with:
python manage.py model2csv members.Member /data/members_export.csv
If you don't care about fieldnames and want all the fields, just do this.
with open('file_name.csv', 'w') as csvfile:
writer = csv.writer(csvfile)
for obj in YourModel.objects.values_list():
row = list(obj)
writer.writerow(row)
I combined some of the previous answers, because I needed to import some data from production and change some of it along the way. So here is my solution, which you can use to override some field values while writing the CSV file.
Export some queryset data into CSV file:
import csv
from myapp.models import MyModel
from user.models import User
# Make some queryset manually using Django shell:
user = User.objects.get(username='peterhil')
queryset = MyModel.objects.filter(user=user)
def query_to_csv(queryset, filename='items.csv', **override):
field_names = [field.name for field in queryset.model._meta.fields]
def field_value(row, field_name):
if field_name in override.keys():
return override[field_name]
else:
return row[field_name]
with open(filename, 'w') as csvfile:
writer = csv.writer(csvfile, quoting=csv.QUOTE_ALL, delimiter=',')
writer.writerow(field_names) # write the header
for row in queryset.values(*field_names):
writer.writerow([field_value(row, field) for field in field_names])
# Example usage:
query_to_csv(queryset, filename='data.csv', user=1, group=1)
Use this solution for model csv file.might being helpful
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment;
filename="somefilename.csv"'
writer = csv.writer(response);
writer.writerow(["username","Email"]);
for i in User.objects.all():
writer.writerow([i.username,i.email])
return response
I used the django-queryset-csv package.
Follow these steps:
pip install django-queryset-csv
Your views.py:
import djqscsv
from products.models import Product
def get_csv(request):
qs = Product.objects.all()
return djqscsv.render_to_csv_response(qs)

Want to prompt browser to save csv

Want to prompt browser to save csv using pyramid.response.Response searched for clues and found here's a link Django answer but i can't use it with Pyramid wsgi my code looks like this:
from pyramid.response import Response
def get_list_names_emails(request):
session, env = request.db, request.client_env
response = Response(content_type='text/csv')
output = StringIO()
writer = csv.writer(output)
writer.writerow(['SomeName', 'SomeEmail', 'CompanyName])
csv_output = output.getvalue()
return csv_output
As a cleaner way to do that, you can register a renderer.
In your configuration set-up, add:
config.add_renderer(name='csv',
factory='mypackage.renderers.CSVRenderer')
then in mypackage/renderers.py:
class CSVRenderer(object):
def __init__(self, info):
pass
def __call__(self, value, system):
fout = StringIO.StringIO()
writer = csv.writer(fout, delimiter=';', quoting=csv.QUOTE_ALL)
writer.writerow(value['header'])
writer.writerows(value['rows'])
resp = system['request'].response
resp.content_type = 'text/csv'
resp.content_disposition = 'attachment;filename="report.csv"'
return fout.getvalue()
After that, you can decorate your view with the renderer:
#view_config(..., renderer='csv')
def myview(self):
header = ['name', 'surname', 'address']
rows = [
(
row['name'],
row['surname'],
row['address'],
)
for row in query_rows(.....)
]
return {
'header': header,
'rows': rows
}
The advantage of this approach is better testable view code (you just check for the dictionary values, no need to parse anything) and you can also add a XLS or whatever renderer to the same view:
#view_config(..., renderer='xls')
#view_config(..., renderer='csv')
def myview(self):
...
Try adding Content-Disposition:
response['Content-Disposition'] = 'attachment; filename="report.csv"'
It's better to set content type as well
response['Content-type'] = 'text/csv'
response['Content-Disposition'] = 'attachment; filename="report.csv"'

Categories

Resources