Переопределение метода "save()" ModelForm
Как вы знаете, ModelForm предоставляет метод save() для сохранения текущего экземпляра модели в базе данных и возврата объекта. Этот метод получает логический boolean commit параметр, который позволяет указать, должен ли объект сохраняться в базе данных. Если commit имеет значение False, метод save() вернет экземпляр модели, но не сохранит его в базе данных. Мы собираемся переопределить метод save() нашей формы, чтобы извлечь заданное изображение и сохранить его.
Добавьте следующие импортируемые компоненты в верхнюю часть файла forms.py:
from urllib import request
from django.core.files.base import ContentFile
from django.utils.text import slugify
Затем добавьте метод save() в ImageCreateForm:
def save(self, force_insert=False, force_update=False, commit=True):
image = super(ImageCreateForm, self).save(commit=False)
image_url = self.cleaned_data['url']
image_name = '{}.{}'.format(slugify(image.title), image_url.rsplit('.', 1)[1].lower())
# download image from the given URL
response = request.urlopen(image_url)
image.image.save(image_name, ContentFile(response.read()), save=False)
if commit:
image.save()
return image
Мы переопределяем метод save(), сохраняя параметры, необходимые для ModelForm. Этот код выглядит следующим образом:
Мы создаем новый экземпляр image, вызвав метод save() формы с commit=False.
Мы получим URL-адрес из cleaned_data словаря формы.
Имя изображения создается путем объединения служебного заголовка изображения с исходным расширением файла.
Мы используем модуль Python urllib для загрузки изображения, а затем вызываем метод save() поля image, передав ему объект ContentFile, который создается с помощью загружаемого содержимого файла. Таким образом мы сохраняем файл в каталог мультимедиа нашего проекта. Мы также перейдем к параметру save=False, чтобы избежать сохранения объекта в базе данных.
Чтобы сохранить то же поведение, что и метод save(), который мы переопределяем, мы сохраняем форму в базу данных только в том случае, если параметр commit True.
Теперь нам нужен view для обработки формы. Отредактируйте файл views.py приложения images и добавьте в него следующий код:
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .forms import ImageCreateForm
@login_required
def image_create(request):
if request.method == 'POST':
# form is sent
form = ImageCreateForm(data=request.POST)
if form.is_valid():
# form data is valid
cd = form.cleaned_data
new_item = form.save(commit=False)
# assign current user to the item
new_item.user = request.user
new_item.save()
messages.success(request, 'Image added successfully')
# redirect to new created item detail view
return redirect(new_item.get_absolute_url())
else:
# build form with data provided by the bookmarklet via GET
form = ImageCreateForm(data=request.GET)
return render(request, 'images/image/create.html', {'section': 'images', 'form': form})
Мы добавляем декоратор login_required в представление image_create, чтобы запретить доступ для неавторизованых пользователей. Вот как это представление работает:
- Мы ожидаем, что исходные данные с помощью GET будут созданы в порядке создания экземпляра формы. Эти данные будут состоять из атрибутов url и title изображения с внешнего веб-сайта и будут предоставлены через GET с помощью средств JavaScript, которое будет создано позже
- Если форма отправлена, мы проверяем ее валидность. Если форма валидна, мы создаем новый экземпляр Image, но мы не можем сохранить объект в базе данных, передав commit=False
- Мы присваиваем текущему пользователю новый объект image. Таким образом мы знаем, какой именно пользователь загрузил изображение
- Мы сохраняем объект изображения в базу данных
- Наконец, мы создаем сообщение об успехе с помощью системы «Джанго» и перенаправляем пользователя к URL-адресу нового изображения. Мы еще не реализовали метод get_absolute_url() модели Image, мы сделаем это позже.
Создайте новый файл urls.py в приложении images и добавьте в него следующий код:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^create/$', views.image_create, name='create'),
]
Измените основной файл urls.py проекта, включив в него шаблоны, которые были только что созданы для приложения images:
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^account/', include('account.urls')),
url(r'^images/', include('images.urls', namespace='images')),
]
Наконец, необходимо создать шаблон для визуализации формы. Создайте следующую структуру каталогов в каталоге приложения images:
templates/
images/
image/
create.html
Отредактируйте новый шаблон create.html и добавьте в него следующий код:
{% extends "base.html" %}
{% block title %}Bookmark an image{% endblock %}
{% block content %}
<h1>Bookmark an image</h1>
<img src="{{ request.GET.url }}" class="image-preview">
<form action="." method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Bookmark it!">
</form>
{% endblock %}
Теперь откройте в браузере http://127.0.0.1:8000/images/create/?title=%20Django%20and%20Duke&url=http://upload.wikimedia.org/wikipedia/commons/8/85/Django\_Reinhardt\_and\_Duke\_Ellington\_%28Gottlieb%29.jpg .
Форма отобразится с предварительным просмотром изображения, как в следующем примере:
Добавьте описание и нажмите кнопку Bookmark it!. Новый объект Image будет сохранен в базе данных. Появится сообщение об ошибке, показывающее, что модель изображения не имеет get_absolute_url(). Не волнуйтесь об этом сейчас, мы собираемся добавить этот метод позже. Откройте в браузере http://127.0.0.1:8000/admin/images/image/ и убедитесь, что новый объект изображения сохранен.