2013-04-26 17 views
19

Bir CSV dosyasını, istemci sistemden dosya yüklemek için bir form kullanarak almaya çalışıyorum. Dosyayı aldıktan sonra, bunun bir kısmını alıp uygulamanıza bir model yerleştireceğim. Ancak, yüklenen dosyadaki satırların üzerinde yinelemeye gittiğimde bir "yineleyicinin dizeleri değil baytları döndürmesi" hatası alıyorum. Farklı şeyler denemek için saatler harcadım ve bu konuda bulabildiğim her şeyi okudum ama çözemiyorum (not, Django'ya göre nispeten yeniyim - 1.5 ve python koşuyorum - 3.3). Sadece hatayı bulmak için bir şeyler çıkardım ve hala orada olduğundan emin olmak için böyle koştum. "Club_list içinde kulüp için" satırı çalıştırırken hata) (tools_clubs_import gösterilir:"Yineleyici, dizeleri değil baytları döndürmelidir"

yanıta göre, işler düzeltilmiş views.py aşağıda işaretlenmiş aşağıdadır:

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

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

İstisna değeri: Ben gönderdiklerinizden orijinal versiyonu aşağıdaki (formunu oluşturur html bu kod listesinin en altında yer almaktadır

yineleyici dizeleri değil, bayt dönmelidir

İstisna Konum (metin modunda dosyayı açmak mı?): FIELDNAMES içinde /usr/lib/python3.3/csv.py, hat 96

cevap

28

request.FILES size ikili dosyaları verir, ancak csv modülü bunun yerine metin modu dosyalarına sahip olmak ister.

Bir io.TextIOWrapper() instance dosyayı sarmak için gerekir ve kodlama anlamaya ihtiyacımız:

from io import TextIOWrapper 

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

O ediyorum sağlanan eğer Content-Type başlığından charset parametresini aldı muhtemelen daha iyi; İstemcinin karakter kümesinin ne olduğunu söylemesi budur.

Dosya verileri için doğru kodlamayı bilmeniz gereken bir işlem yapamazsınız; Eğer ('görmezden' o 'yerine' ayarını veya) örneğin yanı bir errors anahtar kelime vererek, ASCII olarak yorumlanmasını zorlayabilir, ancak bu veri kaybına yol vermez: TextIOWrapper olacak sadece işi kullanma

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

Django 1.11 ve sonrası (this changeset added the required support olarak) kullanırken. Önceki sürümlerde, aslında sonra destek maymun-yama edebilirsiniz: piton 3'te

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 
+2

başarısız " 'InMemoryUploadedFile' nesne hiçbir özelliği 'okunabilir' vardır." – MeaOrdo

+1

@MeaOrdo: Güncelleme; Üzgünüm, Django-on-3.x çalışan atm yok, bu yüzden sadece kaynak kod okuma ve ekstrapolasyon var.:-) –

+0

TextIOWrapper ile kodlama ve TextIOWrapper yerine StringIO kullanarak çeşitli varyasyonları denedim. TextIOWrapper'daki farklı kodlamalar bir byte 10 hatasıyla çözülemez ve StringIO kullanılarak yapılan tüm varyasyonlar TypeErrors verir ve bazı kombinasyonlar "InMemoryUploadedFile" ile ilgilidir. Hata aslında request.FILES ['filename'] aşamasında mı? Bunu sonuç üzerinde yinelemeye çalışmadan csv.DictReader'a iletmek bir hata oluşturmaz. – MeaOrdo

8

, ben kullandı:

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

dosya POST formundan var olma xls_file. Umut ediyorum bu yardım eder.

0

Fuse sizin iki yöntem, bu asla Python 3.5.2 ve Django 1.9

üretir
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)