django 文件上传的默认机制以及 filefield and imagefield and storage存储
第一篇:关于django默认的 fileField上传机制
Uploading Files to Django
Let’s get right down to what we need to allow file uploads in Django.
1. Prerequisite Knowledge
In the last article on Django Forms, we have seen that to get the form data; we use request.POST in the Form object.
Request POSTBut to upload files to Django, we need to include another attribute request.FILES as well because the uploaded files are stored in the attribute request.FILES instead of request.POST.
Here’s what the code will look like:
form = ReviewForm(request.POST,request.FILES) |
Django has separate model fields to handle the different file types – ImageField and FileField.
We use the ImageField when we want to upload only image files(.jpg/.jpeg/.png etc.)
To allow file uploads we need to add the following attribute in the <form> attribute.
enctype ="multipart/form-data" |
At the end, the form HTML tag should look like this:
<form type = 'post' enctype = "multipart/form-data"> |
2. Modify settings.py to store uploaded files
Now in the settings.py add the following lines at the end of the file.
MEDIA_URL = ‘/media/’MEDIA_ROOT = os.path.join(BASE_DIR, ‘media’) |
Here:
- MEDIA_URL: This mentions the URL endpoint. This is the URL the user can go to and upload their files from the browser
- MEDIA_ROOT: This we have seen earlier in the Django Templates article under the DIR settings for Templates.
If you don’t understand it right now, you will understand it later in the article!
The second line tells Django to store all the uploaded files in a folder called ’media’ created in the BASE_DIR, i.e., the project Directory.
We need to create the folder manually so all the uploaded files will be stored in the media folder underlined below:
Django Project Directory3. Creating the Media Folder in the Django Project.
Now in the project folder, create a new folder with the name ‘media.’
MediaOnce the folder is created, we will move to creating the eBook upload webpage.
Creating an E-Book upload webpage
Now let us make a webpage, in which the clients can upload the pdf file of books they have.
1. Creating an E-Book Model in models.py
In models.py, create a new Django Model “EBooksModel” and then add the following code
class EBooksModel(models.Model): title = models.CharField(max_length = 80) pdf = models.FileField(upload_to='pdfs/') class Meta: ordering = ['title'] def __str__(self): return f"{self.title}" |
Here:
- We have used the well-known model CharField, which will store the name of the pdf that the client submits.
- FileField is used for files that the client will upload.
- Upload_to option specifies the path where the file is going to be stored inside the media. For, e.g., I have used ‘pdfs/,’ which implies that the files will get stored in a folder named pdfs inside the media.
- Class Meta and def__str__: we have learned this in Django models article
Note: The upload File won’t be saved in the database. Only the instance of the file will be saved there. Hence even if you delete that particular instance, the Uploaded file will still be inside the media folder.
You will know what I meant by an instance of a file is later in this article, so hold on !!
2. Creating the UploadBookForm in forms.py
We will now import the EBooksModel into forms.py and then create a new ModelForm “UploadBookForm.”
Create the Form using the knowledge we learned in Django Forms
class UploadBookForm(forms.ModelForm): class Meta: model = EBooksModel fields = ('title', 'pdf',) |
3. Creating BookUploadView in views.py
The code here will be similar to the one we wrote in Django Forms. But here, we need to accommodate the uploaded files (placed in request.FILES instead of request.POST.)
For that, simply add request.FILES, along with the request.POST as shown below
form = UploadBookForm(request.POST,request.FILES) |
Therefore the full code will be
def BookUploadView(request): if request.method == 'POST': form = UploadBookForm(request.POST,request.FILES) if form.is_valid(): form.save() return HttpResponse('The file is saved') else: form = UploadBookForm() context = { 'form':form, } return render(request, 'books_website/UploadBook.html', context) |
4. Creating the UploadBook.html Template
Now we need to create the <form> attribute in the template file.
Hence, create a template file “ UploadBook.html.” and add the following.
<form method ='post' enctype ="multipart/form-data"> {% csrf_token %} {{form}} <input type="submit" value = "Submit"></form> |
Don’t forget to add enctype =”multipart/form-data” otherwise, the form won’t work.
Now finally let’s map the View with a URL(book/upload)
5. Creating a URL path for UploadBookView
Now in the urls.py, add the path to link UploadBookView to ‘book/upload.’ using the method we saw in Django URL mapping.
path('book/upload', BookUploadView, name ='BookUploadView') |
Now that we have created a new model, we must perform the migrations again. So in the python shell enter the following command one by one.
python manage.py makemigrationspython manage.py migrate |
That’s it, Now lets run the server and check the browser.
BrowserVoila, the upload form is up !! Now choose a pdf and click the submit button.
BrowserWhen you hit the submit button, then “the file has been saved” page will appear
BrowserIf you go to the media folder, you will see a pdfs folder and in it the pdf that you submitted.
Media/pdfsRegister the newly made model in the admin site, using:
admin.site.register(EBooksModel) |
Then load the admin site in the browser and go to EBooksModel and select the element we just submitted.
Admin SiteNow here, if you observe, in the pdf field. You will see a Currently: option.
The path that is written in front of it: pdfs/cprogramming_tutorial.pdf is called an instance. Therefore pdfs/<pdf_name> is an instance of the <pdf_name> file.
Django saves only the instance of the file and not the file itself. Hence even if you delete the model from the admin site, the pdf file will still be there in the media folder.
View the uploaded files from the browser front-end
In the above webpage, the instance appears as a link. But if you click on it, you will get an error message.
This happens because the endpoint is not mapped.
Error Message
Now to correct this error, we need to map this endpoint to the particular file. To do that, go to urls.py and add
from django.conf import settingsfrom django.conf.urls.static import staticif settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |
UrlsIf you read the line, you will get a rough idea of what we are doing
Here:
- In settings.py, we have already set debug = True, so settings.DEBUG will always be true.
- In side if function, the following code will add static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) to the urlpatterns present above.
Debug 1The line static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) can be thought of in this way.
To the host website(http://127.0.0.1:8000/) is where we are adding the endpoints –
- MEDIA_URL(which we kept as ‘/media/’ in the starting of this article)
Media Settings- and then document_root(which is the location of the pdf file inside the media folder.
Hence if I want to view the cprogramming_tutorial.pdf file that I earlier uploaded, I will go to http://127.0.0.1:8000/media/pdfs/cprogramming_tutorial.pdf (observe how MEDIA_URL(‘/media/’) is being used)
This is what the above code in urls.py does.
That’s it, now if you reload the server and click on the instance we saw earlier on the Django admin page, you won’t get the error now.
Admin SiteNow click on the instance link and check !!
另外一篇关于fileFeild 和imageField的区别
FileField
class FileField(upload_to=None, max_length=100, **options)
一个文件上传字段。
请注意
primary_key参数不受支持,如果使用它将引发一个错误。
有两个可选参数:
FileField.upload_to
这个属性提供了一种设置上传目录和文件名的方法,可以通过两种方式进行设置。在这两种情况下,值都被传递给Storage.save()方法。
字符串值
如果指定一个字符串值,它可能包含strftime()格式,该格式将被文件上传的日期/时间所取代(这样上传的文件就不会填满给定的目录)。例如:
class MyModel(models.Model): # file will be uploaded to MEDIA_ROOT/uploads upload = models.FileField(upload_to='uploads/') # or... # file will be saved to MEDIA_ROOT/uploads/2015/01/30 upload = models.FileField(upload_to='uploads/%Y/%m/%d/')
如果您使用的是默认的FileSystemStorage,则该字符串值将被附加到MEDIA_ROOT路径中,以在本地文件系统上形成存储上传文件的位置。如果您使用的是不同的存储,请检查该存储的文档,看看它是如何处理upload_to的。
函数Upload_to也可以是一个可调用对象,比如一个函数。这将被调用以获得上载路径,包括文件名。这个可调用对象必须接受两个参数并返回一个unix风格的路径(带有斜杠),该路径将被传递给存储系统。
def user_directory_path(instance, filename): # file will be uploaded to MEDIA_ROOT/user_<id>/<filename> return 'user_{0}/{1}'.format(instance.user.id, filename) class MyModel(models.Model): upload = models.FileField(upload_to=user_directory_path)
FileField.storage
一个存储对象,用来处理文件的存储和检索。
ImageField
class ImageField(upload_to=None, height_field=None, width_field=None, max_length=100, **options)
从FileField继承所有属性和方法,但也验证上传的对象是一个有效的图像。
除了FileField可用的特殊属性外,ImageField也有高度和宽度属性。
为了方便查询这些属性,ImageField有两个额外的可选参数:
ImageField.height_field
模型字段的名称,它将在每次保存模型实例时自动填充图像的高度。
ImageField.width_field
模型字段的名称,它将在每次保存模型实例时自动填充图像的宽度。
ImageField 依赖Pillow模块。这是与fileField的区别
mageField实例在数据库中创建为varchar列,默认最大长度为100个字符。与其他字段一样,可以使用max_length参数更改最大长度。
该字段的默认表单小部件是ClearableFileInput。
示例
# models.py from django.db import models class Car(models.Model): name = models.CharField(max_length=255) price = models.DecimalField(max_digits=5, decimal_places=2) photo = models.ImageField(upload_to='cars') json_file = models.FileField(upload_to='cars')
>>> car = Car.objects.get(name="Chevy") >>> car.photo <ImageFieldFile: chevy.jpg> >>> car.photo.name 'cars/chevy.jpg' >>> car.photo.path '/media/cars/chevy.jpg' >>> car.photo.url 'http://media.example.com/cars/chevy.jpg'
删除文件
File.delete(save=True/False)
从模型实例中删除该文件对象并删除文件。如果save为True,modle instance被删除,那么物理file也被删除
>>> car = Car.objects.get(name="Chevy") >>> car.photo.delete(save=True)
第三篇: simple is better than complex讲解的simple file upload 和 modelForm file upload
Simple File Upload
Following is a minimal file upload example using FileSystemStorage. Use it just to learn about the flow of the process.
simple_upload.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="myfile">
<button type="submit">Upload</button>
</form>
{% if uploaded_file_url %}
<p>File uploaded at: <a href="{{ uploaded_file_url }}">{{ uploaded_file_url }}</a></p>
{% endif %}
<p><a href="{% url 'home' %}">Return to home</a></p>
{% endblock %}
views.py
from django.shortcuts import render
from django.conf import settings
from django.core.files.storage import FileSystemStorage
def simple_upload(request):
if request.method == 'POST' and request.FILES['myfile']:
myfile = request.FILES['myfile']
fs = FileSystemStorage()
filename = fs.save(myfile.name, myfile)
uploaded_file_url = fs.url(filename)
return render(request, 'core/simple_upload.html', {
'uploaded_file_url': uploaded_file_url
})
return render(request, 'core/simple_upload.html')
File Upload With Model Forms
Now, this is a way more convenient way. Model forms perform validation, automatically builds the absolute path for the upload, treats filename conflicts and other common tasks.
models.py
from django.db import models
class Document(models.Model):
description = models.CharField(max_length=255, blank=True)
document = models.FileField(upload_to='documents/')
uploaded_at = models.DateTimeField(auto_now_add=True)
forms.py
from django import forms
from uploads.core.models import Document
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ('description', 'document', )
views.py
def model_form_upload(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('home')
else:
form = DocumentForm()
return render(request, 'core/model_form_upload.html', {
'form': form
})
model_form_upload.html
{% extends 'base.html' %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
<p><a href="{% url 'home' %}">Return to home</a></p>
{% endblock %}
About the FileField upload_to Parameter
See the example below:
document = models.FileField(upload_to='documents/')
Note the upload_to parameter. The files will be automatically uploaded to MEDIA_ROOT/documents/.
It is also possible to do something like:
document = models.FileField(upload_to='documents/%Y/%m/%d/')
A file uploaded today would be uploaded to MEDIA_ROOT/documents/2016/08/01/.
The upload_to can also be a callable that returns a string. This callable accepts two parameters, instance and filename.
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'user_{0}/{1}'.format(instance.user.id, filename)
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path)
Download the Examples
The code used in this post is available on Github.