# Django # Background There are many website we use a lot with its apps and feature. Many people also want to learn how to make their own. So if you're reading this Notes, you're gonna learn how to make website from its frontend to backend. Some of the problems of making a website is by code and how to build it in a fast and effective way. # What is Django? Django is a popular web development framework and its also one of the best way to do it. Django has some built-in backend features so it's one of the reason we use Django as backend technology to solve it. Django is written in Python. Which means it's a lot more approachable. Since it's written in Python, it's a lot more approachable than a lot of other frameworks because Python is a lot more approachable than a lot of other programming languages. Just because it's written in Python for beginners but then it's also powerful enough for the experts. Platform such as Instagram still uses Django (slightly) as their backend. Like, all of the things that Instagram uses has Django at its core. Other platform that uses Django is Pinterest. Most of the part of Pinterest is Django powered. # Fundamental of Developing a Website Few things web application do: 1. Understanding urls (links) When we go to a url that we made, and click on menu or features, it will take us to there and show information according to the url we clicked. An example could be a database that saves different usernames. 2. Return responses #Web doing a response related to that url. Ensuring that users receive the correct responses based on their requests. The server processes each request and delivers the response. It makes the website more interactive and dynamic, allowing users to access different features and information smoothly. 3. Remember things Improves user experience by maintaining data across sessions. By using cookies, sessions, databases, and local storage, web can keep track of data such as login status, preferences, and shopping carts. It ensures that users don't have to input information repeatedly, making the site more convenient and efficient. # Getting Started with Django Django's framework is written in Python. So we're gonna use Python version 3.6 and above. Python 2 is kind of on its way out. If we use a different version of Python 3 should be fine as long as you're using something around Python 3.4 and above. Anything below it is gonna be challenging. If we use Python 2.7 most of the things will probably work. So if it doesn't work it's likely related to Python 2.7. Virtualenv, or Virtual Environments, is a tool to keep the dependencies required by different projects in separate places, by creating virtual Python environments for them. Basically, if your project has different software versions (and it will) virtualenv keep them nice and safe. PIP, or Python Package Installer, allows you to install all types of python-related software (and code) include Django, Virtual environments (virtualenv), Python Requests, and more. After Django's depedencies installed (such as Python and its environment), now we can install the latest version of Django. We're also gonna be using getbootstrap.com as our front and web framework, it's gonna make our site look good. It's not like a regular or something like that. It's just really so much of its about HTML and CSS. It is fundamental for web development to familiar with HTML and CSS (specifically for web design). Building a website without coding is impossible. So we shouldn't forget about text editor such as Sublime Text, VSC, and more. Use it up to your personal preference. # Create a Blank Django Project 1. Install Python Tools and Environment ``` $ sudo apt-get update $ sudo apt-get upgrade $ sudo apt-get install python-pip python-virtualenv python-setuptools python-dev build-essential python3.6 $ sudo pip install virtualenv ``` 2. Create a directory for General Project Storage ``` $ mkdir <dirname> && cd <dirname> ``` 3. Install & Create a Virtual Environment ``` $ mkdir <envname> && cd <envname> $ virtualenv -p python3 . ``` 4. Activate The Environment ``` $ source bin/activate ``` If activated correctly, you should see: ``` (<envname>) $ ``` Check Python version: ``` (<envname>) $ python --version ``` To deactivate: ``` (<envname>) $ deactivate ``` To reactivate: ``` $ /path/to/your/virtual/env/ $ source bin/activate ``` 5. Install Django ``` (<envname>) $ pip install django (<envname>) $ mkdir src && cd src # source directory (<envname>) $ django-admin startproject <projectname> . ``` 6. Create New Settings Module ``` (<envname>) $ cd <projectname> (<envname>) $ mv settings.py old_settings.py # renaming settings.py (<envname>) $ mkdir settings ``` 6a. Local Settings ``` (<envname>) $ cp old_settings.py ./settings/base.py (<envname>) $ cd settings (<envname>) $ touch __init__.py (<envname>) $ cp base.py production.py (<envname>) $ cp base.py local.py ``` - Init.py Setting up **init__.py** within new Settings folder to make it a module. Add the following: ``` from .base import * # from base import all from .production import * try: from .local import * except: pass ``` - Local.py It's for local computer so if we actually sent this code somewhere else we might have another local file and therefore would need to update the **__init__.py**. - Base.py We can make sure that base is kind of our always local development settings. It always be there, inside new Settings folder. - Production.py It overrides all base settings. Setting up **production.py** as follows: In **DEBUG** section, change the debug setting from **True** to **False** ``` DEBUG = False ``` **NOTES**: You might also want to change your secret key in the **SECRET_KEY** section. 6b. Live Settings ``` (<envname>) $ cd .. # exit from <projectname> directory (<envname>) $ pip install psycopg2 # for a postgresql database (<envname>) $ pip install gunicorn dj-database-url # for use on heroku (<envname>) $ pip install django-crispy forms (<envname>) $ pip install pillow (<envname>) $ pip freeze > requirements.txt ``` Inside **requirements.txt**, it should've contain common files that are installed inside the virtual environment. 7. Database Migrations ``` (<envname>) $ cd .. # exit from <projectname> directory (<envname>) $ python manage.py migrate ``` It ensures that our database is up to date with the changes defined in our Django models. It also saved a bunch of data in the database. 8. Create Super User ``` python manage.py createsuperuser ``` Create an admin (superuser) account for the Django admin panel. If created correctly, you should see: ``` Superuser created successfully. ``` 9. Start the Django Development Server ``` python manage.py runserver ``` 10. Access the Development Server Access the server we just started by going to the localhost where the server is hosted. Access it in web browser. If you don't know where to find the localhost, usually you can check the result after starting the server as follows: ``` Starting development server at http://127.0.0.1:8000/ ``` ![WhatsApp Image 2025-03-11 at 00.22.28_375af802](https://hackmd.io/_uploads/rJ6sa3zhyx.jpg) So what we're doing is emulating a production server. Development server is local server while production server is when we go live. # Create Web Applications **NOTES**: We're gonna build a web that is about restaurants. **I. Menu Listings** **A. Understand How Django Return Responses** 1. Create/Start an Application ``` $ cd /path/to/general project storage/virtual environment/ $ source bin/activate # activate Python virtual environment (<envname>) $ cd /path/to/source/ (<envname>) $ python manage.py startapp <appname> # we're gonna use "restaurants" ``` 2. Create a Function Based View Create a fuction where its gonna handles a request (example: url) and return some sort of response. Edit the **views.py** inside the **restaurants** as follows: ``` from django.http import HttpResponse from django.shortcuts import render # shortcuts import render def home(request): return HttpResponse("hello") # will return the response with "hello" as a string # return render(request, "home.html", {}) # render takes 3 arguments (request, template name, context) ``` 3. Import the View Set up a view to handle the url. Edit the **urls.py** inside the **new settings** folder of the **<projectname>** as follows: ``` from django.conf.urls import url from django.contrib import admin from restaurants.views import home urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^$', home), ] ``` After edited it, refresh the server in web browser, see if its change. **NOTES: Don't forget to run the server if you like to see the changes you've make.** **B. Rendering HTML** **NOTES: You can get some HTML source code from view-source:getbootstrap.com** 1. HTML Codes as Return Response Edit the **views.py** inside the **restaurants** as follows: ``` from django.http import HttpResponse from django.shortcuts import render def home(request): html_ = """ <!DOCTYPE html> <html lang=en> <head></head> <body> <h1>Hello World!</h1> <p>This is HTML coming through</p> </body> </html> """ return HttpResponse(html_) # instead of return with "hello" string, it returns with html_ code # return render(request, "home.html", {}) ``` 2. F Strings as Part of HTML Codes It's a little way to do string substitution. Edit the **views.py** inside the **restaurants** as follows: ``` from django.http import HttpResponse from django.shortcuts import render def home(request): html_var = 'f strings' html_ = f""" <!DOCTYPE html> <html lang=en> <head></head> <body> <h1>Hello World!</h1> <p>This is {html_var} coming through</p> </body> </html> """ return HttpResponse(html_) # return render(request, "home.html", {}) ``` **C. Render a Template** We're gonna create a templates folder and then use it to storage our templates. ``` (<envname>) $ cd /path/to/source/ (<envname>) $ mkdir templates && cd templates # we're gonna use it as storage for our templates (<envname>) $ touch base.html # use it as a html base ``` Copy the html codes inside **views.py** inside the **restaurants** directory and paste it to **base.html** inside **templates** directory. Open up **base.py** inside /path/to/general/venv/src/projectname/settings/ and search for **TEMPLATES**: ``` TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ] } } ] ``` The **TEMPLATE** settings is how templates are being rendered. In 'DIRS' add the **templates** folder: ``` 'DIRS': [os.path.join(BASE_DIR, 'templates')], # path to templates ``` Setting up the **views.py** inside the **restaurants** as follows: ``` from django.http import HttpResponse from django.shortcuts import render def home(request): return render(request, "base.html", {}) ``` So we're gonna return render (to rendering out templates), this time it's gonna return the **base.html**. Run the server, it should shows that it has some templates error, so we're gonna setting up** __init__.py** inside the new **Settings** directory as follows: ``` from .base import * ``` Setting up the **views.py** inside the **restaurants** as follows: ``` from django.http import HttpResponse from django.shortcuts import render def home_old(request): html_var = 'f strings' html_ = f""" <!DOCTYPE html> <html lang=en> <head></head> <body> <h1>Hello World!</h1> <p>This is {html_var} coming through</p> </body> </html> """ return HttpResponse(html_) def home(request): return render(request, "base.html", {}) ``` We're gonna return the html code and base template. Setting up **base.html** inside **templates** folder as follows: ``` <!DOCTYPE html> <html lang=en> <head></head> <body> <h1>Hello World!</h1> <p>This is {{ html_var }} coming through</p> # {{ }} is called context variable </body> </html> ``` Back to **views.py**: ``` from django.http import HttpResponse from django.shortcuts import render def home_old(request): html_var = 'f strings' html_ = f""" <!DOCTYPE html> <html lang=en> <head></head> <body> <h1>Hello World!</h1> <p>This is {html_var} coming through</p> </body> </html> """ return HttpResponse(html_) def home(request): return render(request, "base.html", {"html_var": True}) ``` What it does is the context variable inside **base.html** is replaced by context inside **views.py**. Again, **views.py**: ``` import random from django.http import HttpResponse from django.shortcuts import render # shortcuts import render def home_old(request): html_var = 'f strings' html_ = f""" <!DOCTYPE html> <html lang=en> <head></head> <body> <h1>Hello World!</h1> <p>This is {html_var} coming through</p> </body> </html> """ return HttpResponse(html_) # instead of return with "hello" string, it returns with html_ code def home(request): num = random.randint(0, 999) return render(request, "base.html", {"html_var": True, "num": num}) # render takes 3 arguments (request, template name, context) ``` Back to **base.html** ``` <!DOCTYPE html> <html lang=en> <head></head> <body> <h1>Hello World!</h1> <p>This is {{ html_var }} coming through</p> <p>Random number is {{ num }}</p> </body> </html> ``` Back to **views.py**: ``` import random from django.http import HttpResponse from django.shortcuts import render # shortcuts import render def home_old(request): html_var = 'f strings' num = random.randint(0, 999) html_ = f""" <!DOCTYPE html> <html lang=en> <head></head> <body> <h1>Hello World!</h1> <p>This is {html_var} coming through</p> <p>This is a random rumber: {num}</p> </body> </html> """ return HttpResponse(html_) def home(request): num = random.randint(0, 999) return render(request, "base.html", {"html_var": True, "num": num}) ``` What it does is input a random generate number to the base template. **D. Adjust Context** We're gonna delete the **home_old** inside the **views.py** since we're not gonna use it anymore. ``` import random from django.http import HttpResponse from django.shortcuts import render # shortcuts import render def home(request): num = random.randint(0, 999) return render(request, "base.html", {"html_var": True, "num": num}) ``` - Make more context variable. 1. Conditional Context Add this to paragraph section inside **base.html**: ``` {% if bool_item %} Random number is {{ num }} {% endif %} ``` So if it's boolean item then it's gonna renew the number. We're gonna change the **html_var** context to **bool_item** inside **views.py**. ``` return render(request, "base.html", {"bool_item": True, "num": num}) ``` What it does its gonna render random number because it sets to **True**. 2. Looping Context Add this to paragraph section inside **base.html**: ``` {% for some_itme in some_list %} {{ some_item }} {% endfor %} Some item is {{ some_item }} ``` **views.py**; Add this to **home** function: ``` some_list = [num, random.randint(0, 999), random.randint(0, 999)] context = { "bool_item": True, "num": num, "some_list": some_list } return render(request, "base.html", context) ``` 3. Template filter - Divisibleby **base.html**; paragraph section ``` {% for some_itme in some_list %} {% if some_item | divisibleby: "2" %} Even number {% endif %} {{ some_item }} {% endfor %} Some item is {{ some_item }} ``` If the number can be divide by 2, its gonna print that number **Even**. 4. Template tag - Verbatim **base.html**; paragraph section ``` This is <code>{% verbatim %}{{ html_var }}{% endverbatim %}</code> coming through ``` Verbating is showing us the actual template itself, the raw template instead of rendering the context/template. - Block Content **base.html**; paragraph section ``` {% block content %} {% endblock content %} {{ block.super }} ``` Holding the content. - Extends **home.html**; paragraph section ``` {% extends "base.html" %} ``` It'll pull **base.html**. **E. Template Inheritance** If you add more fuction in **views.py** (such as home, home1, home2, etc), you also wanna add that function to **urls.py** inside **<projectname>** folder so it'll render the function. Since you add more function, it also necessary to create a template with the same function name inside the **templates** folder. There's a philosophy behind code and software, it called DRY - Don't Repeat Yourself. What it means is we don't want to repeat ourself or we want to limit the amount of repeat ourself as much as possible. So if any CSS or any other sorts of things that are related to the meta data we would have some issue. That's where **template inheritance** shines. **F. Include Template Tags** 1. Create a **snippets** folder inside new **Settings** folder. ``` (<envname>) $ cd /path/to/settings/ (<envname>) $ mkdir snippets && cd snippets ``` 2. Inside it, create these: ``` (<envname>) $ touch nav.html # will be use as navigation in the website (<envname>) $ touch css.html # to style our website (<envname>) $ touch js.html # Javascript (<envname>) $ touch sidebar.html ``` 3. Inside the base.html, include those files ``` {% include 'snippets/css.html' %} {% include 'snippets/nav.html' %} {% include 'snippets/js.html' %} ``` **F. Class Based Views** Eliminate repeating ourself. **1. Based Views** - View **views.py** ``` from django.views import View class ContactView(View): def get(self, request, *args, **kwargs): context = {} return render(request, "contact.html", context) ``` **urls.py** Don't forget to add **ContactView** to **urls.py** inside your **<projectname>** folder ``` from ContactView urlpatterns = [ url(r'^contact/$', ContactView.as_view()), # callable function based view ] ``` - Template View** **views.py*** Adding class based views: ``` from django.views.generic import TemplateView class HomeView(TemplateView): template_name = 'home.html' def get_context_data(self, *args, **kwargs): # overriding method context = super(HomeView, self).get_context_data(*args, **kwargs) num = None some_list = [ random.randint(0, 999) random.randint(0, 999) random.randint(0, 999) ] condition_bool_item = True if condition_bool_item: num = random.randint(0, 999) context = { "num": num, "some_list": some_list } return context class AboutView(TemplateView): template_name = 'about.html' class ContactView(TemplateView): template_name = 'contact.html' ``` Overriding the method that's inside of the template view. Get rid of the original function based views: ``` def home(request): num = None some_list = [ random.randint(0, 999), random.randint(0, 999), random.randint(0, 999) ] condition_bool_item = True if condition_bool_item: num = random.randint(0, 999) context = { "num": num, "some_list": some_list } return render(request, "home.html", context) def about(request): context = { } return render(request, "about.html", context) def contact(request): context = { } return render(request, "contact.html", context) ``` **urls.py** ``` from restaurant.views import HomeView, AboutView, ContactView urlpatterns = [ url(r'^$', HomeView.as_view()), url(r'^about/$', AboutView.as_view()), url(r'^contact/$', ContactView.as_view()), ] ``` There's also a way to make all of those more efficient. Copy these from **views.py** to **urls.py**: ``` from django.views.generic import TemplateView ``` Delete these from **views.py**: ``` class AboutView(TemplateView): template_name = 'about.html' class ContactView(TemplateView): template_name = 'contact.html' ``` So make sure these are inside **urls.py**. ``` from restaurant.views import HomeView urlpatterns = [ url(r'^$', HomeView.as_view()), url(r'^about/$', TemplateView.as_view(template_name='about.html')), url(r'^contact/$', TemplateView.as_view(template_name='contact.html')), ] ``` # Remembering Things with Models Let's try to create another superuser (admin) ``` (<envname>) $ python manage.py createsuperuser ``` Run the server, then go to Django admin at http://127.0.0.1:8000/admin. Login as username and password you just created. Open up **base.py** and search for these: ``` INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] ``` This is what and where did the data came from. Next up, we're gonna create models for our restaurants. Open up **models.py** inside **restaurants** folder. Make sure these commands are inside. ``` from django.db import models class Restaurtant(models.Model): name = models.CharField(max_length=120) ``` Then open up **base.py** again. Put this in **INSTALLED_APPS** section. ``` 'restaurants', ``` Make migrations ``` (<envname>) $ python manage.py makemigrations (<envname>) $ python manage.py migrate ``` What it does is it tells the database about the model and makes the database fit with the model so they work in conjunction with each other. Now we're gonna add another field to **models.py**. ``` location = models.CharField(max_length=120, null=True, blank=True) ``` Migrate/Add it to database. ``` (<envname>) $ python manage.py makemigrations (<envname>) $ python manage.py migrate ``` Whenever you make changes, you're gonna run **makemigrations** and then **migrate**. Now, let's bring it into the admin so we can actually take a look at it. Open up **admin.py** inside of **restaurants**. Add these lines: ``` from .models import Restaurant admin.site.register(Restaurant) ``` So what it does is it imports the model itself so Restaurant model (created in models.py) has been added into the admin. **More on Model Fields** Let's add another field to **models.py** ``` category = models.CharField(max_length=120, null=True, blank=True) ``` max_length = 120; maxiumum length of 120 characters (include spaces, any sort of alphabetical chars or numbs). null = True; empty in the database. blank = True; we can leave a blank in our built-in Django forms or even in the admin. If it is set to False, we cannot leave a blank in the built-in Django forms. You also might add both of these fields to **models.py** ``` timestamp = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) ``` **NOTES: If we run **python manage.py makemigrations** without changing anything inside **models.py**, it'll tell us **No changes detected**.** You can also check docs.djangoproject.com/en/1.11/ref/models/fields/ for more references about model fields. **Displaying Saved Data** We're gonna see locations that are stored in the database. **Function Based View** 1. Define the view inside **views.py** inside restaurants folder ``` def restaurant_listview(request): template_name = '' context = {} return render(render, template_name, context) ``` 2. Create a templates folder inside restaurants, then create restaurants folder inside newly made templates folder. Then create a file **restaurants_list.html** **Generic/Class Based View** II. Accounts III. Videos IV. Analytics