Як завантажити файл у Django? [зачинено]


668

Як новачок у Django, у мене виникають труднощі зробити додаток для завантаження в Django 1.3. Не вдалося знайти жодного актуального прикладу / фрагментів. Може хтось опублікує мінімальний, але повний приклад (модель, перегляд, шаблон) для цього?

Відповіді:


1273

Phew, Django документація насправді не є хорошим прикладом цього. Я витратив понад 2 години, щоб викопати всі шматочки, щоб зрозуміти, як це працює. З цим знанням я реалізував проект, який дозволяє завантажувати файли та показувати їх як список. Щоб завантажити джерело проекту, відвідайте https://github.com/axelpale/minimal-django-file-upload-example або клонуйте його:

> git clone https://github.com/axelpale/minimal-django-file-upload-example.git

Оновлення 2013-01-30: Джерело в GitHub також має реалізацію для Django 1.4 на додаток до 1.3. Незважаючи на те, що змін мало, наступний підручник також корисний для 1.4.

Оновлення 2013-05-10: реалізація для Django 1.5 на GitHub. Незначні зміни перенаправлення в urls.py та використання тегу шаблону URL в list.html. Завдяки hubert3 за докладені зусилля.

Оновлення 2013-12-07: Django 1.6 підтримується на GitHub. Один імпорт змінився в myapp / urls.py. Дякуємо Артедіану .

Оновлення 2015-03-17: Django 1.7 підтримується в GitHub, завдяки aronysidoro .

Оновлення 2015-09-04: Django 1.8 підтримується в GitHub, завдяки nerogit .

Оновлення 2016-07-03: Django 1.9 підтримується в GitHub, завдяки daavve та nerogit

Дерево проекту

Базовий проект Django 1.3 з одним додатком та медіа / каталогом для завантажень.

minimal-django-file-upload-example/
    src/
        myproject/
            database/
                sqlite.db
            media/
            myapp/
                templates/
                    myapp/
                        list.html
                forms.py
                models.py
                urls.py
                views.py
            __init__.py
            manage.py
            settings.py
            urls.py

1. Налаштування: myproject / settings.py

Щоб завантажувати та обслуговувати файли, потрібно вказати, де Django зберігає завантажені файли та за якою URL-адресою Django їх обслуговує. MEDIA_ROOT та MEDIA_URL за замовчуванням знаходяться у settings.py, але вони порожні. Докладніше див. Перші рядки в Django Managing Files . Не забудьте також встановити базу даних і додати myapp до INSTALLED_APPS

...
import os

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'database.sqlite3'),
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}
...
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
...
INSTALLED_APPS = (
    ...
    'myapp',
)

2. Модель: myproject / myapp / models.py

Далі вам потрібна модель з FileField. Це конкретне поле зберігає файли, наприклад, у засобах масової інформації / документах / 2011/12/24 / на основі поточної дати та MEDIA_ROOT. Див. Посилання на FileField .

# -*- coding: utf-8 -*-
from django.db import models

class Document(models.Model):
    docfile = models.FileField(upload_to='documents/%Y/%m/%d')

3. Форма: myproject / myapp / form.py

Щоб красиво здійснити завантаження, вам потрібна форма. Ця форма містить лише одне поле, але цього достатньо. Докладніше див. Посилання на FileField форми .

# -*- coding: utf-8 -*-
from django import forms

class DocumentForm(forms.Form):
    docfile = forms.FileField(
        label='Select a file',
        help_text='max. 42 megabytes'
    )

4. Вид: мійпроект / myapp / views.py

Погляд, де відбувається вся магія. Зверніть увагу, як request.FILESповодитися. Для мене було дуже важко помітити той факт, який request.FILES['docfile']можна зберегти в моделях.FileField просто так. Збереження () моделі обробляє збереження файлу у файловій системі автоматично.

# -*- coding: utf-8 -*-
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse

from myproject.myapp.models import Document
from myproject.myapp.forms import DocumentForm

