1: Adding Bootstrap cards


Now that we have created the first part of our homepage, it's time to demonstrate how Django can utilize Python to create multiple Bootstrap cards. We will add a card to the template, create and add information to a Django model, and finally let the Django template language (DTL) render the information we entered as Bootstrap cards. This type of functionality is one of the key features of Django.

 

 

Add a Bootstrap card to the home.html

env > mysite > main > templates > main > home.html

<!DOCTYPE html>
<html>
  	<head>
    	...
  	</head>
  	<body>

    	{% load static %}

    	<!--Navbar-->
    	...

    	<!--CTA-->
    	...

 		<!--Products-->
	    <div class="container py-3">
	    	<h2>Products</h2>
	    	<hr>
	    	<br>
	      	<div class="row">
	        	<div class="col-sm-12 col-md-6 col-lg-3 pb-4">
	        	</div>
	      	</div>
	    </div>

    	<!-- Optional Javascript -->
    	...
  	</body>
</html>

Create a new Products comment and a division element with a container py-3 class attribute below the CTA section. Then nest a heading element, a thematic break element, and a break element within it. Add a Bootstrap row in the container so the four products we will add can display in a row across the screen. Now add a column division element nested within the row so each Bootstrap card will be full screen on small devices (col-sm-12), half of the screen on medium devices (col-md-6), and a fourth of the screen on large devices (col-lg-3). We will also add a padding bottom class attribute (pb-4) so the cards will not touch in smaller viewports.

 

<!--Products-->
<div class="container py-3">
	<h2>Products</h2>
	<hr>
	<br>
	 <div class="row">
	    <div class="col-sm-12 col-md-6 col-lg-3 pb-4">
	      	<div class="card h-100" style="border:none">
	        	<img src="{% static 'img/jaybird-x3.jpg' %}" class="card-img-top" alt="Jaybird X3">
	        	<div class="card-body">
	          		<h5 class="card-title">Jaybird X3</h5>
	            	<p class="card-text text-muted" style="font-size:12px">Wireless Earphones</p>
	            	<p class="card-text">Sports earbuds with a secure fit for athletic performance</p>
	            	<a href="#" class="btn btn-warning">Buy now</a>
	        	</div>
	      	</div>
	    </div>
	</div>
</div>

Add your first Bootstrap card in the column. The card contains information about a pair of headphones such as product name, type, and description. The card has no border and height is set to h-100, or full height.  Inside the card add an image, body, title, text, and a button.

Reload the http://127.0.0.1:8000/ page and the card will have a picture and the text we coded in the HTML.

Bootstrap card

 

Add more Bootstrap cards with Django models

At this point, you could copy and paste the card multiple times within the row to make more cards; however, this would require you to edit the HTML template every time you want to add a product.  Instead we will use Django model classes to quickly add products using the Django admin page and have them automatically render in the template.  Don't worry you'll see exactly what this means next. 








2: Creating a Django model


If you think back to the Python exercises, you'll remember how you can create a Class that might contain specific functions or information.  Django expands upon this idea with its own special classes called models.  In the models.py file we can create a Class with the fields we specify.  For example we will create a class called Product that will contain information for each pair of headphones.  First we setup the fields we want the class to have in models.py then we will add the actual information on the admin site.  

 

Add a model in models.py

env > mysite > main > models.py

from django.db import models

# Create your models here.

class Product(models.Model):

Open the models.py file and create class called Product that inherits a Django model.  This will provide the class with Django's model functionality.

 

from django.db import models

# Create your models here.

class Product(models.Model):
	product_name = models.CharField(max_length=150)
	product_type = models.CharField(max_length=25)
	product_description = models.TextField()
	affiliate_url = models.SlugField(blank=True, null=True)

Add four model fields to the Product model.  First add product_name and make it a model CharField that allows a maximum of 150 characters of text. Add a product_type as a model CharField that allows a maximum of 25 characters. Next add a product_description field with a TextField to allow larger paragraph text submissions.  Then add product_url as a SlugField so the field only accepts valid website urls and include blank=True, null=True so the field is not required.  Later on, if you choose to deploy your web app on a custom domain, you can visit Amazon Associates, create an account, and enter your site's information to create product referral links.  If a visitor clicks a link and purchases the product, you'll receive a commission.  More information about affiliate marketing and current websites that monetize via affiliate links is provided near the end of the tutorial.

In the code snippet, notice the class takes in models.Model and fields call on models since they are already defined in the django>db>models folder.  That's why it's important to import models at the top of the file.  Visit the Django documentation to see a full list of model fields. 

 

Add a media upload field in models.py

env > mysite > main > models.py

from django.db import models

# Create your models here.

class Product(models.Model):
	product_name = models.CharField(max_length=150)
	product_type = models.CharField(max_length=25)
	product_description = models.TextField()
	affiliate_url = models.SlugField(blank=True, null=True)
	product_image = models.ImageField(upload_to='images/')

We will add our product_image as a Django ImageField that requires an image file upload. The file will be uploaded to a folder named images/. Save all of the changes made to the models.py file. 

 

