How to do it...

Execute these steps to complete the recipe:

  1. Modify the Idea model to add a picture field and image version specifications:
# myproject/apps/ideas/models.py
import contextlib

import os

from imagekit.models import ImageSpecField
from pilkit.processors import ResizeToFill

from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils.timezone import now as timezone_now

from myproject.apps.core.models import (CreationModificationDateBase, UrlBase)


def upload_to(instance, filename):
now = timezone_now()
base, extension = os.path.splitext(filename)
extension = extension.lower()
return f"ideas/{now:%Y/%m}/{instance.pk}{extension}"


class Idea(CreationModificationDateBase, UrlBase):
# attributes and fields…
picture = models.ImageField(
_("Picture"), upload_to=upload_to
)
picture_social = ImageSpecField(
source="picture",
processors=[ResizeToFill(1024, 512)],
format="JPEG",
options={"quality": 100},
)
picture_large = ImageSpecField(
source="picture",
processors=[ResizeToFill(800, 400)],
format="PNG"
)
picture_thumbnail = ImageSpecField(
source="picture",
processors=[ResizeToFill(728, 250)],
format="PNG"
)
# other fields, properties, and methods…

def delete(self, *args, **kwargs):
from django.core.files.storage import default_storage
if self.picture:
with contextlib.suppress(FileNotFoundError):
default_storage.delete(
self.picture_social.path
)
default_storage.delete(
self.picture_large.path
)
default_storage.delete(
self.picture_thumbnail.path
)
self.picture.delete()
super().delete(*args, **kwargs)
  1. Create a model form, IdeaForm, for the Idea model in forms.py, just like we did in the previous recipes.
  2. In the view for adding or changing ideas, make sure to post request.FILES beside request.POST to the form:
# myproject/apps/ideas/views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import (render, redirect, get_object_or_404)
from django.conf import settings

from .forms import IdeaForm
from .models import Idea


@login_required
def add_or_change_idea(request, pk=None):
idea = None
if pk:
idea = get_object_or_404(Idea, pk=pk)
if request.method == "POST":
form = IdeaForm(
request,
data=request.POST,
files=request.FILES,
instance=idea,
)

if form.is_valid():
idea = form.save()
return redirect("ideas:idea_detail", pk=idea.pk)
else:
form = IdeaForm(request, instance=idea)

context = {"idea": idea, "form": form}
return render(request, "ideas/idea_form.html", context)
  1. In the template, make sure to have encoding type set to "multipart/form-data", as follows:
<form action="{{ request.path }}" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<button type="submit">{% trans "Save" %}</button>
</form>
If you are using django-crispy-form as described in the  Creating a form layout with django-crispy-forms recipe, the enctype attribute will be added to the form automatically.