クライアントシステムからファイルをアップロードするフォームを使用して、CSVファイルをインポートしようとしています。ファイルを入手したら、その一部を取り、アプリにモデルを入力します。しかし、アップロードされたファイルの行を反復処理しようとすると、「バイトではなく文字列を返す必要がある」というエラーが発生します。私は何時間も費やしてさまざまなことを試し、これで見つけたすべてのものを読んでいましたが、解決できないようです(メモ、私はDjangoを比較的新しく使用しています-1.5-およびpython-実行中3.3)。エラーのみを取得するために必要なものを取り除き、次のように実行してエラーがまだ存在することを確認します。tools_clubs_import()で「for clubs in club_list」の行を実行すると、エラーが表示されます。
以下は、以下にマークされた回答に基づいて機能する修正されたviews.pyです:
import csv
from io import TextIOWrapper
from Django.shortcuts import render
from Django.http import HttpResponseRedirect
from Django.core.urlresolvers import reverse
from rank.forms import ClubImportForm
def tools_clubs_import(request):
if request.method == 'POST':
form = ClubImportForm(request.POST, request.FILES)
if form.is_valid():
# the following 4 lines dumps request.META to a local file
# I saw a lot of questions about this so thought I'd post it too
log = open("/home/joel/meta.txt", "w")
for k, v in request.META.items():
print ("%s: %s\n" % (k, request.META[k]), file=log)
log.close()
# I found I didn't need errors='replace', your mileage may vary
f = TextIOWrapper(request.FILES['filename'].file,
encoding='ASCII')
club_list = csv.DictReader(f)
for club in club_list:
# do something with each club dictionary entry
pass
return HttpResponseRedirect(reverse('rank.views.tools_clubs_import_show'))
else:
form = ClubImportForm()
context = {'form': form, 'active_menu_item': 4,}
return render(request, 'rank/tools_clubs_import.html', context)
def tools_clubs_import_show(request):
return render(request, 'rank/tools_clubs_import_show.html')
以下は、私が提出したものの元のバージョンです(フォームを生成するhtmlは、このコードリストの最後に含まれています)。
views.py
--------
import csv
from Django.shortcuts import render
from Django.http import HttpResponseRedirect
from rank.forms import ClubImportForm
def tools(request):
context = {'active_menu_item': 4,}
return render(request, 'rank/tools.html', context)
def tools_clubs(request):
context = {'active_menu_item': 4,}
return render(request, 'rank/tools_clubs.html', context)
def tools_clubs_import(request):
if request.method == 'POST':
form = ClubImportForm(request.POST, request.FILES)
if form.is_valid():
f = request.FILES['filename']
club_list = csv.DictReader(f)
for club in club_list:
# error occurs before anything here is executed
# process here... not included for brevity
return HttpResponseRedirect(reverse('rank.views.tools_clubs_import_show'))
else:
form = ClubImportForm()
context = {'form': form, 'active_menu_item': 4,}
return render(request, 'rank/tools_clubs_import.html', context)
def tools_clubs_import_show(request):
return render(request, 'rank/tools_clubs_import_show.html')
forms.py
--------
from Django import forms
class ClubImportForm(forms.Form):
filename = forms.FileField(label='Select a CSV to import:',)
urls.py
-------
from Django.conf.urls import patterns, url
from rank import views
urlpatterns = patterns('',
url(r'^tools/$', views.tools, name='rank-tools'),
url(r'^tools/clubs/$', views.tools_clubs, name='rank-tools_clubs'),
url(r'^tools/clubs/import$',
views.tools_clubs_import,
name='rank-tools_clubs_import'),
url(r'^tools/clubs/import/show$',
views.tools_clubs_import_show,
name='rank-tools_clubs_import_show'),
)
tools_clubs_import.html
-----------------------
{% extends "rank/base.html" %}
{% block title %}Tools/Club/Import{% endblock %}
{% block center_col %}
<form enctype="multipart/form-data" method="post" action="{% url 'rank-tools_clubs_import' %}">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
例外値:
イテレータはバイトではなく文字列を返す必要があります(ファイルをテキストモードで開きましたか?)
例外の場所:/usr/lib/python3.3/csv.py in fieldnames、line 96
_request.FILES
_はbinaryファイルを提供しますが、csv
モジュールは代わりにテキストモードファイルを必要とします。
ファイルを io.TextIOWrapper()
instance でラップする必要があり、エンコーディングを理解する必要があります。
_from io import TextIOWrapper
f = TextIOWrapper(request.FILES['filename'].file, encoding=request.encoding)
_
提供されている場合、_Content-Type
_ヘッダーからcharset
パラメーターを取得した方がよいでしょう。これは、クライアントが文字セットと言っていることです。
ファイルデータの正しいエンコーディングを知っておく必要はありません。たとえば、errors
キーワードを指定して(「置換」または「無視」に設定して)、ASCIIとして強制的に解釈できますが、これによりデータが失われます。
_f = TextIOWrapper(request.FILES['filename'].file, encoding='ascii', errors='replace')
_
TextIOWrapperの使用は、Django 1.11以降を使用している場合にのみ機能します( としてこの変更セットが必要なサポートを追加しました ))。以前のバージョンでは、事後にサポートにパッチを当てます:
_from Django.core.files.utils import FileProxyMixin
if not hasattr(FileProxyMixin, 'readable'):
# Pre-Django 1.11, add io.IOBase support, see
# https://github.com/Django/django/commit/4f474607de9b470f977a734bdd47590ab202e778
def readable(self):
if self.closed:
return False
if hasattr(self.file, 'readable'):
return self.file.readable()
return True
def writable(self):
if self.closed:
return False
if hasattr(self.file, 'writable'):
return self.file.writable()
return 'w' in getattr(self.file, 'mode', '')
def seekable(self):
if self.closed:
return False
if hasattr(self.file, 'seekable'):
return self.file.seekable()
return True
FileProxyMixin.closed = property(
lambda self: not self.file or self.file.closed)
FileProxyMixin.readable = readable
FileProxyMixin.writable = writable
FileProxyMixin.seekable = seekable
_
python 3で、私は使用しました:
import csv
from io import StringIO
csvf = StringIO(xls_file.read().decode())
reader = csv.reader(csvf, delimiter=',')
xls_fileは、POSTフォームから取得したファイルです。お役に立てば幸いです。
Python 3.5.2およびDjango 1.9
delimitador = list_delimitadores[int(request.POST['delimitador'])][1]
try:
text = TextIOWrapper(request.FILES['csv_x'].file, encoding='utf-8 ', errors='replace')
reader = csv.reader(text, delimiter=delimitador)
except:
text = StringIO(request.FILES['csv_x'].file.read().decode())
reader = csv.reader(text, delimiter=delimitador)