Change the admin model display name

env > mysite > main > models.py

from django.db import models

# Create your models here.

class Product(models.Model):
	product_name = models.CharField(max_length=150)
	product_type = models.CharField(max_length=25)
	product_description = models.TextField()
	affiliate_url = models.SlugField(blank=True, null=True)
	product_image = models.ImageField(upload_to='images/')

	def __str__(self):
		return self.product_name

The last thing we need to complete our Product model is to specify that the name displayed in the Django admin will be product_name and not the default Product object (1), Product object (2) names given by Django admin. Save the file.

Model object (1)
Model object (1) with name

 

Add the media folder url to the settings.py

env > mysite > mysite > settings.py

...

STATIC_URL = '/static/'

STATICFILES_DIRS = [
   os.path.join(BASE_DIR, "static"),
   ]

MEDIA_URL = '/media/'

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Now we'll specify the media folder for the images uploaded in the product_image model field. The media folder allows images to be uploaded by a user instead of only adding images directly to the static folder. Open the settings.py file and specify the media folder url, similar to the static folder. Save the file.

 

Add the media folder url to the urls.py

env > mysite > mysite > urls.py

from django.contrib import admin
from django.urls import path, include
from django.conf import settings #add this
from django.conf.urls.static import static #add this

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include ('main.urls')),
]

if settings.DEBUG: #add this
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Then go to the urls.py file in the mysite folder, not the main > urls.py file, and add the code above to get access to the media images on your web page. Save the file.  Next we will install a program that is needed to upload images.

Keep in mind that this solution is for your development server, hence the if condition that checks if debugging is set to true.  Your production server will need a different configuration. 

 

Install Pillow

macOS Terminal

(Ctrl + C)

(env)User-Macbook:mysite user$ pip install Pillow
...

Windows Command Prompt

(Ctrl + C)

(env) C:\Users\Owner\Desktop\Code\env\mysite>pip install Pillow
...

First we need to stop running the server with Ctrl + C. Next we need to install a package named Pillow to use the Django ImageField in the models.py.

 

Make migrations and migrate the new model to the database

macOS Terminal

(env)User-Macbook:mysite user$ python3 manage.py makemigrations
Migrations for 'main':
  main\migrations\0001_initial.py
    - Create model Product

(env)User-Macbook:mysite user$ python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, main, sessions
Running migrations:
  Applying main.0001_initial... OK

(env)User-Macbook:mysite user$

Windows Command Prompt

(env) C:\Users\Owner\Desktop\Code\env\mysite>py manage.py makemigrations
Migrations for 'main':
  main\migrations\0001_initial.py
    - Create model Product

(env) C:\Users\Owner\Desktop\Code\env\mysite>py manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, main, sessions
Running migrations:
  Applying main.0001_initial... OK

(env) C:\Users\Owner\Desktop\Code\env\mysite>

We have added the Product model and all of the model fields to the Django project. However, we will need to save them to the Django database, db.sqlite3. Run the commands for makemigrations and migrate. makemigrations creates the model Product in the database and then migrate applies the database updates to the overall Django project.








3: Using the Django admin site


Django comes with a built-in administration interface. The Django admin site is one of the most powerful parts of Django and can be customized by developers for businesses looking for an internal management tool. To begin, we will use the admin page to create instances of our Product model. Later we will add more models and check out the user management panel.

 

Register the model in the admin.py file

env > mysite > main > admin.py

from django.contrib import admin
from .models import Product  #import Product model here
# Register your models here.

admin.site.register(Product)

The Django database was successfully updated so now we need to register our model so it will appear in the admin page. Go to the admin.py file in the main folder and add the above code to register the model Product in the Django administration page. Notice how we import Product from models.py. Save the file.

 

Create a Django admin username and password

macOS Terminal

(env)User-Macbook:mysite user$ python3 manage.py createsuperuser
Username (leave blank to use 'owner'): admin
Email address:
Password: *****
Password (again): *****
Superuser created successfully.

(env)User-Macbook:mysite user$ python3 manage.py runserver

Windows Command Prompt

(env) C:\Users\Owner\Desktop\Code\env\mysite>py manage.py createsuperuser
Username (leave blank to use 'owner'): admin
Email address: 
Password: *****
Password (again): *****
Superuser created successfully.

(env) C:\Users\Owner\Desktop\Code\env\mysite>py manage.py runserver

Enter the createsuperuser command to create a username and password to log into the admin site. Your username can be admin. Press Enter to skip the optional email address field and create a password. Then run your server again.

 

Login to the Django admin site

Django admin loginGIF

Go to the URL http://127.0.0.1:8000/admin/ in a new tab and the Django administration login page will appear.  Enter the username and password you just created and login.  You will be brought to the Django admin interface where you should see the model "Products" listed below "Main" since we added it to the admin.py file.

 

Add a product to the Django admin

Django admin > Main > Products > Add product > Jaybird X3

Add product to Django Products modelGIF

Product name: Jaybird X3

Product type: Wireless Earphones

Product description: Sports earbuds with a secure fit for athletic performance

Affiliate url: 

