Creating a dynamic photo collection app that spans different industries is an exciting opportunity in web development. These apps, also known as lookbooks, can be used in fields like fashion, real estate, and travel to showcase visual content effectively.
Recently, I developed a comprehensive lookbook using Django and Cloudinary. You can get a thorough understanding of its functionalities by watching a and reading a detailed description. Additionally, we'll highlight some noteworthy features that developers may find intriguing. Finally, you can fork the app from
Here’s what the app offers:
This lookbook application integrates several advanced features that make it a robust platform for managing and displaying user-generated content. Below are some highlights that developers might find particularly interesting.
One of the standout features of this application is the integration of Cloudinary for managing images. Cloudinary's powerful image hosting and transformation capabilities make it an ideal choice for applications dealing with a lot of visual content.
Highlights:
CloudinaryField usage:
Using CloudinaryField in Django models simplifies the process of storing image references and managing them. It integrates seamlessly with Django's ORM and forms, and automatically uploads images to Cloudinary when the form is saved. (See Dynamic Form Handling.)
from cloudinary.models import CloudinaryField
class Lookbook(models.Model):
overlay_image = CloudinaryField('overlay_images')
Transformations and overlays:
The application utilizes Cloudinary’s AI-powered capabilities to automatically enhance lookbook images. Users can customize their lookbooks by choosing image overlays, image orientation, border width, and border color. AI paints out the images to fit the selected orientation, ensuring that they are displayed correctly without manual adjustments. These features are supported by utility functions designed for efficient image processing.
In addition, when handling user-generated profile images, AI-based transformations ensure that images are optimized and uniformly cropped and centered, regardless of the original image content.
Here’s an example of an original and transformed profile picture, along with the code to generate it. Notice how the crop honors the face’s placement in the center.
profile_url = CloudinaryImage(public_id).build_url(quality='auto', width=600, height=600, crop='auto', gravity='face')
Easy retrieval and display:
Images stored in Cloudinary can be easily retrieved using their URLs, simplifying the process of displaying images in templates.
For example, when a user selects a lookbook to view, its ID is passed to the display function in the views.py file.
The display function then retrieves a list of all images linked to that lookbook. The transformed delivery URLs of these images stored in the Lookbook model are sent to the template for rendering.
def display(request, lookbook_id):
lookbook = get_object_or_404(Lookbook, pk=lookbook_id)
images = lookbook.images.all()
return render(request, 'display.html', {'photos': images, 'lookbook': lookbook})
<!-- templates/display.html -->
{% extends 'base.html' %}
{% block content %}
<div class="container first-container">
<!-- Display lookbook title -->
<h2>{{ lookbook.title }}</h2>
<!-- Display lookbook description -->
<p>{{ lookbook.description }}</p>
</div>
<div class="container">
<div class="gallery-container" >
<div class="gallery-row">
{% for photo in photos %}
<div class="gallery-col">
<div class="gallery-image">
<!-- Display transformed image -->
<img src="{{ photo.transformed_image }}" alt="Image" class="img-fluid">
<div class="gallery-overlay">
<!-- Link to view in a new browser tab -->
<a href="{{ photo.transformed_image }}" target="_blank"><h5>Click to View</h5></a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
Efficient and secure handling of user-generated content is crucial for any application. In this lookbook app, content management is personalized and user-specific.
Highlights:
from django.contrib.auth.models import User
class Lookbook(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_picture = CloudinaryField('profile_pictures')
profile_url = models.URLField(blank=True, null=True, default='https://res.cloudinary.com/yelenik/image/upload/avatar')
bio = models.TextField(blank=True)
my_lookbooks = models.ManyToManyField(Lookbook, blank=True)
In the HTML template that displays all lookbooks, each lookbook is associated with its creator's profile image, accessed via {{ lookbook.user.profile.profile_url }}
.
def __str__(self):
return self.user.username
div class="row">
{% for lookbook in lookbooks %}
<div class="col-md-4">
<div class="card mb-4">
<div class="card-body all">
<div class="d-flex align-items-center mb-3">
<img src="{{ lookbook.user.profile.profile_url }}" alt="{{ lookbook.user.username }}" class="rounded-circle mr-2" style="width: 40px; height: 40px;">
<h5 class="mb-0">{{ lookbook.user.username }}</h5>
</div>
<h5 class="card-title">{{ lookbook.title }}</h5>
<p class="card-text">{{ lookbook.description }}</p>
<a href="{% url 'display' lookbook.id %}" class="btn btn-primary card-btn">View</a>
</div>
</div>
</div>
{% empty %}
<div class="col">
<p>No lookbooks found.</p>
</div>
{% endfor %}
</div>
Forms are essential for handling user inputs in any web application. This app uses LookbookForm
and LookbookImageForm
to manage lookbook creation and image uploads.
Highlights:
CloudinaryImage
field type.class LookbookForm(forms.ModelForm):
class Meta:
model = Lookbook
fields = ['title', 'description', 'overlay_image']
def save(self, commit=True):
instance = super().save(commit=False)
if commit:
instance.save()
return instance
To keep the codebase maintainable and consistent, utility functions handle the bulk of image-processing tasks. This ensures standardized image transformations and simplifies maintenance.
Highlights:
Centralized image processing:
Functions likegenerate_transformation_options
, get_public_id_from_url
, and create_transformed_url
are reused within the application to standardize image processing. These functions generate Cloudinary transformations based on user selections for the lookbook’s attributes, ensuring consistency during both creation and editing processes.
This transformation, built using these functions, applies user-selected formatting to an original image, as shown below. If the chosen orientation requires reshaping, generative AI will outpaint the necessary sides. Additionally, a border with the selected width and color is added, and a chosen overlay image is applied with reduced opacity in the top right corner.
def generate_transformation_options(lookbook):
# Determine width and height based on orientation
if lookbook.orientation == 'portrait':
width, height = 400, 500
elif lookbook.orientation == 'landscape':
width, height = 800, 600
else: # square
width, height = 800, 800
# Initialize transformation list if not already present
transformation_options = { }
if 'transformation' not in transformation_options:
transformation_options['transformation'] = []
# Apply border style if border width is not '0px'
if lookbook.border_width != '0px':
transformation_options['border'] = f'{lookbook.border_width}_solid_{lookbook.border_color}'
# Define base transformation options
all_transformation = [
{'quality': 'auto',
'width': width,
'height': height,
'crop': 'pad',
'background': 'gen_fill:ignore-foreground_true'}
]
transformation_options['transformation'].insert(0, all_transformation)
# Apply overlay image if provided
if lookbook.overlay_image:
overlay_transformation = [
{'overlay': lookbook.overlay_image, 'gravity': 'north_east', 'width': 100, 'flags': 'layer_apply', 'x': 20, 'y': 20, 'opacity': 80}
]
transformation_options['transformation'].insert(1, overlay_transformation)
# Add scale transformation to make the width 800
transformation_options['transformation'].insert(2, {'width': 800, 'crop': 'scale'})
return transformation_options
def create_transformed_url(public_id, transformation_options):
return CloudinaryImage(public_id).build_url(**transformation_options)
Efficient data retrieval and filtering are vital for application performance. The lookbook app uses Django ORM to fetch and filter lookbooks based on user selections.
Highlights:
User-specific data retrieval:
Functions like all_lookbooks
and my_lookbooks
efficiently retrieve user-specific data.
def all_lookbooks(request):
users = User.objects.all()
user_ids = request.GET.getlist('user_ids')
if not user_ids or 'all' in user_ids:
user_ids = [str(user.id) for user in users]
lookbooks = Lookbook.objects.filter(user__id__in=user_ids).order_by('title')
return render(request, 'all_lookbooks.html', {'lookbooks': lookbooks, 'users': users, 'selected_user_ids': user_ids})
A clear and intuitive URL routing scheme is essential for easy navigation within the application. The urls.py
file defines clear URL patterns for accessing different views.
Highlights:
Auth integration:
Incorporating Django's built-in authentication views simplifies user authentication management.
from django.urls import path
from lookbook_app import views
from django.contrib.auth import views as auth_views
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/login/', auth_views.LoginView.as_view(), name='login'),
path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'),
path('signup/', views.signup, name='signup'),
path('create_lookbook/', views.create_lookbook, name='create_lookbook'),
path('display/<int:lookbook_id>/', views.display, name='display'),
path('profile/', views.profile, name='profile'),
path('my_lookbooks/', views.my_lookbooks, name='my_lookbooks'),
path('all_lookbooks/', views.all_lookbooks, name='all_lookbooks'),
path('', views.all_lookbooks, name='all_lookbooks'),
path('edit_lookbook/<int:lookbook_id>/', views.edit_lookbook, name='edit_lookbook'),
]
The lookbook application leverages Django to create a robust platform for managing and displaying user-generated content. Key features like Cloudinary integration for image management, dynamic form handling, centralized utility functions, and efficient data querying contribute to a well-structured and scalable application.
These aspects improve the user experience and streamline the development process, making this project a valuable example for Django/Python developers interested in advanced web application techniques.
Feel free to explore the project in