Customization¶
Although the built-in image model, along with the image processing views demonstrated in the demo, might have met the basic needs for some apps, advance users might require more features in terms of image storage, permission control, template inheritance, and Image model (fields) customization.
Before that, we need to address how this app is working.
Image model and views¶
Until now, Django didn’t supply any type of Field which can store unknown
length of images or files. However, the introduction of JsonField
(since Django 3) made it possible workaround the issue by storing the pk
of image model instances as a Json list in a custom JsonField
.
The first obstacle we met is: how to map the pks
to the actual image model instances?
Our workaround is saving the
app_label.model_name
of the image model (which we called target_model
throughout the app and docs)
in galleryfield.fields.GalleryField
.
Following that, there should be three basic views to handle image model instances,
before saving their pks
to the GalleryField
:
Create (which we called upload operation),
List (which we called fetch operation)
and Update (which we mean crop operation).
The potential problems include: users will have to write 3 image handling views each time when
a new target_model
is introduced for a new type of gallery/album,
is there any shortcut that we don’t need to write much code to achieve that?
And, can a image model automatically know what default url name they should look for
(to find the views) when trying to do the 3 operations?
We finally introduced a class-based views for each operation, and
set a naming rule for the naming of the url names
for the 3 operations.
Therefore, a model level customization (for image model) involves:
A valid target image model¶
Image model is where we actually save the image uploaded. To be a valid target image model, it need to meet one of the following 2 requirements:
It has a
django.db.models.ImageField
namedimage
.It has a
django.db.models.ImageField
which not namedimage
but the field can be accessed by aclassmethod
namedget_image_field
. For example, in an app namedmy_app
, we can have the following valid target image model inmy_app.models.py
:
class MyImage(models.Model):
photo = models.ImageField(
upload_to="my_images", storage=default_storage, verbose_name=_("Image"))
creator = models.ForeignKey(
settings.AUTH_USER_MODEL, null=False, blank=False,
verbose_name=_('Creator'), on_delete=models.CASCADE)
creation_time = models.DateTimeField(default=now(), blank=False)
@classmethod
def get_image_field(cls):
return cls._meta.get_field("photo")
Note
In the example above, when defining the get_image_field()
,
we can’t simply return cls.photo
because it
returns a django.db.models.fields.files.ImageFieldFile
object instead of a django.db.models.ImageField
object.
The galleryfield.models.BuiltInGalleryImage
is using the first style (
with target_model="garllery_widget.BuiltInGalleryImage"
).
However, if you don’t want to do much change to your existing models
(e.g., avoiding migrations of existing model),
the second style is more sounding.
In the following, we will use the above model in a galleryfield.fields.GalleryField
with target_model = "my_app.MyImage"
.
Three views for handling the image model objects¶
Three views for handling the image model objects (upload, fetch and crop). We provided 3 class-based-views for these views to enable the built-in views.
See Built-in Image handling Views for more detail. We hope users can subclass the views above without much coding work. We think the 3 views handling built-in image model (i.e.,
galleryfield.image_views.BuiltInImageCreateView
,galleryfield.image_views.BuiltInImageListView
andgalleryfield.image_views.BuiltInImageCropView
were good examples of how to used them.
Naming rule for urls of image handling views¶
Generally, the widget need to know the urls for image handling views (see GalleryWidget docs).
We may specify the url name manually in gallery modelform fields widget configurations.
Alternatively, we can also let the widget infer what urls it should use for those views, by
following a naming rules for those views in URL_CONF
.
The default url names are the lower cased model_name, suffixed by -upload
, -fetch
and -crop
,
respectively. For example, if you have a target_model
named my_app.MyImage
, then the default
url names are myimage-upload
, myimage-fetch
and myimage-crop
. In this way, you don’t
need to specify in the GalleryWidget
the param upload_url
and fetch_url
,
and no need to specify the crop_url_name
in each of the 3 class-based views.
Until now, we were talking about image model instance handling.
GalleryField rendering customization¶
Now we turn to the customization of gallery model.
Back to the demo, when dealing with the gallery model instance, there isn’t much magic about
demo.views.GalleryCreateView
and demo.views.GalleryUpdateView
.
Here, we need to address demo.views.GalleryDetailView
, on how it rendering the
galleryfield.fields.GalleryField
.
With the MyImage
in previous example as the target_model
, we can have a gallery model named MyGallery
:
class MyGallery(models.Model):
album = GalleryField(target_model="my_app.MyImage", verbose_name=_('My photos'))
owner = models.ForeignKey(
settings.AUTH_USER_MODEL, null=False, blank=False,
verbose_name=_('Owner'), on_delete=models.CASCADE)
By subclassing django.views.generic.detail.DetailView
, we can have a gallery detail view like:
from django.views.generic.detail import DetailView
from my_app.models import MyGallery
class MyGalleryDetailView(DetailView):
model = MyGallery
Then we add a template file named mygallery_detail.html
to folder my_app/templates/my_app/
,
with the following code block:
{% extends 'base.html' %}
{% load static %}
...
{% for obj in object.album.objects.all %}
<img src="{{ obj.photo.url}}">
{% endfor %}
...
And add the url of the view:
from my_apps import views
urlpatterns = [
...
path('album-detail/<int:pk>',
views.MyGalleryDetailView.as_view(), name='my_gallery-detail'),
]
Then we can navigate to see the images in a specific gallery.
As you might guess from the first line in the template snippet,
the GalleryField
provide a Queryset
API for the image model
instances it related to. No wonder, you can do the following:
>>> first_gallery = MyGallery.objects.first()
>>> photos_in_first_gallery = first_gallery.album.objects.all()
>>> photos_before_2021 = photos_in_first_gallery.filter(creation_time__lt=datetime(2021, 01, 01))
More over, the demo provide an example of how to render
the field using sorl.thumbnail
and Blueimp Gallery
package.
Finally, it’s your opportunity to show your skills on customizing the gallery/album frontend, which is beyond the scope of this package.
Template customization¶
TODO