Ich versuche, eine CSV-Datei mithilfe eines Formulars zu importieren, um die Datei vom Client-System hochzuladen. Nachdem ich die Datei habe, nehme ich Teile davon und fülle ein Modell in meiner App auf. Ich erhalte jedoch den Fehler "Iterator sollte Strings und nicht Bytes zurückgeben", wenn ich über die Zeilen in der hochgeladenen Datei iteriere. Ich habe stundenlang verschiedene Dinge ausprobiert und alles gelesen, was ich dazu finden konnte, aber es scheint, als ob ich es nicht lösen kann. Ich habe Dinge entfernt, um nur den Fehler zu finden, und habe es so ausgeführt, um sicherzustellen, dass es immer noch da ist. Der Fehler wird angezeigt, wenn in tools_clubs_import () die Zeile "für Clubs in club_list" ausgeführt wird:
Das Folgende ist die korrigierte views.py, die funktioniert, basierend auf der unten markierten Antwort:
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')
Das Folgende ist die Originalversion dessen, was ich eingereicht habe (der HTML-Code, der das Formular generiert, befindet sich am Ende dieser Codeliste:
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 %}
Ausnahmewert:
der Iterator sollte Zeichenfolgen und keine Bytes zurückgeben. (Haben Sie die Datei im Textmodus geöffnet?)
Ausnahmeposition: /usr/lib/python3.3/csv.py in Feldnamen, Zeile 96
request.FILES
gibt Ihnen binary files, aber das Modul csv
möchte stattdessen Dateien im Textmodus haben.
Sie müssen die Datei in ein io.TextIOWrapper()
instance einschließen und die Codierung herausfinden:
from io import TextIOWrapper
f = TextIOWrapper(request.FILES['filename'].file, encoding=request.encoding)
Es wäre wahrscheinlich besser, wenn Sie den Parameter charset
aus dem Header Content-Type
nehmen würden, falls vorhanden. Das sagt Ihnen der Client, der Zeichensatz ist.
Sie können nicht umgehen, wenn Sie die richtige Codierung für die Dateidaten kennen müssen. Sie können die Interpretation als ASCII-Code erzwingen, indem Sie beispielsweise auch ein Schlüsselwort errors
angeben ("Ersetzen" oder "Ignorieren"). Dies führt jedoch zu Datenverlust:
f = TextIOWrapper(request.FILES['filename'].file, encoding='ascii', errors='replace')
Die Verwendung von TextIOWrapper funktioniert nur mit Django 1.11 und höher (als dieser Änderungssatz fügte die erforderliche Unterstützung hinzu ). In früheren Versionen können Sie die Unterstützung nachträglich mit einem Affen-Patch versehen:
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
In Python 3 habe ich Folgendes verwendet:
import csv
from io import StringIO
csvf = StringIO(xls_file.read().decode())
reader = csv.reader(csvf, delimiter=',')
xls_file ist die Datei, die aus dem POST-Formular stammt. Ich hoffe, es hilft.
Verbinden Sie Ihre beiden Methoden, dies schlägt in Python 3.5.2 und Django 1.9 niemals fehl
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)