Terminal crash course
-
Where did the terminal come from?
The terminal originates from around the 1950s-60s and its original form really doesn’t resemble what we use today. Since then, the terminal has remained a constant feature of all operating systems. It provides direct access to the computer’s underlying file system and low-level features, and is therefore incredibly useful for performing complex tasks rapidly, if you know what you are doing.It is also useful for automation — for example to write a command to update the titles of hundreds of files instantly, say from “ch01-xxxx.png” to “ch02-xxxx.png”. If you updated the file names using your finder or explorer GUI app, it would take you a long time. -
What does the terminal look like?
- In Windows: "cmd" & "powershell"
- In MacOS: "terminal"
-
How do you access the terminal?
- In Linux: Linux/Unix systems have a terminal available by default, listed among your
Applications
. - In MacOS: macOS has a system called Darwin that sits underneath the graphical user interface. Darwin is the Unix-like system, which provides the terminal, and access to the low-level tools. The terminal is available on macOS at
Applications/Utilities/Terminal
. - In Windows:
CMD
or better known as "command prompt" doesn't have parity with Unix commands, and is equivalent of aDOS prompt
. Better programs exist for providing a terminal experience on Windows, such asPowershell
, andGitbash
. However, the best option for Windows in the modern day is theWindows Subsystem for Linux (WSL)
— a compatibility layer for running Linux operating systems directly from inside Windows 10, allowing you to run a “true terminal” directly on Windows, without needing a virtual machine. - Side note: what's the difference between a command line and a terminal? [Refer website]
- In Linux: Linux/Unix systems have a terminal available by default, listed among your
-
Basic built-in terminal commands:
- Navigate your computer’s file system along with base level tasks such as create, copy, rename and delete:
- Move around your directory structure:
cd
- Create directories:
mkdir
- Create files (and modify their metadata):
touch
- Copy files:
cp
- Move files:
mv
- Delete files or directories:
rm
- Move around your directory structure:
- Download files found at specific URLs:
curl
- Search for fragments of text inside larger bodies of text:
grep
- View a file's contents page by page: less, cat
Manipulate and transform streams of text (for example changing all the instances of
<div>
s in an HTML file to<article>
):awk
,tr
,sed
- Navigate your computer’s file system along with base level tasks such as create, copy, rename and delete:
-
Tab
key comes in handy as an auto-complete feature -
Relative vs Absolute Paths
-
dir
in Windows equivalent ofls
in Linux -
To find out exactly what options each command has available, you can look at its
man page
. This is done by typing theman
command, followed by the name of the command you want to look up, for exampleman ls
. -
To run a command with multiple options at the same time, you can usually put them all in a single string after the dash character, for example
ls -lah
, orls -ltrh
-
rmdir -r
would delete the directory and its contents recursively. Just be sure before deleting. -
mv
for moving and renaming. -
Many terminal commands allow you to use asterisks as "wild card" characters, meaning "any sequence of characters". This allows you to run an operation against a potentially large number of files at once, all of which match the specified pattern.
rm mdn-*.bak
would delete all files that start withmdn-
and end with.bak
.🤑 Why does Windows use backslash instead of forward slash: Digital Equipment Corporation made a design choice to use forward slash for cmd line switches which was carried forward to CPM, then to MS DOS 1.0, but in MS DOS 2.0 Directories came into existence, so people thought it would be nice idea to use back slash as directory separator. (and everything was okay, but since devs wanted forward slash as directory separator, the switches went from forward slash to hyphen)
-
❤️ tl;dr is simplified version of
man
pages -
Connecting commands together with pipes: Using pipes take the output of the first command's execution and pass it as input onto the next command after the pipe. A general philosophy of (unix) command line tools is that they print text to the terminal (also referred to "printing to standard output" or
STDOUT
). A good deal of commands can also read content from streamed input (known as "standard input" orSTDIN
). Thepipe operator
canconnect
these inputs and outputs together, allowing us to build up increasingly more complex operations to suit our needs — the output from one command can become the input to the next command. -
Adding powerups: The vast ecosystem of installable tools for front end web development currently exists mostly inside
npm
, a privately owned, package hosting service that works closely together with Node.js. This is slowly expanding — you can expect to see more package providers as time goes on. InstallingNode.js
also installs thenpm
command line tool (and a supplementary npm-centric tool callednpx
), which offers a gateway to installing additional command line tools. Node.js and npm work the same across all systems: macOS, Windows, and Linux. -
Installing CLI Tools
-
Version control systems are vital to almost every development workflow.
-
Git is one of the most widely used version control system in the world, it is developed by the same guy who built the Linux operating system kernel (Linus Torvarlds) and it is an essential prerequisite for almost all software development related jobs out there.
-
Prerequisites for Github: Creating an account on the github platform
-
Collaborative Development with Github: Working on the local directory, staging area and remote repository
-
More comprehensive tutorial at Git-it
- Git is open source software (free for anyone to use) written by Linus Torvalds who also wrote the Linux operating system. It is a program for keeping track of changes over time, known in programming as version control.
- Dollar signs $ are often used in programming documentation to signify that the line is command line code (see the code snippets above). You don't actually type the $ in though, only type what comes after. For instance, to run the verify command above, you're actually going to type git-it verify into terminal.
- Summary of the commonly used commands:
# Chapter 1 - Get git : Installing git on your local machine # Chapter 2 - Repository # Make a new folder (aka directory) $ mkdir <FOLDERNAME> # Navigate into an existing folder (aka change directory) $ cd <FOLDERNAME> # List the items in a folder $ ls # Turn Git on for a folder $ git init # Chapter 3 - Commit to it # Check status of changes to a repository $ git status # View changes to files $ git diff # Add a file's changes to be committed [into staging area] $ git add <FILENAME> # To add all files changes [into staging area] $ git add . # To commit (aka save) the changes you've added with a short message describing the changes $ git commit -m "<your commit message>" # Chapter 4 - GitHubbin # A common error is not having your GitHub username match the case of the one you set with git config. For instance, 'JLord' isn't the same as 'jlord'. To change your username set with Git, just do the same command you did earlier, but with the correct capitalization: $ git config --global user.username <USerNamE> # Chapter 5 - Remote Control #Add remote connections $ git remote add <REMOTENAME> <URL> # Set a URL to a remote $ git remote set-url <REMOTENAME> <URL> # Pull in changes $ git pull <REMOTENAME> <BRANCHNAME> # View remote connections $ git remote -v # Push changes $ git push <REMOTENAME> <BRANCH> # Chapter 6 - Forks and clones # After you FORK the ORIGINALREPO [upstream] into your account, you willl get a URLFROMGITHUB [origin], you can clone this local repository $ git clone <URLFROMGITHUB> $ git remote add upstream <ORIGINALREPO> # Chapter 7 - Branches aren't just for birds # You can create and switch to a branch in one line: $ git checkout -b <BRANCHNAME> # Create a new branch: $ git branch <BRANCHNAME> # Move onto a branch: $ git checkout <BRANCHNAME> # List the branches: $ git branch # Rename a branch you're currently on: $ git branch -m <NEWBRANCHNAME> # Verify what branch you're working on $ git status # Chapter 8 - It's A Small World : Adding Colloborators to the repository using GitHub settings # Chapter 9 - Pull Never Out Of Date # Check Git status $ git status # Pull in changes from a remote branch $ git pull <REMOTENAME> <REMOTEBRANCH> # See changes to the remote before you pull in $ git fetch --dry-run # Chapter 10 - Requesting You Pull Please : Creating pull requests using the GitHUb interface to the ORIGINALREPO # Chapter 11 - Merge Tada # Merge a branch into current branch $ git merge <BRANCHNAME> # Change the branch you're working on $ git checkout <BRANCHNAME> # Delete a local branch $ git branch -d <BRANCHNAME> # Delete a remote branch $ git push <REMOTENAME> --delete <BRANCHNAME> # Pull from a remote branch $ git pull <REMOTENAME> <BRANCHNAME>
- Chapter 5 - More on Remote control
- Chapter 6 - More on Forks and clones
- Setting up developer environment:
VSCode
andPython
- Running python code:
- Running python shell (Python Interpreter): Based out of Read–eval–print loop
- Executing python files
- Autoformatting used: Black, instructed to install
Python is an interpreted, object-oriented, high-level programming language. Python is easy to get started with and is powerful enough to run large web applications.
A program is a collection of statements that performs a specific task. or in simple words, it is an abstraction that takes some input and processes it to produce some output.
- Variable names should:
- be in snake_case
- not start with a number
- not be a reserved keyword
number = 5 # int
ratio = 1.5 # float
salutation = "Hello" # string
is_even = True # boolean
- The process of converting one data type to another is called type conversion.
type(<variable>)
: Returns the type of thevariable
- Implicit type conversions are done automatically by Python
- Explicit type conversions are done manually in code
int()
,float()
,str()
,bool()
converts a given data type into the respective function's datatype- Note:
- that converting a float to an int will truncate the decimal part of the number (Flooring the number)
- trying to convert a non-numeric string to an int will result in an exception
some_string = "hello world, This is A test"
print(some_string.capitalize()) # Capitalizes the first character
print(some_string.count("a")) # Counts the number of times the character "a" occurs in the string
print(some_string.endswith("test")) # Returns True if the string ends with the substring "test"
print(some_string.find("gem")) # Returns the index of the first occurrence of the substring "gem" and -1 if it does not exist.
print(some_string.index("test")) # Returns the index of the first occurrence of the substring "test" and raises an exception if it does not exist.
print(some_string.upper()) # Returns a copy of the string in uppercase
print(some_string.title()) # Returns a copy of the string in title case
print(some_string.swapcase()) # Returns a copy of the string in swapcase
- Note: A neat little hack in Python to see all the functions available is to type
dir()
and see what functions are available. - F strings:
F strings
are a relatively new addition to Python (since3.6
), they are a way to format stringssome_variable = "john" print(f"hello {some_variable}") # prints hello john
Python programs get structured through indentation, i.e. code blocks are defined by their indentation. it's a language requirement, not a matter of style.
- Functions in Python are a group of statements (code block) that may or may not take inputs and may or may not return values.
- At times, functions don't return any value, then they assume return type of
None
- To create functions without any statements in them use the
pass
statement
def greet(name, greeting = "Good Morning"): # this defines a function called greet with two parameters called name and greeting
return greeting + " " + name
print(greet("John")) # prints "Good Morning John"
print(greet("Jane", "Good Afternoon")) # prints "Good Afternoon Jane"
print(greet(name="John")) # we can also pass parameters to the function by name, that way we can change the order of the parameters
- A very simple if statement, has only one condition, if the condition evaluates as
True
or1
, then the body of the if statement is executed, if not the body of the else is executed.
(2 > 1) and print("2 is greater than 1") # prints 2 is greater than 1
(1 > 2) and print("1 is greater than 2") # prints nothing
- A list is a collection of items. These items can be of any data type. In Python, lists are very flexible as they allow to add/remove items if needed. Lists are mutable as in you can change the value of an item in a list.
- Lists are indexed starting from
0
- Lists can be sliced (create a new sublist from the list) like this
list_variable[start:end]
, note that thestart
is included and theend
is excluded - Lists can also be
negatively
indexed - đź’ˇ: To interpret negative indices as positive, just convert to positive by adding given -ve index to length of list
list_with_numbers = [1, 2, 3, 4, 5]
print(list_with_numbers[0]) # prints 1
print(list_with_numbers[-1]) # prints 5
print(list_with_numbers[-3:]) # prints [3, 4, 5] (the last three items with the start included)
list_with_numbers.append(6) # adds 6 to the end of the list
list_with_numbers.insert(2, 7) # inserts 7 at index 2
list_with_numbers.remove(3) # removes the first 3 from the list ** Based on the value not the index
list_with_numbers.pop() # removes the last item from the list
list_with_numbers.pop(0) # removes the first item from the list
list_with_numbers.reverse() # reverses the order of the list, note that this function does not return anything
list_with_numbers.sort() # sorts the list , note that this function does not return anything
list_with_numbers.sort(reverse=True) # sorts the list in reverse order
some_string = "This is a test"
list_of_words = some_string.split(" ") # The argument to the split function is used to split the words by.
print(list_of_words) # prints ['This', 'is', 'a', 'test']
list_of_words = ["This", "is", "a", "test"]
joined_string = "-".join(list_of_words) # The argument to the join function is the list of words to be joined, "-" is the string that will be used to concatenate the list items.
print(joined_string) # prints "This-is-a-test"
- Given a list, we can define a function and have Python call the function with every item in the list. This is performed using the
map function
. Syntax:map(<function>, <list>)
, returns a map, we must explicitly convert to list to use - Python comes with an immutable version of lists called
tuple
. Tuples do not support changes once initialized.
- A dictionary in Python is simply a collection of key-value pairs.
contact_details = {
"John": "1234567890",
"Jane": "0987654321",
}
print(contact_details.keys()) # prints ["John", "Jane"]
print(contact_details.values()) # prints ["1234567890", "0987654321"]
print(contact_details.get("Jane")) # prints 0987654321
print(contact_details.get("Mary", "Not found")) # prints Not found
Iterative statements are used to execute a block of code multiple times, usually, until a given condition has been reached.
-
while
loop: In case you do end up in an infinite loop usecontrol + c
key combination to stop the program execution. We can alsobreak
to come out of a loop at any point. Similarly, we can also cause the loop to skip over some code by using thecontinue
statement.i= 10 while True: i = i - 1 # decrement i by 1 if i == 5: continue if i == 0: break print(i)
-
for
loop: Thefor
loop is used to iterate over a sequence (lists, dictionaries, string, or anything iterable).range(start, stop, step)
can be used to build a list of numbers to be iterated over.
Advanced Reading : Python has a concept of iterators and generators.
Iterators
are objects that are used to iterate over a sequence.Generators
are functions that return iterators. They are a bit hard to understand at first but once you get them you will understand them very well. You can find a full list of iterators and generators in the Python documentation.
- Errors are called Exceptions in the Python world
- The
try
block is the block that we want to execute, theexcept
block is the code that we want to execute if an error occurs. - Note that the statements after the error in the try block are skipped. Once an error occurs the rest of the try block is skipped and the except block is executed immediately.
- We can also add generic exception handling by using the
except
keyword without specifying the exception type. This will handle all the exceptions that are not handled by the otherexcept
blocks. We can also add afinally
block to theexcept
block to execute code after thetry
block has been executed. Thefinally
block is executed whether an error occurs or not - Exceptions in Python can also be raised by the programmer, this is done using the
raise
statement. This is useful when we want to force the execution to move to the except block.
try:
numerator = 10
denominator = 0 # Change this to see different execution flows
if denominator > 100:
raise ValueError("Denominator cannot be greater than 100")
print("Answer is" , numerator/denominator)
except ZeroDivisionError:
print("You cannot divide by zero")
except ValueError:
print("Value Error! Denominator Must be < 100") # Catches the Value Error
except:
print("Something Happened")
finally:
print("All operations have been completed")
A function that calls itself and terminated by a base case
.
def countdown(number):
if number < 0:
return
print(number)
countdown(number - 1)
countdown(10)
- In Python, everything is an object. You can think of an object as something that holds a collection of data and methods that act on those data. The objects are created from classes.
- Classes as blueprints for creating objects AND objects as something that was created with those blueprints
Unorganized
code (also known asspaghetti
code) Note thatclass
names are always inUpperCamelCase
class Person: # This syntax creates a new class called Person
name = "Person" # This is a class attribute.
# Note that the data and methods of a class are defined in the class block
john = Person() # This is the syntax used to convert a class into an objects
# Converting a class into an object is called instantiation, We create an instance of the class as an object
print(john.name) # Prints out Person
# We can access by using the dot notation : <obj_variable>.<class_variable>
-
Constructors
are used to initialize an object. Constructors can receive arguments and change attributes of the object during initialization. The constructor in Python is called__init__
class Person: # Create a new Class Person name = "Person" # Default Attributes salutation = "Hello " def __init__(self , name):# Note that name is both a parameter and a class attribute self.name = name # self.name refers to the class attribute and the other one refers to the parameter def greet(self): # Create another class method that returns a greeting return self.salutation + self.name # self is the object on which the method is called john = Person("John Doe") print(john.greet()) # Prints Hello John Doe jane = Person("Jane Doe") print(jane.greet()) # Prints Hello Jane Doe
Notice that the first argument in the constructor was
self
, this is not an argument that we pass onto the constructor, it refers to the object that has called the method, so if you wanted to access another attribute of the object you can use theself.<variable_name>
-
In Python, constructor methods are called magic methods or dunder methods. They are prefixed and suffixed with two underscores.
- Inheritance is a way to create new classes from existing classes.
- This is done by using the class keyword followed by the name of the new class and the name of the existing class. The new class inherits all the methods and attributes of the existing class. The new class can also have its own methods and attributes. The new class is called a subclass while the existing class is called a superclass. The subclass can also override attributes and methods from a superclass (create its own versions of attributes and methods).
- Classes can also override methods in their parents, this is used to redefine the behavior of a method.
- Python also supports multiple inheritance. This is used when we want to inherit from multiple classes.
- Each Python object has a method resolution order (MRO) which is a list of classes that are used to resolve the method. The MRO is used to determine which class to call when a method is called. Let's say that each of the inherited classes has the same function, to identify which method is called we use the MRO.
print(ab.mro()) # [<class '__main__.AB'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
# The MRO is [AB, A, B, object] so the AB class is checked first then the A class and then the B class.
-Mixin: When developing applications you might come across common functionality that is shared between a number of classes, in Python each feature can be built into different classes and then combined together to create a new class with the functionality required. Each of the feature classes is called a Mixin. Mixins are extremely useful in web development to add new features to existing classes.
class RunnableMixin:
def run(self):
print("Can Run")
class WalkableMixin:
def walk(self):
print("Can Walk")
class TalkingMixin:
def talk(self):
print("Can Talk")
class Animal(RunnableMixin, WalkableMixin):
pass
class Human(RunnableMixin, WalkableMixin, TalkingMixin):
pass
Encapsulation is the process of wrapping the data
and code
together to prevent data from being accessed by other parts of the program. This is done so that we don't manually change the attributes of the object, which could cause unexpected results.
-
Protected members can only be accessed by the class itself or its subclasses. Protected members are prefixed with an underscore (
_
). Protected members are only a convention, and they are not enforced by Python. You can directly change the_variable
value. -
Python also has a concept of private members, which are members that can only be accessed by the class itself, even base classes cannot access these members. Private members are prefixed with two underscores (
__
). Private members are also by convention only since these members can be accessed in the following format:object._<class_name><private_member_name>
or in our casejohn._Person__name
class Person: __name = "Person" # This is a private member (Prefixed by double underscores) def __init__(self, name): self.__name = name def greet(self): print("Hello, my name is " + self.__name) john = Person("John") john.__name = "Jane" # Creates a new attribute but does not edit the existing __name attribute john.greet() # Hello, my name is John
Name mangling: Convention School of thought
- Abstract Classes: An Abstract class is a class that contains blueprints for other classes to inherit from. An Abstract class does not contain any implementation but it defines a common interface for its subclasses.
Python Operators like +
, -
can be overloaded to perform different operations based on what object is being operated on. The list of operators that we can override are defined using the dunder methods.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # Overloading the `+` operator
return Point(self.x + other.x, self.y + other.y)
# self is the object on the lhs and other is the object on the rhs
def __sub__(self, other): # Overloading the `-` operator
return Point(self.x - other.x, self.y - other.y)
# This is called when the object is printed or converted to a string
def __str__(self): # Overloading the `str` operator
return f"({self.x}, {self.y})"
point_a = Point(1, 2)
point_b = Point(3, 4)
print(point_a + point_b) # Output: (4, 6)
print(point_a - point_b) # Output: (-2, -2)
When browsing the web you might see pages that don't change no matter who views them or where you view them from, these are called static web pages
. Most of the pages you visit will be dynamic
, these are pages that are generated just for you, they are newly generated just for you by servers (specifically, web servers).
The frontend
is the part of the web that is seen by the user, they are also called client-side
. Backend
is the part of the web that is not seen by the user, they are also called server-side
. The backend contains the logic that makes your application work. Splitting the app into different parts makes it easier to manage and update. It also allows developers to create different versions of the application like a mobile version, a desktop version, a version for a tablet, etc without much hassle.
- In modern applications, the Front End and Back End are developed and maintained separately, They often use APIs to communicate with each other, we talked about backend and frontend, the frontend usually communicates with the backend using
HTTP requests
andresponses
. - JSON Stands for JavaScript Object Notation, it was based on a subset of the Javascript Programming language. Even though JSON is based on Javascript, it is a language-independent data format, as in it can be used in any programming language.
- The reason why we have standard interchange formats like JSON is that HTTP only allows transferring plaintext data, Plaintext data does not have any kind of structuring, it is just a bunch of characters. Formats like JSON or XML bring formatting and structure to the data, which makes it easier for us developers and machines to understand it better.
- API's are not just used for data interchange between the frontend and the backend, it can be used for machine communication (backend to backend) as well.
Note: Web 1.0 - read-only version of the internet:
- Web 1.0 is the term used for the earliest version of the Internet as it emerged from its origins with Defense Advanced Research Projects Agency (DARPA) and became, for the first time, a global network representing the future of digital communications.
- mostly composed of web pages joined by hyperlinks, without the additional visuals, controls and forms
- passive, and much of the user input took place offline
- 🤫 Web users now may find it shocking that at the time of Web 1.0, running advertisements was banned.
- In fact, it's interesting that even years later when full functionality is delivered over the web, many corporate functions and user experiences are instead provided through mobile applications on operating systems iOS and Android, rather than through an Internet browser. The browser can deliver nearly any sort of function that an app can provide; in fact, on modern smartphones, it’s a user preference to either access a social media platform or other tool from an app, or through the web.
- Using Forms and Inputs the user can interact with the webpage and send data back to the server. This data can then be used to create dynamic web pages, In dynamic web pages, the pages are generated on the fly by a web server.
- The form tag has two important attributes that are used to send back data to the server, the
action
attribute specifies the URL that the data will be sent to, and themethod
attribute specifies the method that will be used to send the data. - There are two main methods that we use to send data to the server,
GET
andPOST
.- GET is used to send data encoded into the URL, and POST is used to send data to the server in the body of the request.
- Ultimately both of them achieve the same goal, POST over HTTPS is the most secure method. GET is used when the data is not sensitive.
- GET is used when you don't want any side effects and are ready to expose some insensitive data, and POST for vice-versa.
python3 -m http.server 8000
-m
refers to the module/library as opposed to the traditional single file execution, here,http.server
module8000
refers to the port number where the static server is spun up- This small basic server is able to comprehend files types and render each file accordingly
- Code, Explained: Refer to
Level-03/dynamic-server/server.py
- Status code:
- Informational responses (
100
-199
) - Successful responses (
200
–299
) - Redirection messages (
300
–399
) - Client error responses (
400
–499
) - Server error responses (
500
–599
)
- Informational responses (
- Code, Explained: Refer to
Level-03/dynamic-server/server.py
# print the current path
# as it an attribute of the SimpleHTTPRequestHandler Class
print(self.path)
pip
will allow us to install external libraries with ease. Python packages are usually hosted at PyPI. PyPI is free and anyone can upload their own package into it. Packages from PyPI can be installed using pip.- Python packages are versioned. Versioning allows us to keep track of the changes in the code, this becomes very useful when we are working with large libraries that are updated frequently. We can force pip to install a specific version of the library so that even if newer versions of the library are released, we can still use the old version which works with our project.
- The libraries we use can also have their own dependencies, pip finds out exactly who needs what and installs them correctly.
-
This command lists all the packages that are installed on your system, note that each package is listed with its current version as well. The packages are listed in the following format:
pip freeze
package_name==version
(==
is the separator for the version number). - To install a python package, you can use the following command:
pip install package_name==version pip install package_name>=version # installs a version greater than or equal to the specified version
Virtualenvs
create an isolated environment where you can install packages and experiment freely without affecting the rest of your machine. You can create as many virtualenvs as you want. Another advantage of virtualenvs is that they don't require permissions to install packages, installing global packages usually required you to have root access.- Virtualenv itself is a package that is installed with pip, This command installs the virtualenv package.
-
virtualenv .env # This command will create a new virtual environment under the .env folder, usually where the environment resides source .env/bin/activate # To activate your virtual environment which python # To confirm the virtual environment is active deactivate # To deactivate the virtual environment
- Frameworks are simply a bunch of code that solves a lot of problems you might face when developing web applications, it allows you to focus on the core of your application and not worry about the low level implementations.
- Frameworks also bring structure to your projects, structure helps in reading your code and making it easier to understand.
- Frameworks also help keep our application safe, web attacks change every day and keeping track of them and patching them is a very tedious task, web frameworks like django keep track of each vulnerability and issues a fix as soon as its discovered, which makes the developers life easier.
Django - "The web framework for perfectionists with deadlines!"
Create a virtual environment and pip install django
. Format the interpreter in VS code to point to the binary in the environment. Run python manage.py runserver
to test, if the server is working.
To understand file structure, read docs
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
-
A view function, or view for short, is a Python function that takes a web request and returns a web response.
-
URL Dispatcher: A clean, elegant URL scheme is an important detail in a high-quality web application. Django lets you design URLs however you want, with no framework limitations.
- Templates: Being a web framework, Django needs a convenient way to generate HTML dynamically. The most common approach relies on templates. A template contains the static parts of the desired HTML output as well as some special syntax describing how dynamic content will be inserted.
- Code, Explained: Refer to
Level-04/task_manager/task_manager/urls.py
-
Issues with global variable (volatile, different versions of data on different servers) and text files (inefficient, rewriting large data to update one line per se)
-
You can visualize relational tables as a bunch of columns and rows where columns are called
attributes or fields
and rows are calledrecords
. -
For example, we can have a table called users with the following columns or attributes: name, email, age then the rows would be the actual data that is stored in the table. Like,
("John", "john@gmail.com", 24)
,("Doe", "doe@gmail.com", 18)
and so on. -
Databases also allow us to perform queries on the data, for example, we can ask the database to give us all the users that are older than 18, or all the users that are younger than 18 or any condition you can think of, you can also delete rows, join tables and so on.
-
Databases use
SQL
(Structured Query Language) which is a standardized programming language that's used to interact with the database, raw SQL will not be covered in this course, instead, we will focus onDjango ORM
(Object Relational Mapping) which is a library that allows us to interact with databases using Python.
- DEFINITION: Django loves modularising things, as in converting everything into smaller modules that can be reused, these modules can be published as packages that can be used in different projects. Django calls these small modules
apps
.
Apps should “Do one thing and do it well.” - Unix Philosophies
-
To create an app you can run the command
python manage.py startapp <app_name>
, the app name should always be in lower cases. -
The app we created is not yet linked to the Django project, to link it to the project we need to add the name of our new app to the end of the
INSTALLED_APPS
list in thesettings.py
file.
-
When we created the new app, Django created a bunch of files and a folder for us, among these files, all Database/Schema related code resides in
models.py
-
The predefined table structure is often called a
schema
, it is more or less the blueprint of how the database/table is constructed. -
Django allows us to model schemas by creating classes, in Django, a model is any class that inherits from the
django.db.models.Model
class, if you create a class from this base class then Django maps it into a database table automatically, it then allows us to perform all kinds of operations by calling methods on this class. -
ORM is an Abstraction Layer: It is important to note that Django does not store the data, Django just converts what you want to store/query into a SQL query understandable by the database. The actual storing/querying is done by the database, Django provides this layer of abstraction so that we can create better applications without spending time with the specifics.
-
In all projects, Database Schemas are sacred, even simple changes in them can break the whole application. Because of that, Django does not automatically sync the schema with the database, we have to manually ask Django to keep the schema up to date.
-
Migrations: are a way to record changes in the database schema. you can think of it as git for database schema changes. It keeps track of the history, it allows us to move to a specific version of the schema as well.
-
Guide to writing migrations
-
Commands:
makemigrations
: This is responsible for creating new migrations based on the changes you have made to your models.migrate
: This is when Django actually performs the schema changes in the Database. This command can also be used to move to a specific migration version.
-
Idea: Schema <-> Migrations <-> Database
-
Migrations are atomic in nature, ie if one change fails to be performed, all the migrations are rolled back. the database is moved to the state it was in before the migration was applied.
- Every model has a model manager
-
Understanding QuerySet: A
QuerySet
is, in essence, a list of objects of a given Model. QuerySets allow you to read the data from the database, filter it and order it. -
Read crisp article from DjangoGirls on ORM
Generally in realtime-applications, we don't perform hard deletions so as to remove the record from the database rather we mark an attribute to denote whether the record is deleted or not.
- The dictionary definition of a
relational database
is a database structured to recognize relations between stored items of information. - Relational Database
- Primary and Foreign Key
- Joins
- Problems of large database with duplicate data and importance of database design
- Normalization
- First Normal Form:
- Eliminate repeating groups in individual tables.
- Create a separate table for each set of related data.
- Identify each set of related data with a primary key.
- Second Normal Form:
- Create separate tables for sets of values that apply to multiple records.
- Relate these tables with a foreign key.
- Third Normal Form:
- Eliminate fields that do not depend on the key.
- First Normal Form:
-
tasks/admin.py
=> We must register model before accessing inadmin/
from tasks.models import Task admin.sites.site.register(Task)
-
Creating superuser:
python manage.py createsuperuser
, we can use this credentials to login to the/admin
console -
Now that we've registered model in admin, we're able to add, delete, modify and play around the records
🤪 Django was named after the jazz guitarist Jean Reinhardt, Django was his nickname.
-
Django started as a side project of an intern who was fed up with maintaining bulky PHP code at a newspaper company called Lawrence, it was created back in 2005 when there were little to no web applications written in python.
-
Django was built to build applications quickly, one of its core philosophies is "Don't Repeat Yourself" often abbreviated as
DRY
. -
⚜️ Design Philosophies listed on Django's Official
- Function-based view to Class-based view
- Class based view
- A view is a callable which takes a request and returns a response.
- This can be more than just a function, and Django provides an example of some classes which can be used as views. These allow you to structure your views and reuse code by harnessing inheritance and mixins.
- There are also some generic views for tasks which we’ll get to later, but you may want to design your own structure of reusable views which suits your use case.
- Generic List View
- Pagination
- To prevent against CSRF Attack:
- Make sure those routes having side effects use safe methods like
POST
- And include the
CSRF
token
- Make sure those routes having side effects use safe methods like
- Idea is that the server generates that token and keeps a record of it, so if the server receives a form with a csrf token that it has no record of, it will flag the request
- This should not be done for POST forms that target external URLs, since that would cause the CSRF token to be leaked, leading to a vulnerability.
-
Forms always deal with
POST
HTTP method, even for deletion; Only in API we use theDELETE
HTTP method
- All communication between the server and client is done with HTTP, which is a stateless protocol.
stateless
means that every request is an independent transaction, each one does not affect the other, HTTP does not know what request came before it or how many times it was called, it has no state.- To keep track of the sessions, Django uses
cookies
- An
HTTP cookie
(web cookie, browser cookie) is a small piece of data that a server sends to a user's web browser. The browser may store the cookie and send it back to the same server with later requests. - When you visit a page that Django renders, Django will create a new random key and send it over to your browser as a cookie, every time you visit any route in the same server (base URL), the cookie is sent along with the request, when Django sees the cookie it knows that it's you sending the request and sets the context accordingly.
- You can also store values in sessions, maybe a login time and a session timeout, maybe a language preference, anything like that. These values are stored in the session table and can be accessed in the view.
- Sessions are used to store small amount of data, for everything else we use database
- Associate User with Task
- Detail, Delete and Update Views should have been authenticated to access and only be able to views the respective user's details
REST stands for Representational State Transfer
. It is a standard or a design pattern that is used when developing web application APIs. API's that follow the REST standard are often called RESTful web applications or RESTful APIs. We can perform a variety of operations on the tasks, the major ones being CREATE
, RETRIEVE
, UPDATE
, DELETE
(CRUD Operations). REST forms guidelines on how these operations are performed, for example, HTTP has a method called GET The GET
method must always be used to retrieve or READ data as per the REST guidelines, similarly POST
must be used to perform CREATE operations, PATCH
and PUT
for update operations and finally DELETE
for delete operations. So instead of having routes like create-tasks
, delete-tasks
and so on .. we will have one route for tasks /task
and then all operations are done with different HTTP methods, this makes the API much easier to understand to us and the folks using our API.
Serializing and deserializing like so, can get painful after a point, so make use of the Django REST framework
from django.views import View
from django.http.response import JsonResponse
from tasks.models import Task
class TaskList(View):
def get(self, request):
tasks = Task.objects.filter(deleted=False)
data = []
for task in tasks:
data.append({"title": task.title})
return JsonResponse({"tasks": data})
-
Install:
Django REST framework
pip install djangorestframework
-
Install:
Django filters
pip install django-filter
-
After installing, append to the
INSTALLED_APPS
list in thesettings.py
file:"rest_framework"
"django_filters"
-
To start using the DRF, just include:
from rest_framework.views import APIView from rest_framework.response import Response from tasks.models import Task class TaskList(APIView): def get(self, request): tasks = Task.objects.filter(deleted=False) data = [] for task in tasks: data.append({"title": task.title}) return Response({"tasks": data})
You will observe that entire UI has been changed
-
Let's use the DRF's built in serializer to make things more smooth:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.serializers import ModelSerializer from tasks.models import Task class TaskSerializer(ModelSerializer): class Meta: model = Task fields = ["title", "description", "completed"] class TaskList(APIView): def get(self, request): tasks = Task.objects.filter(deleted=False) data = TaskSerializer(tasks, many=True).data return Response({"tasks": data})
-
DRF generates Dynamic Routers
# task_manager/urls.py from rest_framework.routers import SimpleRouter router = SimpleRouter() # routers.register("api/task", TaskViewSet ) urlpatterns = [ #Already filled in views # path("admin/", admin.site.urls), # path("all-tasks/", GenericAllTaskView.as_view()), ] + router.urls
-
Side Note: For the course, we stick to DRF router for API and Django for other views
-
To build generic view set:
from rest_framework.viewsets import ModelViewSet from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.serializers import ModelSerializer from tasks.models import Task class TaskSerializer(ModelSerializer): class Meta: model = Task fields = ["title", "description", "completed"] class TaskList(APIView): def get(self, request): tasks = Task.objects.filter(deleted=False) data = TaskSerializer(tasks, many=True).data return Response({"tasks": data}) class TaskViewSet(ModelViewSet): queryset = Task.objects.all() serializer_class = TaskSerializer
- We go for Generic
ModelSerializer
, because it is similar to repeating the model's fields everywhere. So, the DRF takes care of the redundant work in populating the fields and methods of the ModelSerializzer ModelViewSet
consists of CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, ListModelMixin and GenericViewSet. TheGenericViewSet
maps the HTTP Method to the methods in each of the Mixin
- In order to add permission to allow only respective user to view his or her own task, we include
permssion_classes = (IsAuthenticated,)
- Override
perform_create
to link the tasks with the user - Override
get_queryset
to list only the tasks that aren't soft deleted and only belong to the current authenticated - Understanding Nested Serializers
- Understanding filters
-
Read to understand the entire workflow: Request -> Request Middleware -> URL Router -> Views -> Response Middleware -> Response Middlewares make it incredibly simple to "plugin" new handlers into an existing request-response cycle.
-
If we were to create a
CustomMiddleware
, we can do so:class CustomMiddleware(object): def __init__(self, get_response): """ One-time configuration and initialisation. """ self.get_response = get_response def __call__(self, request): """ Code to be executed for each request before the view (and later middleware) are called. """ print("From middleware: ", request) response = self.get_response(request) return response
In this any LOC in
__call__
function beforeresponse = self.get_response(request)
is processed before request reaching the view, and any LOC after it is processed while returning back from the view
- One of the powerful features
- Template inheritance allows you to create a template skeleton that can be reused in other templates.
- At the start of the file for any child template, you must define which parent template you are extending from. We use the
extends
tag for extending a parent template, and you can extend any template in Django. Once we specify the parent template, we can create the blocks we want to override/modify in the child template. - When rendering the page, Django will load up the parent template and replace the blocks with the child template. This means that anything that is not specified inside a block is ignored completely. This makes it really easy to structure web documents and makes it easier to reuse HTML code.
- Static files are files that are not changed when the application is running. These include the CSS files, images, Javascript files, and any other types of files that are not dependent on the application logic.
- To use this feature: follow tutorial
- ER Diagrams are usually used to visualize a database design, ER Diagram stands for
Entity Relationship Diagram
. - Understanding relationships:
- One-to-one
- Many-to-one
- Many-to-many
Django shell is simply the Python interactive interpreter with Django configured. Since Django is configured, you can interact with the Django ORM without any additional setup.
python manage.py shell
-
Let's say that you want to send an email with all the pending tasks to the user at midnight every day. This is a job that is dependent on the current time and not based on an external request. These types of jobs are usually called scheduled tasks or scheduled jobs.
-
There can also be externally triggered jobs as well, for example, let's say you have an endpoint that does a lot of computation and needs a lot of time to process. These types of long-running jobs are usually performed in the background so that the actual server has more time to work on simpler requests.
-
Celery
is a popular library that solves exactly this problem. Note that Celery is not dependent on Django. There are packages that integrate Django and Celery for specific use cases. -
Celery Terms:
-
Task: A Celery task is a representation of a callable that can be invoked by Celery. In other words, it is a method that is registered with Celery. An instance of the task contains the arguments you passed to the method as well. When you start a background job, all you are doing is creating a new task object and storing it somewhere to be executed.
-
Broker:
-
Since Celery is used in background processing there needs to be some sort of queueing mechanism, any task that needs to be processed are added to the queue and then processed one by one. This queue is usually called a broker. The broker does not have any knowledge of the actual implementation of the task. It just stores the task in the queue.
-
For our implementation we will be using Redis as the broker, Redis is an in-memory data store, Redis has implementations of different kinds of abstracted data structures built-in, one of them is the queue which makes it ideal as the broker. The use of Redis is not limited to Celery.
-
-
Worker: A Celery worker is responsible for actually executing the task, a worker asks the broker to get pending tasks and executes them as soon as they are available. You can have as many celery workers as you want in a project.
-
Periodic Scheduler(Beat): Periodic Tasks in Celery are made possible with Celery beat, which is a scheduler that schedules tasks to be executed based on the schedules we configure.
-
-
Install redis, use it as a service and check if it works:
# Install redis brew install redis # Start redis service brew services start redis # Stop redis service brew services stop redis # Ping CLI, expects PONG redis-cli ping
-
Install redis and celery:
pip install celery==4.4.7 redis
Note that Celery does NOT automatically restart when code changes are made, Celery processes have to be manually restarted for any changes to take effect.