Python 如何解决“迭代器应该返回字符串,而不是字节”

Python 如何解决“迭代器应该返回字符串,而不是字节”,python,django,python-3.x,iterator,Python,Django,Python 3.x,Iterator,我正在尝试导入CSV文件,使用表单从客户端系统上载该文件。在我有了这个文件后,我会把它的一部分,并在我的应用程序中填充一个模型。但是,当我遍历上传文件中的行时,迭代器应该返回字符串,而不是字节错误。我花了几个小时尝试不同的东西,阅读了所有我能找到的东西,但似乎无法解决它。注意,我对Django(运行1.5)和python(运行3.3)比较陌生。我去掉了一些东西,只是为了找出错误,然后像这样运行它,以确保它仍然存在。执行工具俱乐部导入中俱乐部列表中俱乐部的行时显示错误: 以下是正确的views.p

我正在尝试导入CSV文件,使用表单从客户端系统上载该文件。在我有了这个文件后,我会把它的一部分,并在我的应用程序中填充一个模型。但是,当我遍历上传文件中的行时,迭代器应该返回字符串,而不是字节错误。我花了几个小时尝试不同的东西,阅读了所有我能找到的东西,但似乎无法解决它。注意,我对Django(运行1.5)和python(运行3.3)比较陌生。我去掉了一些东西,只是为了找出错误,然后像这样运行它,以确保它仍然存在。执行工具俱乐部导入中俱乐部列表中俱乐部的行时显示错误:

以下是正确的views.py,基于下面标记的答案:

以下是我提交内容的原始版本。生成表单的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在fieldnames中,第96行

request.FILES为您提供二进制文件,但csv模块希望使用文本模式文件

您需要将文件包装在一个文件夹中,并且需要确定编码:

from io import TextIOWrapper

f = TextIOWrapper(request.FILES['filename'].file, encoding=request.encoding)
如果您从Content-Type头中获取charset参数(如果提供的话),可能会更好;这就是客户机告诉您的字符集

您无法解决需要知道文件数据的正确编码的问题;您可以强制解释为ASCII,例如,通过提供错误关键字并将其设置为“替换”或“忽略”,但这确实会导致数据丢失:

f = TextIOWrapper(request.FILES['filename'].file, encoding='ascii', errors='replace')
仅当使用Django 1.11及更高版本作为时,使用TextIOWrapper才有效。在早期版本中,您可以在以下情况下在中对支持进行修补:

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_文件是从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)

生成“InMemoryUploadedFile”对象的没有属性“readable”。@MeaOrdo:Updated;很抱歉,我没有运行atm的Django-on-3.x,所以这里只进行源代码读取和推断:-@MeaOrdo:迭代开始阅读;您向csv请求一行,然后模块向该文件请求数据。InMemoryUploadedFile对象充当实际文件对象io.BytesIO对象的代理。您确实需要使用TextIOWrapper包装该对象,但必须找到正确的编码。如果所有其他操作都失败,请使用TextIOWrapperrequest.FILES['filename'].file,encoding='ASCII',errors='replace'。你看过request.META了吗。是否键入内容以查看是否指定了字符集参数?您的操作方法是正确的,但不能忽略编码。如果您的客户上载国际数据,您需要能够以Unicode格式读取这些数据。@Jonathan:谢谢您的编辑;不过,提前一点也可以。我添加了一个变通方法。根据,请不要使用bare Exception子句-您特别希望出现哪些例外情况?
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)