Djangoでファイルアップローダを作る

古い情報及び醜いコードがあるので更新しました。(2017/03/16)

## 環境

- Python 3.5.1
- Django 1.9.1
- Virtualenv 15.0.1
- OS X El Capitan 10.11.6

## 最終構成

```
file_uploader
	├── db.sqlite3
	├── file_uploader
	│   ├── __init__.py
	│   ├── settings.py
	│   ├── urls.py
	│   └── wsgi.py
	├── manage.py
	├── requirements.txt
	└── upload_form
	    ├── __init__.py
	    ├── admin.py
	    ├── apps.py
	    ├── migrations
	    │   ├── 0001_initial.py
	    │   └── __init__.py
	    ├── models.py
	    ├── static
	    │   └── files
	    │       └── __init__.py
	    ├── templates
	    │   └── upload_form
	    │       ├── base.html
	    │       ├── complete.html
	    │       └── form.html
	    ├── tests.py
	    ├── urls.py
	    └── views.py
```

こんな感じの構成になっていればいいかなって思います。
`__pycache__` は省略

## セットアップ

### プロジェクト作成

```bash
$ pip install Django==1.9.1
$ django-admin.py startproject file_uploader
$ cd file_uploader
$ django-admin.py startapp upload_form
# あとでどのファイルがいつアップロードされたか確認したいので
$ python manage.py createsuperuser
```

### `settings.py` を変更

```python:settings.py
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'upload_form', # 追加
)

・・・

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

・・・
```

ぐらいしておけばいいかな

### とりあえず確認

```bash
$ python manage.py migrate
$ python manage.py runserver
```

http://127.0.0.1:8000

にアクセスして例のアレが出てきたらOK

## `upload_form` Appの構築

### models.py

```python:upload_form/models.py
from django.db import models
from datetime import datetime

class FileNameModel(models.Model):
    file_name = models.CharField(max_length = 50)
    upload_time = models.DateTimeField(default = datetime.now)
```

`file_name` : ファイル名
`upload_time` : アップロード日時(デフォルトで現在日時が入る)

### views.py

```python:upload_form/views.py
from django.shortcuts import render, redirect
from django.template.context_processors import csrf
from django.conf import settings
from upload_form.models import FileNameModel
import sys, os
UPLOADE_DIR = os.path.dirname(os.path.abspath(__file__)) + '/static/files/'

def form(request):
    if request.method != 'POST':
        return render(request, 'upload_form/form.html')

    file = request.FILES['file']
    path = os.path.join(UPLOADE_DIR, file.name)
    destination = open(path, 'wb')

    for chunk in file.chunks():
        destination.write(chunk)

    insert_data = FileNameModel(file_name = file.name)
    insert_data.save()

    return redirect('upload_form:complete')

def complete(request):
    return render(request, 'upload_form/complete.html')
```

#### テンプレート

```html:upload_form/templates/upload_form/base.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta name="viewport" content="content">
        <title>ファイルアップローダ</title>
    </head>
    <body>
        {% block content %}
            {{ content }}
        {% endblock %}
    </body>
</html>
```

```html:upload_form/templates/upload_form/form.html
{% extends "upload_form/base.html" %}

{% block title %}アップローダサンプル{% endblock title %}
{% block content %}
    <form method="POST" enctype="multipart/form-data">
        {% csrf_token %}
        <input type="file" name="file">
        <input type="submit" value="アップロード">
    </form>
{% endblock content %}
```

```html:upload_form/templates/upload_form/complete.html
{% extends "upload_form/base.html" %}

{% block title %}アップロード完了{% endblock title %}
{% block content %}
    <div align="center">
        <h1 align="center">アップロード完了</h1>
        <a href="{% url 'upload_form:form' %}"><button>戻る</button></a>
    </div>
{% endblock content %}
```

アップロードするともしかするとアップロード先のディレクトリがないぞ！って怒られるかもしれないので
`upload_form/static/files/` というような構成でディレクトリを作成しておいてください。

## URL設定

### urls.py

`upload_form/urls.py` を新規作成して以下

```python:upload_form/urls.py
from django.conf.urls import url
from upload_form import views

urlpatterns = [
    url(r'^$', views.form, name = 'form'),
    url(r'^complete/', views.complete, name = 'complete'),
]
```

```file_uploader/urls.py
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include('upload_form.urls', namespace = 'upload_form')),
]
```

## Django管理サイト設定

### admin.py

```python:upload_form/admin.py
from django.contrib import admin
from upload_form.models import FileNameModel

class FileNameAdmin(admin.ModelAdmin):
    list_display = ('id', 'file_name', 'upload_time')
    list_display_links = ('id', 'file_name')

admin.site.register(FileNameModel, FileNameAdmin)
```

Django管理サイトからデータを閲覧・編集できるようにする

## マイグレーション & 起動

```bash
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver
```

http://127.0.0.1:8000
にアクセスしたらクソショボいWebページが表示されると思われる。
そこから適当にアップロードしてあげると上手くいく。

![スクリーンショット 2016-08-18 午後3.42.39.png](https://qiita-image-store.s3.amazonaws.com/0/97280/75cf25ad-84dc-4e40-1c4a-388351163e4f.png "スクリーンショット 2016-08-18 午後3.42.39.png")

http://127.0.0.1:8000/admin/
にアクセスしてログインして
http://127.0.0.1:8000/admin/upload_form/filenamemodel/
に入ったらデータがあることを確認する！！

![スクリーンショット 2016-08-18 午後3.43.48.png](https://qiita-image-store.s3.amazonaws.com/0/97280/6e8b978a-3909-3b06-7496-c8d7ddeca5e6.png "スクリーンショット 2016-08-18 午後3.43.48.png")

アップロードしたファイルが何故かiOSのCertificateやらp12ファイルばかりで申し訳ないかぎりだが確認ができた！

あとは焼くなり煮るなり。

私は、去年の卒研でCSVファイルをアップロードすると中身を読み取ってDBテーブルにインサートするような処理をしました。

## リポジトリ

https://github.com/nnsnodnb/django-file-uploader

一応リポジトリリンク