def list(request):
    # Handle file upload
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            newdoc = Document(docfile = request.FILES['docfile'])
            newdoc.save()

            # Redirect to the document list after POST
            return HttpResponseRedirect(reverse('myapp.views.list'))
    else:
        form = DocumentForm() # A empty, unbound form

    # Load documents for the list page
    documents = Document.objects.all()

    # Render list page with the documents and the form
    return render_to_response(
        'myapp/list.html',
        {'documents': documents, 'form': form},
        context_instance=RequestContext(request)
    )

5. URL-адреси проекту: myproject / urls.py

Django за замовчуванням не обслуговує MEDIA_ROOT. Це було б небезпечно у виробничих умовах. Але на стадії розробки ми могли б скоротити. Зверніть увагу на останній рядок. Цей рядок дозволяє Django обслуговувати файли з MEDIA_URL. Це працює лише на стадії розробки.

Докладніше див. Посилання на django.conf.urls.static.static . Дивіться також цю дискусію про розміщення медіафайлів .

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = patterns('',
    (r'^', include('myapp.urls')),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

6. URL-адреси додатків: myproject / myapp / urls.py

Щоб зробити перегляд доступним, потрібно вказати для нього URL-адреси. Тут нічого особливого.

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, url

urlpatterns = patterns('myapp.views',
    url(r'^list/$', 'list', name='list'),
)

7. Шаблон: myproject / myapp / templates / myapp / list.html

Остання частина: шаблон для списку та форма для завантаження під ним. Форма повинна мати атрибут enctype-атрибут "multipart / form-data" та метод "post", щоб зробити завантаження на Django можливим. Докладніше див. Документацію щодо завантаження файлів .

У FileField є багато атрибутів, які можна використовувати в шаблонах. Наприклад, {{document.docfile.url}} та {{document.docfile.name}}, як у шаблоні. Детальніше про них див. У статті Використання файлів у моделях та Документація об'єкта File .

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Minimal Django File Upload Example</title>   
    </head>
    <body>
    <!-- List of uploaded documents -->
    {% if documents %}
        <ul>
        {% for document in documents %}
            <li><a href="{{ document.docfile.url }}">{{ document.docfile.name }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>No documents.</p>
    {% endif %}

        <!-- Upload form. Note enctype attribute! -->
        <form action="{% url 'list' %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <p>{{ form.non_field_errors }}</p>
            <p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
            <p>
                {{ form.docfile.errors }}
                {{ form.docfile }}
            </p>
            <p><input type="submit" value="Upload" /></p>
        </form>
    </body>
</html> 

8. Ініціалізуйте

Просто запустіть syncdb та runserver.

> cd myproject
> python manage.py syncdb
> python manage.py runserver

Результати

Нарешті, все готово. За умовчанням середовище розробки Django список завантажених документів можна побачити на localhost:8000/list/. Сьогодні файли завантажуються в / шлях / до / мій проект / медіа / документи / 2011/12/17 / і можуть бути відкриті зі списку.

Я сподіваюся, що ця відповідь комусь допоможе настільки, наскільки це допомогло б мені.


9
Знайдено місце в документах django, де відображаються завантаження файлів. Приклад у цій відповіді чудовий, але інформація у документах про django буде оновлена ​​новими випусками. docs.djangoproject.com/en/dev/topics/http/file-uploads
TaiwanGrapefruitTea

1
Приклад не працює для Джанго "1,5". У HTML {% url list %}стає {% url "list" %}.
Матьє Ріглер

4
Дуже дякую . Це справді працює для мене. Однак для майбутніх глядачів слід перевірити код у gitHub на найкращу сумісність із новими версіями Python та Django. Наприклад, views.py, render_to_response () слід замінити рендерінгом (запит, ...,), щоб уникнути помилки CSRF. Ура.
Хуй, ніж

1
чи можна це робити без ФОРМ?
Roel

1
Чи може файл .zip або інші стислі файли?
qg_java_17137

75

Взагалі кажучи, коли ви намагаєтеся "просто отримати робочий приклад", найкраще "просто почати писати код". Тут немає коду, який би вам допоміг, тому для відповіді на це питання нам набагато більше працювати.

Якщо ви хочете схопити файл, вам потрібно щось подібне у файлі html десь:

<form method="post" enctype="multipart/form-data">
    <input type="file" name="myfile" />
    <input type="submit" name="submit" value="Upload" />
</form>

Це дасть вам кнопку перегляду, кнопку завантаження, щоб розпочати дію (надішліть форму) та відзначте ентетип, щоб Джанго знав, щоб дати вам request.FILES

У вікні десь ви можете отримати доступ до файлу за допомогою

def myview(request):
    request.FILES['myfile'] # this is my file

У документах про завантаження файлів є величезна кількість інформації

Я рекомендую вам уважно прочитати сторінку та просто почати писати код - потім повернутися із прикладами та скласти сліди, коли вона не працює.


10
Дякую Генрі. Насправді я читав документи і писав якийсь код, але оскільки у документів є деякі прогалини (наприклад, "звідкись імпорт handle_uploaded_file"), і мій код був помилковим, подумав, що було б набагато краще, якби я міг почати з робочого прикладу .
qliq

26
Погодьтеся з qliq. Простий робочий приклад - це найефективніший спосіб залучити новачків, а не документи
Philip007

11
enctype="multipart/form-data", Що мені потрібно , щоб зробити цю роботу, спасибі!
john-charles

5
Просто не пропустіть {% csrf_token%} у тегах форми.
jonincanada

чи можна це робити БЕЗ ФОРМ З ІНФОРМАЦІЙ.
Roel

71

Демо

Дивіться github repo , працює з Django 3

Мінімальний приклад завантаження файлу Django

1. Створіть проект джанго

Запустити стартовий проект ::

$ django-admin.py startproject sample

тепер створена папка ( зразок ).

2. створити додаток

Створіть додаток ::

$ cd sample
$ python manage.py startapp uploader

Тепер створена папка ( uploader) з цими файлами:

uploader/
  __init__.py
  admin.py
  app.py
  models.py
  tests.py
  views.py
  migrations/
    __init__.py

3. Оновіть settings.py

На sample/settings.pyоних 'uploader'до INSTALLED_APPSі додати MEDIA_ROOTі MEDIA_URL, тобто ::

INSTALLED_APPS = [
    'uploader',
    ...<other apps>...      
]

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

4. Оновіть urls.py

в sample/urls.pyоних ::

...<other imports>...
from django.conf import settings
from django.conf.urls.static import static
from uploader import views as uploader_views

urlpatterns = [
    ...<other url patterns>...
    path('', uploader_views.UploadView.as_view(), name='fileupload'),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

5. Оновіть models.py

оновлення uploader/models.py::

from django.db import models
class Upload(models.Model):
    upload_file = models.FileField()    
    upload_date = models.DateTimeField(auto_now_add =True)

6. Оновіть views.py

оновлення uploader/views.py::

from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .models import Upload
class UploadView(CreateView):
    model = Upload
    fields = ['upload_file', ]
    success_url = reverse_lazy('fileupload')
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['documents'] = Upload.objects.all()
        return context

7. створюйте шаблони

Створіть зразок папки / завантажувача / шаблонів / завантажувача

Створіть файл upload_form.html, тобто sample/uploader/templates/uploader/upload_form.html::

<div style="padding:40px;margin:40px;border:1px solid #ccc">
    <h1>Django File Upload</h1>
    <form method="post" enctype="multipart/form-data">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit">Submit</button>
    </form><hr>
    <ul>
    {% for document in documents %}
        <li>
            <a href="{{ document.upload_file.url }}">{{ document.upload_file.name }}</a>
            <small>({{ document.upload_file.size|filesizeformat }}) - {{document.upload_date}}</small>
        </li>
    {% endfor %}
    </ul>
</div>

8. Синхронізувати базу даних

Синхронізувати базу даних та пробіг ::

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

відвідайте http: // localhost: 8000 /


2
ідеально, за винятком останнього рядка - має бути localhost.com:8000/upload > Це працювало для django 1.6 та Python 3.3.
Стів

5
+1 для схеми дизайну додатків для багаторазового використання
Марсель

1
Акселі деякий FileFieldчас використовував верхівку ImageField, чи могла б хто-небудь пояснити вибір?
двтан

@dtgq Я оновив відповідь, яку потрібно використовувати FileField. ImageFieldПотрібно лише завантажувати зображення. оновлення працюватиме з Django 1.11.
suhailvs

тестував на Django 2.0 і працював чудово
diek

29

Треба сказати, що я вважаю документацію на джанго заплутаною. Також для найпростішого прикладу, чому згадуються форми? Приклад, з яким я працював у Views.py:

for key, file in request.FILES.items():
    path = file.name
    dest = open(path, 'w')
    if file.multiple_chunks:
        for c in file.chunks():
            dest.write(c)
    else:
        dest.write(file.read())
    dest.close()

HTML-файл схожий на код нижче, хоча цей приклад завантажує лише один файл, а код для збереження файлів обробляє багато: -

<form action="/upload_file/" method="post" enctype="multipart/form-data">{% csrf_token %}
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>

Ці приклади не є моїм кодом, вони отримані з двох інших прикладів, які я знайшов. Я відносно початківця джанго, тому дуже ймовірно, що я пропускаю якийсь ключовий момент.


3
+1 за те, що не використовується a FileFieldі a model.Form. Для початківців (і для тривіальних завдань) ручна обробка завантажених файлів, як показано вище, менш заплутана.
AneesAhmed777

dest = open (шлях, 'wb'), коли файл
пишеться

20

У мене також була схожа вимога. Більшість прикладів у мережі пропонують створити моделі та створити форми, які я не хотів використовувати. Ось мій підсумковий код.

if request.method == 'POST':
    file1 = request.FILES['file']
    contentOfFile = file1.read()
    if file1:
        return render(request, 'blogapp/Statistics.html', {'file': file1, 'contentOfFile': contentOfFile})

І в HTML для завантаження я написав:

{% block content %}
    <h1>File content</h1>
    <form action="{% url 'blogapp:uploadComplete'%}" method="post" enctype="multipart/form-data">
         {% csrf_token %}
        <input id="uploadbutton" type="file" value="Browse" name="file" accept="text/csv" />
        <input type="submit" value="Upload" />
    </form>
    {% endblock %}

Далі йде HTML, який відображає вміст файлу:

{% block content %}
    <h3>File uploaded successfully</h3>
    {{file.name}}
    </br>content = {{contentOfFile}}
{% endblock %}

добре тому, що іноді просто хочеться використовувати вміст файлу, не зберігаючи завантаження ...
nemesisfixx

17

Розширення на прикладі Генрі :

import tempfile
import shutil

FILE_UPLOAD_DIR = '/home/imran/uploads'

def handle_uploaded_file(source):
    fd, filepath = tempfile.mkstemp(prefix=source.name, dir=FILE_UPLOAD_DIR)
    with open(filepath, 'wb') as dest:
        shutil.copyfileobj(source, dest)
    return filepath

Ви можете викликати цю handle_uploaded_fileфункцію зі свого подання із завантаженим файловим об’єктом. Це дозволить зберегти файл з унікальним іменем (з префіксом імені оригіналу завантаженого файлу) у файловій системі та повернути повний шлях збереженого файлу. Ви можете зберегти шлях у базі даних і пізніше зробити щось із файлом.


Імран, я спробував ваш код на моєму огляді, але отримав цю помилку: "WSGIRequest" об'єкт не має атрибута "name".
qliq

2
Передайте завантажений файл ( request.FILES['myfile']) handle_uploaded_file, а не requestсам.
Імран

Чи можу я зберегти його безпосередньо в базі даних? stackoverflow.com/questions/24705246/…
AlexandruC

Використовуючи prefix=source.nameйого, додали зайві символи в кінці файлу, псуючи розширення файлу. Наприклад, upload.csvзмінили на upload.csv5334. Змінивши його, щоб suffix=source.nameвиправити це для мене.
Тахрем Ікбал

13

Тут це може допомогти вам: створити поле файлу у вашому models.py

Щоб завантажити файл (у своєму admin.py):

def save_model(self, request, obj, form, change):
    url = "http://img.youtube.com/vi/%s/hqdefault.jpg" %(obj.video)
    url = str(url)

    if url:
        temp_img = NamedTemporaryFile(delete=True)
        temp_img.write(urllib2.urlopen(url).read())
        temp_img.flush()
        filename_img = urlparse(url).path.split('/')[-1]
        obj.image.save(filename_img,File(temp_img)

і використовувати це поле також у вашому шаблоні.


1
Це корисно, коли вам доведеться вручну загартовувати файли, які ви хочете зберегти. Якщо це так, можливо, вам також знадобиться цей розділ: docs.djangoproject.com/en/dev/topics/files/#the-file-object
kecske

11

Ви можете посилатися на приклади серверів у програмі Fine Uploader, яка має версію django. https://github.com/FineUploader/server-examples/tree/master/python/django-fine-uploader

Це дуже елегантно і найважливіше з усіх, він пропонує найголовніші js lib. Шаблон не включений у приклади сервера, але ви можете знайти демонстрацію на його веб-сайті. Тонкий завантажувач: http://fineuploader.com/demos.html

django-fine-uploader

views.py

UploadView відправляє повідомлення та видаляє запит до відповідних обробників.

class UploadView(View):

    @csrf_exempt
    def dispatch(self, *args, **kwargs):
        return super(UploadView, self).dispatch(*args, **kwargs)

    def post(self, request, *args, **kwargs):
        """A POST request. Validate the form and then handle the upload
        based ont the POSTed data. Does not handle extra parameters yet.
        """
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_upload(request.FILES['qqfile'], form.cleaned_data)
            return make_response(content=json.dumps({ 'success': True }))
        else:
            return make_response(status=400,
                content=json.dumps({
                    'success': False,
                    'error': '%s' % repr(form.errors)
                }))

    def delete(self, request, *args, **kwargs):
        """A DELETE request. If found, deletes a file with the corresponding
        UUID from the server's filesystem.
        """
        qquuid = kwargs.get('qquuid', '')
        if qquuid:
            try:
                handle_deleted_file(qquuid)
                return make_response(content=json.dumps({ 'success': True }))
            except Exception, e:
                return make_response(status=400,
                    content=json.dumps({
                        'success': False,
                        'error': '%s' % repr(e)
                    }))
        return make_response(status=404,
            content=json.dumps({
                'success': False,
                'error': 'File not present'
            }))

form.py

class UploadFileForm(forms.Form):

    """ This form represents a basic request from Fine Uploader.
    The required fields will **always** be sent, the other fields are optional
    based on your setup.
    Edit this if you want to add custom parameters in the body of the POST
    request.
    """
    qqfile = forms.FileField()
    qquuid = forms.CharField()
    qqfilename = forms.CharField()
    qqpartindex = forms.IntegerField(required=False)
    qqchunksize = forms.IntegerField(required=False)
    qqpartbyteoffset = forms.IntegerField(required=False)
    qqtotalfilesize = forms.IntegerField(required=False)
    qqtotalparts = forms.IntegerField(required=False)

7

Не впевнений, чи є у цього підходу якісь недоліки, але ще більш мінімальні, у Views.py:

entry = form.save()

# save uploaded file
if request.FILES['myfile']:
    entry.myfile.save(request.FILES['myfile']._name, request.FILES['myfile'], True)

0

Я зіткнувся з подібною проблемою і вирішив сайт адміністратора django.

# models
class Document(models.Model):
    docfile = models.FileField(upload_to='documents/Temp/%Y/%m/%d')

    def doc_name(self):
        return self.docfile.name.split('/')[-1] # only the name, not full path

# admin
from myapp.models import Document
class DocumentAdmin(admin.ModelAdmin):
    list_display = ('doc_name',)
admin.site.register(Document, DocumentAdmin)

[введіть тут опис посилання] [1] [введіть тут опис посилання] [2] [1]: youtu.be/0tNZB3dyopY [2]: youtu.be/klhMYMc3PlY
uda123
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.