Product image: jaybird-x3.jpg

Once logged in, go to the "Products" link listed below "Main". Select "Products"Click the gray button on the right side of the screen that says "Add product". Add the product information for Jaybird X3 headphones.

For the image, click on the link above and download the image. Save the image in your Downloads folder so you can easily find it before uploading.

 

Add the remaining products to the Django admin

Django admin > Main > Products > Add product > Apple Airpods

Product name: Apple Airpods

Product type: Wireless Earbuds

Product description: Active noise cancellation and quick access to voice assistant

Affiliate url: 

Product image: apple-airpods.jpg

 

Save and add another > ASTRO A40

Product name: ASTRO A40

Product type: Gaming Headset

Product description: Adjustable & lightweight with removable mic

Affiliate url: 

Product image: astro-a40.jpg

 

Save and add another > Beats Studio3 Wireless

Product name: Beats Studio3 Wireless

Product type: On-Ear Headphones

Product description: Up to 22 hour battery life, noise cancelling headphones

Affiliate url:

Product image: beats-studio3.jpg

Add the remaining three products to the admin. Remember to download and save the images linked above in your Downloads folder so you can easily access and upload them in the Django admin.








4: Displaying information with DTL


In order for the products to display on the homepage, we need to configure the homepage function in views.py to pass the products data as context.

 

Configure the views.py to display the Product model

env > mysite > main > views.py

from django.shortcuts import render
from .models import Product #import Product from models

# Create your views here.
def homepage(request):
	product = Product.objects.all() #queryset containing all products we just created
	return render(request=request, template_name="main/home.html", context={'product':product})

First import the Product model at the top of the views.py file so it can be called in the function below. Now call Product.objects.all() to get all the products we just created and store them in product. Then pass this product variable as the context 'product':product to access it in the home.html file. The value in quotations is the actual name we will use in the HTML template; for simplicity we usually use the same name.

 

Configure the home.html to display the Product model

env > mysite > main > templates > main > home.html

<!--Products-->
<div class="container py-3">
	<h2>Products</h2>
	<hr>
	<br>
	<div class="row">
		{% for p in product %}
		<div class="col-sm-12 col-md-6 col-lg-3 pb-4">
			<div class="card h-100" style="border:none">
			  ....
			</div>
		</div>
		{% endfor %}
	</div>
</div>

Now go back the home.html template and find the Bootstrap card in the code. We are going to use the Django template language (DTL) so all of the cards will be rendered using just one card. DTL is a collection of Python commands that can be used in HTML documents that strikes a balance between functionality and ease.

In DTL, Python functions are surrounded by {% %} while variables are surrounded by {{ }}. Add a for loop that iterates over the product queryset just after the division element with the row class attribute. The for loop will cycle though the list of products, each represented as p. Close the for loop after the end tag of the column division element so the for loop is completely nested within the row.

 

Add the Product fields to home.html

env > mysite > main > templates > main > home.html

<!--Products-->
<div class="container py-3">
	<h2>Products</h2>
	<hr>
	<br>
	<div class="row">
		{% for p in product %}
		<div class="col-sm-12 col-md-6 col-lg-3 pb-4">
			<div class="card h-100" style="border:none">
				<img src="{{ p.product_image.url }}" class="card-img-top" alt="{{ p.product_name}}">
			  	<div class="card-body">
					<h5 class="card-title">{{ p.product_name }}</h5>
				  	<p class="card-text text-muted" style="font-size:12px">{{ p.product_type }}</p>
				  	<p class="card-text">{{ p.product_description }}</p>
				  	<a href="{{ p.affiliate_url }}" class="btn btn-warning">Buy now</a>
			  	</div>
			</div>
		</div>
		{% endfor %}
	</div>
</div>

Add the Product fields we entered in models.py to a Bootstrap card.

Refresh the http://127.0.0.1:8000/ browser page and there are now four cards that display all of the content you added to the Django backend. However, the cards don't line up properly because of the different image sizes. Let's make all of the cards align correctly no matter the size of the image.

Homepage with models

 

 

Add an object-fit to the product image element

env > mysite > main > templates > main > home.html

<img src="{{ p.product_image.url }}" class="card-img-top" alt="{{ p.product_name}}" style="width: auto; height: 250px; object-fit: scale-down;">

Add a style attribute to the image element. Set the width to auto so it automatically resizes to the specified height: 250px. Then add object-fit: scale-down to have the image maintains its proportions at different screen sizes.

Homepage with even model images

 

Limit the number of products on the homepage

env > mysite > main > views.py

def homepage(request):	
	product = Product.objects.all()[:4]
	return render(request=request, template_name="main/home.html", context={'product': product})

Now that all of the cards are the same size, let's make it so only 4 products can display on the homepage. Go to the views.py and add [:4] to the end of Product.objects.all(). Rather than get all of the Product model objects, the homepage will now only display the last four model objects added. The rest of the products will display on a separate products page we'll create next.






Quiz Questions


1. Which url gives access to the admin page?


2. What is the purpose of Django models?


Next lesson


Check out the comments and debug buttons if you get stuck.