The Assignment:
A friend of ours was starting a business and they needed a fairly complicated and very customized site to be able to put it all together for them. We got the site mostly built but unforeseen circumstances, including the addition of a new child into the family, prevented them from going much further with it at the time. Now this is still possibly in on hold status and the idea had a lot of potential so I don’t want to speak to freely about the functionality specifics but I can say this site was very configurable for each individual user, had some pretty sophisticated search and filtering tools, had a sleek professional look (my friend was a UX designer professionally), and was meticulously and very specifically responsive to any sized viewport. It processed credit cards and kept track of memberships and it was all done in Django and Javascript with some help from Tailwind for the css.
Django is a web framework for Python, or basically a lot of structured code like a skeleton for a website. With Django a lot of the nitty gritty nuts and bolts are already assembled and laid out in a way that keeps things efficient and organized. Yes, you could build a website from scratch using just Python, but it would be a much larger undertaking and very time consuming. Before I had used Django my websites could sometimes get a little confused and messy. Yes, they worked in the end, and through trial and error I had come up with schemes that were fairly clear and orderly, but Django had the combined experience of many developers figuring out the most clear and efficient way to do things.
Now ScreenRanker was not my first Django project, and I was already pretty comfortable with of having things organized in models (which define the backend or data structures), urls (which define paths where everything is located), views (which apply logic to the models and arrange them for presenting), and templates (which are the end html pages with some template logic sprinkled in). This keeps everything in it’s own place and once I got used to it in Django I started implementing it in my PHP sites as well.
So let’s look at a few examples in code, then we can see what it all looks like in the end.
Let’s start with models.
The following is equivalent to a table in a database. It is written as a class which inherits the built in Django model class and is basically an interpreter between the database and the rest of the code.
class IP(models.Model):
is_active = models.BooleanField(default=False)
CHOICES = [
('1', 'Book'),
('2', 'Documentary'),
('3', 'Graphic Novel/Comic'),
('4', 'Journalism'),
('5', 'Merchandise'),
('6', 'Podcast'),
('7', 'Short Film'),
('8', 'Stage Play/Musical'),
('9', 'Video Game'),
]
media_type = models.CharField(max_length=1, choices=CHOICES, blank=False)
title = models.CharField(max_length=255, blank=False)
screenranker_score = models.DecimalField(decimal_places=2, max_digits=6, default=0.00)
author_creator = models.CharField(max_length=255, blank=True)
production_company = models.CharField(max_length=255, blank=True)
artist = models.CharField(max_length=255, blank=True)
composer = models.CharField(max_length=255, blank=True)
producer = models.CharField(max_length=255, blank=True)
publisher = models.CharField(max_length=255, blank=True)
director = models.CharField(max_length=255, blank=True)
year_published = models.ForeignKey(Year, on_delete=models.CASCADE, related_name='year_published', blank=True)
# published_through = models.ForeignKey(Year, on_delete=models.CASCADE, related_name='published_through', blank=True)
ip_image = ResizedImageField(blank=True, size=[360, 360], force_format='JPEG', upload_to='media/ips/%Y/%m/')
logline = models.TextField(blank=True)
summary_description = models.TextField(blank=True)
BOOKS = [
('1', '1'),
('2', '2'),
('3', '3'),
('4', '4'),
('5', '5'),
('6', '6'),
('7', '7'),
('8', '8'),
('9', '9'),
('10', '10+'),
]
series_length = models.CharField(max_length=2, choices=BOOKS, blank=True)
...
...
(and so on and so forth, this class had a lot of properties)
You can see that this class is defining a bunch of fields in a database. In Django you can set your backend to Postgres, or MySql, or whatever you like then just run migrations and it will write the structure from the class into that database. Pretty cool.
Then if you want to have a search function to look through the IP (intellectual property) table in your views.py file you would have something like this:
def search_ip(request):
msgs = get_messages(request)
sort = '-created_date'
if request.GET.get('sort'):
sort = request.GET.get('sort')
all_ips = ips = IP.objects.filter(is_active=True).order_by(sort)
total = ips.count()
query = request.GET.get('search')
if not query:
query = ''
if request.method == 'GET':
if query is not None:
lookups = Q(title__icontains=query) | Q(author_creator__icontains=query) | Q(genres__name__icontains=query) | Q(tags__name__icontains=query)
search_ips = ips = ips.filter(lookups).distinct().order_by(sort)
total = ips.filter(lookups).distinct().count()
paginator = Paginator(ips, 10)
if request.GET.get('page') != '':
page_number = request.GET.get('page')
for ip in ips:
ip.overall_score = round(ip.screenranker_score / 10, 1)
ip.mop = '${:,}'.format(ip.minimum_option_price)
ips = paginator.get_page(page_number)
ips.adjusted_elided_pages = paginator.get_elided_page_range(page_number)
context = {
'total':total,
'ips':ips,
'all_ips':all_ips,
'search_ips':search_ips,
'search':query,
'page':page_number,
'sort':sort,
'msgs':msgs,
}
return render(request, 'ips/search_ip.html', context)
Finally, you want to take the items in the context dictionary and output them to a template page, which is basically html with some template logic similar to Python and now you have a data driven page.
{% extends 'base.html' %}
{% block content %}
<div class="text-white mx-auto xl:mx-0 md:ml-[10vw] xl:ml-[122px] xl:mr-[122px]">
<!-- Title Section -->
<div class="hidden text-4xl font-bold mt-[90px] mb-[60px] xl:pr-0 xl:mr-[321px] md:flex">
Find books, novels, producers, directors, writers, publishers, agents, actors….
</div>
<!-- Search Bar -->
<div class="mt-4 md:mt-0 md:mb-[60px]">
<form class="flex flex-row" action="{% url 'ips:search_ip' %}" method="GET">
{% csrf_token %}
<div class="">
<input id="search-input" class="w-[85vw] h-[48px] sm:w-[500px] sm:h-[63px] md:w-[562px] md:h-[63px] rounded text-black font-semibold pl-2" type="text" name="search" autocomplete="off">
<input type="hidden" name="page" value="1">
<div id="search-drpdwn" class=" text-black hidden pt-3 pb-3 absolute z-10 rounded w-[85vw] h-[510px] sm:w-[500px] sm:h-[690px] md:w-[562px] md:h-[710px] bg-white">
{% for ip in ips %}
<a href="{% url 'ips:detail' id=ip.id %}?page={{ page }}&search={{ search }}&sort={{ sort }}">
<div class="flex flex-row mb-2 hover:bg-orange-400 hover:rounded-lg hover:text-white">
<div class="ml-3 mr-3 mb-1.5 mt-1.5">
<img class="rounded h-[32px] w-[30px] md:h-[50px]" {% if '/covers/' in ip.ip_image.url %} src="/media{{ ip.ip_image.url }}" {% else %} src="{{ ip.ip_image.url }}" {% endif %} alt="">
</div>
<div class="">
<div class="font-bold">
{{ ip.title }}
</div>
<div class="text-sm text-gray">
by {{ ip.author_creator }}
</div>
...
...
...
</div>
</div>
</div>
</div>
</form>
</div>
</div>
Then for precise placement, style, and responsive behavior Winter fine tuned the Tailwind css and additional custom css, and for faster loading and custom functional behavior my cousin Dave helped with a lot of Javascript. When it all came together, it was a pretty nice website.





