for __iter__ python django python-3.x iterator

python - __iter__ - Cómo resolver "iterador debe devolver cadenas, no bytes"



iterator python 3 (3)

En Python 3, utilicé:

import csv from io import StringIO csvf = StringIO(xls_file.read().decode()) reader = csv.reader(csvf, delimiter='','')

xls_file siendo el archivo obtenido del formulario POST. Espero que ayude.

Estoy intentando importar un archivo CSV, usando un formulario para cargar el archivo desde el sistema cliente. Después de tener el archivo, tomaré partes de él y poblaré un modelo en mi aplicación. Sin embargo, aparece un error "el iterador debería devolver cadenas, no bytes" cuando voy a iterar sobre las líneas en el archivo cargado. Pasé horas probando cosas diferentes y leyendo todo lo que pude encontrar en esto, pero no puedo resolverlo (nota, soy relativamente nuevo en Django, que ejecuta 1.5 y python, que ejecuta 3.3). Desplegué las cosas para solucionar el error y lo ejecuté así para asegurarme de que todavía está allí. El error se muestra al ejecutar la línea "para clubes en club_list" en tools_clubs_import ():

El siguiente es el views.py corregido que funciona, según la respuesta marcada a continuación:

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'')

La siguiente es la versión original de lo que envié (el html que genera el formulario se incluye al final de esta lista de códigos)

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 %}

Valor de excepción:

El iterador debe devolver cadenas, no bytes (¿abrió el archivo en modo de texto?)

Ubicación de excepción: /usr/lib/python3.3/csv.py en nombres de campo, línea 96


Fusiona tus dos métodos, esto nunca falla en Python 3.5.2 y 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)


request.FILES le proporciona archivos binarios , pero el módulo csv quiere tener archivos en modo de texto.

io.TextIOWrapper() envolver el archivo en una instancia de io.TextIOWrapper() y debe averiguar la codificación:

from io import TextIOWrapper f = TextIOWrapper(request.FILES[''filename''].file, encoding=request.encoding)

Probablemente sería mejor si tomara el parámetro charset del encabezado Content-Type si se proporciona; eso es lo que el cliente le dice que el conjunto de caracteres es.

No puede evitar la necesidad de conocer la codificación correcta para los datos del archivo; puede forzar la interpretación como ASCII, por ejemplo, al proporcionar también una palabra clave de errors (configurándola como ''reemplazar'' o ''ignorar''), pero eso lleva a la pérdida de datos:

f = TextIOWrapper(request.FILES[''filename''].file, encoding=''ascii'', errors=''replace'')

El uso de TextIOWrapper solo funcionará cuando se use Django 1.11 y posterior (ya que este conjunto de cambios agregó el soporte requerido ). En versiones anteriores, puede aplicar un parche en el soporte después del hecho:

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