Compare commits
1 Commits
97814b3b57
...
docker
| Author | SHA1 | Date | |
|---|---|---|---|
| 42f8df2589 |
@@ -1,281 +0,0 @@
|
|||||||
# 05 – Object-Oriented Programming (OOP) in Python
|
|
||||||
|
|
||||||
This document explains the basics of **Object-Oriented Programming (OOP)** in Python using simple examples.
|
|
||||||
We cover:
|
|
||||||
|
|
||||||
* Classes and objects
|
|
||||||
* Attributes and methods
|
|
||||||
* Class attributes vs instance attributes
|
|
||||||
* Inheritance
|
|
||||||
* Special (magic) methods
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Basic Class, Attribute, and Method
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
class test_class():
|
|
||||||
def __init__(self, input):
|
|
||||||
self.parm = input
|
|
||||||
print("Class Created")
|
|
||||||
|
|
||||||
def result(self):
|
|
||||||
print(f"param is : {self.parm}")
|
|
||||||
|
|
||||||
var = test_class('abbas')
|
|
||||||
var.result()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
#### Class
|
|
||||||
|
|
||||||
* `test_class` is a **class**, which acts as a blueprint for creating objects.
|
|
||||||
|
|
||||||
#### `__init__` method (Constructor)
|
|
||||||
|
|
||||||
* `__init__` is a **special method** that runs automatically when a new object is created.
|
|
||||||
* `input` is a **parameter** passed when creating the object.
|
|
||||||
* `self.parm = input` creates an **instance attribute** called `parm`.
|
|
||||||
|
|
||||||
#### Attribute
|
|
||||||
|
|
||||||
* `parm` is an **attribute** (a variable that belongs to the object).
|
|
||||||
* It stores data specific to each object.
|
|
||||||
|
|
||||||
#### Method
|
|
||||||
|
|
||||||
* `result()` is a **method** (a function that belongs to the class).
|
|
||||||
* It uses `self.parm` to access the object’s data.
|
|
||||||
|
|
||||||
#### Object Creation
|
|
||||||
|
|
||||||
```python
|
|
||||||
var = test_class('abbas')
|
|
||||||
```
|
|
||||||
|
|
||||||
* Creates an object named `var`.
|
|
||||||
* Calls `__init__` automatically.
|
|
||||||
|
|
||||||
#### Method Call
|
|
||||||
|
|
||||||
```python
|
|
||||||
var.result()
|
|
||||||
```
|
|
||||||
|
|
||||||
* Calls the `result` method on the object.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Class Attributes vs Instance Attributes
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
class test_class():
|
|
||||||
test_value = 'abbas'
|
|
||||||
|
|
||||||
def __init__(self, input):
|
|
||||||
self.parm = input
|
|
||||||
print("Class Created")
|
|
||||||
|
|
||||||
def result(self):
|
|
||||||
print(f"param is : {self.parm}")
|
|
||||||
|
|
||||||
var = test_class('abbas')
|
|
||||||
var2 = test_class('mmd')
|
|
||||||
|
|
||||||
var.result()
|
|
||||||
var.test_value
|
|
||||||
|
|
||||||
var2.test_value = 'mmd'
|
|
||||||
var2.test_value
|
|
||||||
var.test_value
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
#### Class Attribute
|
|
||||||
|
|
||||||
```python
|
|
||||||
test_value = 'abbas'
|
|
||||||
```
|
|
||||||
|
|
||||||
* This is a **class attribute**.
|
|
||||||
* It belongs to the class itself.
|
|
||||||
* Shared by all objects unless overridden.
|
|
||||||
|
|
||||||
#### Instance Attribute
|
|
||||||
|
|
||||||
```python
|
|
||||||
self.parm = input
|
|
||||||
```
|
|
||||||
|
|
||||||
* This is an **instance attribute**.
|
|
||||||
* Each object has its own value.
|
|
||||||
|
|
||||||
#### Behavior Analysis
|
|
||||||
|
|
||||||
```python
|
|
||||||
var.test_value
|
|
||||||
```
|
|
||||||
|
|
||||||
* Accesses the class attribute → `'abbas'`
|
|
||||||
|
|
||||||
```python
|
|
||||||
var2.test_value = 'mmd'
|
|
||||||
```
|
|
||||||
|
|
||||||
* Creates a **new instance attribute** for `var2`.
|
|
||||||
* Does not change the class attribute.
|
|
||||||
|
|
||||||
```python
|
|
||||||
var2.test_value
|
|
||||||
```
|
|
||||||
|
|
||||||
* Returns `'mmd'` (instance attribute)
|
|
||||||
|
|
||||||
```python
|
|
||||||
var.test_value
|
|
||||||
```
|
|
||||||
|
|
||||||
* Still returns `'abbas'` (class attribute)
|
|
||||||
|
|
||||||
#### Key Rule
|
|
||||||
|
|
||||||
* Instance attributes override class attributes **only for that object**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Inheritance
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
class class_1():
|
|
||||||
def __init__(self):
|
|
||||||
print("Class 1 Created")
|
|
||||||
|
|
||||||
def hi(self):
|
|
||||||
print("Hi")
|
|
||||||
|
|
||||||
class class_2(class_1):
|
|
||||||
def __init__(self):
|
|
||||||
print("Class 2 Created")
|
|
||||||
self.hi()
|
|
||||||
|
|
||||||
b = class_2()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
#### Parent Class
|
|
||||||
|
|
||||||
```python
|
|
||||||
class class_1():
|
|
||||||
```
|
|
||||||
|
|
||||||
* `class_1` is the **parent (base) class**.
|
|
||||||
|
|
||||||
#### Child Class
|
|
||||||
|
|
||||||
```python
|
|
||||||
class class_2(class_1):
|
|
||||||
```
|
|
||||||
|
|
||||||
* `class_2` **inherits** from `class_1`.
|
|
||||||
* It automatically has access to all public methods of `class_1`.
|
|
||||||
|
|
||||||
#### Method Usage
|
|
||||||
|
|
||||||
```python
|
|
||||||
self.hi()
|
|
||||||
```
|
|
||||||
|
|
||||||
* `hi()` is defined in `class_1`.
|
|
||||||
* Because of inheritance, `class_2` can call it.
|
|
||||||
|
|
||||||
#### Output Order
|
|
||||||
|
|
||||||
```text
|
|
||||||
Class 2 Created
|
|
||||||
Hi
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Important Note
|
|
||||||
|
|
||||||
* `class_1.__init__()` is **not called automatically** here.
|
|
||||||
* To call it, you would need:
|
|
||||||
|
|
||||||
```python
|
|
||||||
super().__init__()
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Special (Magic) Methods
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
class class_1():
|
|
||||||
def __init__(self):
|
|
||||||
print("Class 1 Created")
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return 'print command on class'
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
return 'on del value'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
Special methods start and end with **double underscores (`__`)** and control built-in behavior.
|
|
||||||
|
|
||||||
#### `__init__`
|
|
||||||
|
|
||||||
* Runs when an object is created.
|
|
||||||
|
|
||||||
#### `__len__`
|
|
||||||
|
|
||||||
```python
|
|
||||||
len(object)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Defines the behavior of `len()` on the object.
|
|
||||||
* Returns `1` in this example.
|
|
||||||
|
|
||||||
#### `__str__`
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(object)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Defines the string representation of the object.
|
|
||||||
* Used by `print()` and `str()`.
|
|
||||||
|
|
||||||
#### `__del__`
|
|
||||||
|
|
||||||
* Runs when the object is deleted or garbage-collected.
|
|
||||||
* Used rarely in modern Python.
|
|
||||||
* Return value is ignored.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
* **Class**: Blueprint for objects
|
|
||||||
* **Object**: Instance of a class
|
|
||||||
* **Attribute**: Data stored in an object
|
|
||||||
* **Method**: Function inside a class
|
|
||||||
* **Class Attribute**: Shared across all objects
|
|
||||||
* **Instance Attribute**: Unique per object
|
|
||||||
* **Inheritance**: Child class reuses parent class logic
|
|
||||||
* **Magic Methods**: Customize built-in Python behavior
|
|
||||||
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
# 06 – Packages and Modules in Python
|
|
||||||
|
|
||||||
This document explains how **modules**, **packages**, and the `__name__` concept work in Python.
|
|
||||||
These features help organize code, reuse functionality, and build scalable projects.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Importing External Modules
|
|
||||||
|
|
||||||
Python allows you to import **external libraries** installed in your environment.
|
|
||||||
|
|
||||||
### Example: Using the `emoji` Module
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
import emoji
|
|
||||||
print(emoji.emojize("abbas is :red_heart:"))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* `import emoji` imports the entire `emoji` module.
|
|
||||||
* `emoji.emojize()` converts emoji aliases into actual emoji characters.
|
|
||||||
* You must use the module name (`emoji`) to access its functions.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Importing a Specific Function
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
from emoji import emojize
|
|
||||||
print(emojize("abbas is :red_heart:"))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* `from emoji import emojize` imports only the `emojize` function.
|
|
||||||
* You can call the function directly without prefixing the module name.
|
|
||||||
* This approach is cleaner when you only need a specific function.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Creating a Module
|
|
||||||
|
|
||||||
A **module** is a single Python file containing functions, classes, or variables.
|
|
||||||
|
|
||||||
### File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
hi.py
|
|
||||||
main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### `hi.py`
|
|
||||||
|
|
||||||
```python
|
|
||||||
def hi():
|
|
||||||
print("Hi :)")
|
|
||||||
```
|
|
||||||
|
|
||||||
### `main.py`
|
|
||||||
|
|
||||||
```python
|
|
||||||
import hi
|
|
||||||
hi.hi()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* `hi.py` is a module.
|
|
||||||
* `hi()` is a function defined inside the module.
|
|
||||||
* `import hi` loads the module.
|
|
||||||
* `hi.hi()` calls the function from the module.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Creating a Package
|
|
||||||
|
|
||||||
A **package** is a directory that contains multiple modules.
|
|
||||||
|
|
||||||
### Package Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
honor/
|
|
||||||
│── __init__.py
|
|
||||||
│── hi.py
|
|
||||||
main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### `honor/hi.py`
|
|
||||||
|
|
||||||
```python
|
|
||||||
def hello():
|
|
||||||
print("Hi :)")
|
|
||||||
```
|
|
||||||
|
|
||||||
### `honor/__init__.py`
|
|
||||||
|
|
||||||
```python
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* The `honor` directory is a package.
|
|
||||||
* `__init__.py` tells Python that this directory is a package.
|
|
||||||
* The file can be empty, but it **must exist** (especially for older Python versions and clarity).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Importing from a Package (Method 1)
|
|
||||||
|
|
||||||
### `main.py`
|
|
||||||
|
|
||||||
```python
|
|
||||||
from honor import hi
|
|
||||||
hi.hello()
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Explanation
|
|
||||||
|
|
||||||
* Imports the `hi` module from the `honor` package.
|
|
||||||
* Accesses the function using `hi.hello()`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Importing from a Package (Method 2)
|
|
||||||
|
|
||||||
### `main.py`
|
|
||||||
|
|
||||||
```python
|
|
||||||
from honor.hi import hello
|
|
||||||
hello()
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Explanation
|
|
||||||
|
|
||||||
* Imports the `hello` function directly.
|
|
||||||
* Allows calling the function without the module name.
|
|
||||||
* Cleaner when only one function is needed.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. The `__name__` Concept
|
|
||||||
|
|
||||||
Every Python file has a built-in variable called `__name__`.
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(__name__)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Behavior
|
|
||||||
|
|
||||||
#### When a File Is Run Directly
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 abbas.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```text
|
|
||||||
__main__
|
|
||||||
```
|
|
||||||
|
|
||||||
* This means the file is the **entry point** of the program.
|
|
||||||
|
|
||||||
#### When a File Is Imported
|
|
||||||
|
|
||||||
```python
|
|
||||||
import abbas
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```text
|
|
||||||
abbas
|
|
||||||
```
|
|
||||||
|
|
||||||
* `__name__` is set to the **module name**, not `__main__`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Why `__name__ == "__main__"` Is Important
|
|
||||||
|
|
||||||
This pattern allows code to run **only when the file is executed directly**, not when imported.
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```python
|
|
||||||
def main():
|
|
||||||
print("Running directly")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* The code inside the `if` block runs only when the file is executed directly.
|
|
||||||
* Prevents unwanted execution when the file is imported as a module.
|
|
||||||
* This is a standard Python best practice.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
* **Module**: A single `.py` file
|
|
||||||
* **Package**: A directory containing modules
|
|
||||||
* `__init__.py`: Marks a directory as a package
|
|
||||||
* `import module`: Imports the whole module
|
|
||||||
* `from module import item`: Imports specific items
|
|
||||||
* `__name__`: Identifies how a file is executed
|
|
||||||
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
# 06 – Error Handling, Linting, Formatting, and Testing in Python
|
|
||||||
|
|
||||||
This document explains how Python handles runtime errors, how to write safer code using `try / except`, and how to improve code quality using **linting**, **formatting**, and **unit testing** tools.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Error Handling with `try / except`
|
|
||||||
|
|
||||||
Python uses `try / except` blocks to handle runtime errors gracefully without crashing the program.
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```python
|
|
||||||
def abbas(a, b):
|
|
||||||
try:
|
|
||||||
res = a / b
|
|
||||||
print(res)
|
|
||||||
except ZeroDivisionError:
|
|
||||||
print("Zero Number Detected")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error Detected {e}")
|
|
||||||
|
|
||||||
abbas(1, 0)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* The code inside `try` is executed first.
|
|
||||||
* If no error occurs, the result is printed.
|
|
||||||
* If `b` is `0`, a `ZeroDivisionError` is raised.
|
|
||||||
* The specific `ZeroDivisionError` block runs first.
|
|
||||||
* Any other error is caught by the generic `Exception` block.
|
|
||||||
|
|
||||||
### Key Rule
|
|
||||||
|
|
||||||
* Always catch **specific exceptions first**.
|
|
||||||
* Use `Exception` only as a fallback.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Full `try / except` Structure
|
|
||||||
|
|
||||||
Python supports additional blocks for more control.
|
|
||||||
|
|
||||||
### Syntax
|
|
||||||
|
|
||||||
```python
|
|
||||||
try:
|
|
||||||
# code that may raise an error
|
|
||||||
except:
|
|
||||||
# runs if an error occurs
|
|
||||||
else:
|
|
||||||
# runs if no error occurs
|
|
||||||
finally:
|
|
||||||
# always runs
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* `try`: code that may fail
|
|
||||||
* `except`: handles errors
|
|
||||||
* `else`: runs only if no exception occurred
|
|
||||||
* `finally`: runs no matter what (used for cleanup)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Linting with `pylint`
|
|
||||||
|
|
||||||
Linting analyzes code for:
|
|
||||||
|
|
||||||
* Syntax errors
|
|
||||||
* Style problems
|
|
||||||
* Bad practices
|
|
||||||
|
|
||||||
### Basic Command
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pylint main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### Detailed Report
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pylint --report y main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* `pylint` gives a score and suggestions.
|
|
||||||
* Helps maintain readable and maintainable code.
|
|
||||||
* Commonly used in professional Python projects.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Code Formatting with `black`
|
|
||||||
|
|
||||||
`black` is an automatic code formatter that enforces a consistent style.
|
|
||||||
|
|
||||||
### Command
|
|
||||||
|
|
||||||
```bash
|
|
||||||
black main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* Reformats code automatically.
|
|
||||||
* Removes style debates.
|
|
||||||
* Safe and widely used.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Unit Testing with `unittest`
|
|
||||||
|
|
||||||
Unit tests verify that individual parts of code behave as expected.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Application Code
|
|
||||||
|
|
||||||
#### `abbas.py`
|
|
||||||
|
|
||||||
```python
|
|
||||||
def bemola(a, b):
|
|
||||||
try:
|
|
||||||
res = a / b
|
|
||||||
print(res)
|
|
||||||
except ZeroDivisionError:
|
|
||||||
print("Zero Number Detected")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error Detected {e}")
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Test Code
|
|
||||||
|
|
||||||
#### `abbas_test.py`
|
|
||||||
|
|
||||||
```python
|
|
||||||
import unittest
|
|
||||||
from abbas import bemola
|
|
||||||
|
|
||||||
class TestAbbas(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_bemola(self):
|
|
||||||
a = 10
|
|
||||||
b = 2
|
|
||||||
self.assertEqual(bemola(a, b), 5)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
#### `unittest.TestCase`
|
|
||||||
|
|
||||||
* Base class for writing test cases.
|
|
||||||
|
|
||||||
#### Test Method
|
|
||||||
|
|
||||||
```python
|
|
||||||
def test_bemola(self):
|
|
||||||
```
|
|
||||||
|
|
||||||
* Test methods must start with `test_`.
|
|
||||||
|
|
||||||
#### Assertion
|
|
||||||
|
|
||||||
```python
|
|
||||||
self.assertEqual(bemola(a, b), 5)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Checks if the function returns the expected result.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Important Note (Design Issue)
|
|
||||||
|
|
||||||
The function `bemola` **prints** the result but does not return it.
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(res)
|
|
||||||
```
|
|
||||||
|
|
||||||
This causes the test to fail because the function returns `None`.
|
|
||||||
|
|
||||||
#### Correct Implementation
|
|
||||||
|
|
||||||
```python
|
|
||||||
def bemola(a, b):
|
|
||||||
try:
|
|
||||||
return a / b
|
|
||||||
except ZeroDivisionError:
|
|
||||||
return "Zero Number Detected"
|
|
||||||
except Exception as e:
|
|
||||||
return f"Error Detected {e}"
|
|
||||||
```
|
|
||||||
|
|
||||||
This version:
|
|
||||||
|
|
||||||
* Returns values instead of printing
|
|
||||||
* Is testable
|
|
||||||
* Follows best practices
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
* `try / except` prevents program crashes
|
|
||||||
* `else` runs only when no error occurs
|
|
||||||
* `finally` always runs
|
|
||||||
* `pylint` improves code quality
|
|
||||||
* `black` enforces formatting
|
|
||||||
* `unittest` verifies correctness
|
|
||||||
* Functions should **return values**, not print them, when tested
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
# 08 – Decorators in Python
|
|
||||||
|
|
||||||
This document explains **decorators**, how they work, and how they are used to extend function behavior without modifying the original function code.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. What Is a Decorator?
|
|
||||||
|
|
||||||
A **decorator** is a function that:
|
|
||||||
|
|
||||||
* Takes another function as input
|
|
||||||
* Adds extra behavior
|
|
||||||
* Returns a new function
|
|
||||||
|
|
||||||
Decorators are commonly used for:
|
|
||||||
|
|
||||||
* Input validation
|
|
||||||
* Logging
|
|
||||||
* Authentication
|
|
||||||
* Performance measurement
|
|
||||||
* Access control
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Basic Decorator Structure
|
|
||||||
|
|
||||||
A decorator has three layers:
|
|
||||||
|
|
||||||
1. The decorator function
|
|
||||||
2. The wrapper function
|
|
||||||
3. The original function
|
|
||||||
|
|
||||||
### General Pattern
|
|
||||||
|
|
||||||
```python
|
|
||||||
def decorator(func):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
# extra behavior
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
return wrapper
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Example: Input Validation Decorator
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
def check_number(func):
|
|
||||||
def wrapper(a, b):
|
|
||||||
if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
|
|
||||||
print("Input must be numbers")
|
|
||||||
return
|
|
||||||
return func(a, b)
|
|
||||||
return wrapper
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* `check_number` is the decorator.
|
|
||||||
* `func` is the function being decorated.
|
|
||||||
* `wrapper` replaces the original function.
|
|
||||||
* `a` and `b` are the arguments passed to the original function.
|
|
||||||
* `isinstance(a, (int, float))` ensures inputs are numeric.
|
|
||||||
* If validation fails, execution stops.
|
|
||||||
* If validation passes, the original function is called.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Using the Decorator with `@` Syntax
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
@check_number
|
|
||||||
def bemola(a, b):
|
|
||||||
try:
|
|
||||||
res = a / b
|
|
||||||
print(res)
|
|
||||||
except ZeroDivisionError:
|
|
||||||
print("Zero Number Detected")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error Detected {e}")
|
|
||||||
```
|
|
||||||
|
|
||||||
### What Happens Internally
|
|
||||||
|
|
||||||
This line:
|
|
||||||
|
|
||||||
```python
|
|
||||||
@check_number
|
|
||||||
```
|
|
||||||
|
|
||||||
Is equivalent to:
|
|
||||||
|
|
||||||
```python
|
|
||||||
bemola = check_number(bemola)
|
|
||||||
```
|
|
||||||
|
|
||||||
The function `bemola` is replaced by `wrapper`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Execution Flow
|
|
||||||
|
|
||||||
When calling:
|
|
||||||
|
|
||||||
```python
|
|
||||||
bemola(10, 2)
|
|
||||||
```
|
|
||||||
|
|
||||||
The flow is:
|
|
||||||
|
|
||||||
1. `wrapper(10, 2)` is called
|
|
||||||
2. Inputs are validated
|
|
||||||
3. `func(10, 2)` is executed
|
|
||||||
4. Result is printed
|
|
||||||
|
|
||||||
If calling:
|
|
||||||
|
|
||||||
```python
|
|
||||||
bemola(10, "a")
|
|
||||||
```
|
|
||||||
|
|
||||||
The output will be:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Input must be numbers
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Why Use Decorators?
|
|
||||||
|
|
||||||
Without decorators, input validation would need to be repeated in every function.
|
|
||||||
|
|
||||||
Decorators allow:
|
|
||||||
|
|
||||||
* Reusable logic
|
|
||||||
* Cleaner code
|
|
||||||
* Separation of concerns
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Limitations in This Example
|
|
||||||
|
|
||||||
* The decorator only works with exactly two arguments.
|
|
||||||
* It does not preserve the original function’s metadata (`__name__`, `__doc__`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Improved Version (Best Practice)
|
|
||||||
|
|
||||||
```python
|
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
def check_number(func):
|
|
||||||
@wraps(func)
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
if not all(isinstance(x, (int, float)) for x in args):
|
|
||||||
print("Input must be numbers")
|
|
||||||
return
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
return wrapper
|
|
||||||
```
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
|
|
||||||
* Supports any number of arguments
|
|
||||||
* Preserves function name and documentation
|
|
||||||
* More reusable and professional
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
* Decorators modify function behavior without changing its code
|
|
||||||
* They wrap functions inside another function
|
|
||||||
* `@decorator` is syntactic sugar
|
|
||||||
* Commonly used for validation, logging, and access control
|
|
||||||
* Best practice is to use `*args`, `**kwargs`, and `functools.wraps`
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
# 09 – Python Standard Library
|
|
||||||
|
|
||||||
This document introduces some of the most commonly used **Python standard library** modules:
|
|
||||||
|
|
||||||
* `datetime`
|
|
||||||
* `math`
|
|
||||||
* `random`
|
|
||||||
* `decimal`
|
|
||||||
|
|
||||||
These modules come bundled with Python and require no external installation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Date and Time with `datetime`
|
|
||||||
|
|
||||||
The `datetime` module provides classes for working with dates and times.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Working with Dates
|
|
||||||
|
|
||||||
#### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
date_1 = datetime.date(2026, 1, 1)
|
|
||||||
|
|
||||||
print(date_1.year)
|
|
||||||
print(date_1.month)
|
|
||||||
print(date_1.day)
|
|
||||||
|
|
||||||
print(date_1.weekday)
|
|
||||||
print(date_1.ctime)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Explanation
|
|
||||||
|
|
||||||
* `datetime.date(year, month, day)` creates a date object.
|
|
||||||
* `.year`, `.month`, `.day` access individual components.
|
|
||||||
|
|
||||||
#### Important Note
|
|
||||||
|
|
||||||
```python
|
|
||||||
date_1.weekday()
|
|
||||||
```
|
|
||||||
|
|
||||||
* Returns the day of the week as an integer:
|
|
||||||
|
|
||||||
* Monday = 0
|
|
||||||
* Sunday = 6
|
|
||||||
|
|
||||||
```python
|
|
||||||
date_1.ctime()
|
|
||||||
```
|
|
||||||
|
|
||||||
* Returns a human-readable string representation of the date.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Working with Time
|
|
||||||
|
|
||||||
#### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
time_1 = datetime.time(12, 12)
|
|
||||||
|
|
||||||
print(time_1.hour)
|
|
||||||
print(time_1.min)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Explanation
|
|
||||||
|
|
||||||
* `datetime.time(hour, minute)` creates a time object.
|
|
||||||
* `.hour` returns the hour.
|
|
||||||
* `.minute` returns the minute.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Working with Date and Time Together
|
|
||||||
|
|
||||||
#### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
abbas_birth = datetime.datetime(2026, 1, 1, 12, 12)
|
|
||||||
today = datetime.date.today()
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
|
|
||||||
diff_time = now - abbas_birth
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Explanation
|
|
||||||
|
|
||||||
* `datetime.datetime` includes both date and time.
|
|
||||||
* `date.today()` returns today’s date.
|
|
||||||
* `datetime.now()` returns the current date and time.
|
|
||||||
* Subtracting two `datetime` objects returns a `timedelta`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Mathematical Operations with `math`
|
|
||||||
|
|
||||||
The `math` module provides advanced mathematical functions and constants.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Mathematical Constants
|
|
||||||
|
|
||||||
```python
|
|
||||||
import math
|
|
||||||
|
|
||||||
print(math.pi)
|
|
||||||
print(math.e)
|
|
||||||
print(math.inf)
|
|
||||||
```
|
|
||||||
|
|
||||||
* `math.pi`: π constant
|
|
||||||
* `math.e`: Euler’s number
|
|
||||||
* `math.inf`: infinity
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Power and Rounding
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(math.pow(2, 3))
|
|
||||||
|
|
||||||
print(round(4.2))
|
|
||||||
print(round(4.8))
|
|
||||||
```
|
|
||||||
|
|
||||||
* `math.pow(a, b)` returns `a` raised to the power of `b`.
|
|
||||||
* `round()` rounds to the nearest integer.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Floor and Ceil
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(math.floor(4.2))
|
|
||||||
print(math.floor(4.9))
|
|
||||||
|
|
||||||
print(math.ceil(4.2))
|
|
||||||
print(math.ceil(4.9))
|
|
||||||
```
|
|
||||||
|
|
||||||
* `floor`: rounds down
|
|
||||||
* `ceil`: rounds up
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Logarithms
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(math.log(100, 10))
|
|
||||||
```
|
|
||||||
|
|
||||||
* Returns the logarithm of 100 with base 10.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Random Values with `random`
|
|
||||||
|
|
||||||
The `random` module is used to generate pseudo-random values.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Random Numbers
|
|
||||||
|
|
||||||
```python
|
|
||||||
import random
|
|
||||||
|
|
||||||
print(random.randint(1, 6))
|
|
||||||
print(random.random())
|
|
||||||
```
|
|
||||||
|
|
||||||
* `randint(a, b)`: random integer between `a` and `b` (inclusive)
|
|
||||||
* `random()`: random float between `0` and `1`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Random Selection
|
|
||||||
|
|
||||||
```python
|
|
||||||
number_list = list(range(15))
|
|
||||||
print(random.choice(number_list))
|
|
||||||
|
|
||||||
char_list = ['a', 'm', 's']
|
|
||||||
print(random.choice(char_list))
|
|
||||||
```
|
|
||||||
|
|
||||||
* `choice()` selects a random element from a sequence.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Shuffling
|
|
||||||
|
|
||||||
```python
|
|
||||||
random.shuffle(number_list)
|
|
||||||
print(number_list)
|
|
||||||
```
|
|
||||||
|
|
||||||
* `shuffle()` randomly rearranges the list in place.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Decimal Precision with `decimal`
|
|
||||||
|
|
||||||
The `decimal` module provides precise decimal arithmetic, avoiding floating-point errors.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Decimal Context
|
|
||||||
|
|
||||||
```python
|
|
||||||
import decimal
|
|
||||||
|
|
||||||
print(decimal.getcontext())
|
|
||||||
```
|
|
||||||
|
|
||||||
* Shows current precision and rounding settings.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Float vs Decimal
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(decimal.Decimal(0.1))
|
|
||||||
print(decimal.Decimal('0.1'))
|
|
||||||
```
|
|
||||||
|
|
||||||
* Passing a float carries floating-point error.
|
|
||||||
* Passing a string preserves exact value.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Precision Comparison
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(0.1 + 0.2 == 0.3)
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns `False` due to floating-point precision issues.
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(decimal.Decimal(0.1) + decimal.Decimal(0.2) == decimal.Decimal(0.3))
|
|
||||||
```
|
|
||||||
|
|
||||||
Still `False` because the floats are imprecise.
|
|
||||||
|
|
||||||
```python
|
|
||||||
print(decimal.Decimal('0.1') + decimal.Decimal('0.2') == decimal.Decimal('0.3'))
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns `True` because strings preserve precision.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
* `datetime` handles dates and times
|
|
||||||
* `math` provides mathematical constants and functions
|
|
||||||
* `random` generates pseudo-random values
|
|
||||||
* `decimal` solves floating-point precision problems
|
|
||||||
* Always use strings when creating `Decimal` values
|
|
||||||
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
```python
|
||||||
|
import docker
|
||||||
|
import time
|
||||||
|
|
||||||
|
docker_client = docker.DockerClient(base_url="unix://var/run/docker.sock")
|
||||||
|
docker_client.ping()
|
||||||
|
|
||||||
|
print("All Networks:\n")
|
||||||
|
|
||||||
|
all_networks = docker_client.networks.list()
|
||||||
|
|
||||||
|
for network in all_networks:
|
||||||
|
print(network.name, network.id)
|
||||||
|
|
||||||
|
print("\nNetworks Named host and bridge:\n")
|
||||||
|
|
||||||
|
system_networks = docker_client.networks.list(names=["host", "bridge"])
|
||||||
|
|
||||||
|
for network in system_networks:
|
||||||
|
print(network.name, network.id)
|
||||||
|
|
||||||
|
print("\nNetwork With Custom ID:\n")
|
||||||
|
|
||||||
|
custom_id_networks = docker_client.networks.list(
|
||||||
|
ids=["29c9e588bb8e0db6445f2a2278a1c2f42e39dc163c0a404f744dc4139fe47d21"]
|
||||||
|
)
|
||||||
|
|
||||||
|
for network in custom_id_networks:
|
||||||
|
print(network.name, network.id)
|
||||||
|
|
||||||
|
print("\nNetwork With Custom ID (Including Attributes):\n")
|
||||||
|
|
||||||
|
custom_id_networks = docker_client.networks.list(
|
||||||
|
ids=["29c9e588bb8e0db6445f2a2278a1c2f42e39dc163c0a404f744dc4139fe47d21"]
|
||||||
|
)
|
||||||
|
|
||||||
|
for network in custom_id_networks:
|
||||||
|
print(network.name, network.id, network.attrs)
|
||||||
|
|
||||||
|
print("\nNetwork With Custom Filter:\n")
|
||||||
|
|
||||||
|
filtered_networks = docker_client.networks.list(
|
||||||
|
names=["gitea_default"],
|
||||||
|
filters={"driver": "bridge"}
|
||||||
|
)
|
||||||
|
|
||||||
|
for network in filtered_networks:
|
||||||
|
if network.attrs["Driver"]:
|
||||||
|
print(network.name, network.id)
|
||||||
|
|
||||||
|
print("\nNetwork With Custom Filter (Greedy):\n")
|
||||||
|
|
||||||
|
filtered_networks = docker_client.networks.list(
|
||||||
|
names=["gitea_default"],
|
||||||
|
filters={"driver": "bridge"},
|
||||||
|
greedy=True
|
||||||
|
)
|
||||||
|
|
||||||
|
for network in filtered_networks:
|
||||||
|
if network.attrs["Driver"]:
|
||||||
|
print(network.name, network.id, network.attrs)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
```
|
||||||
@@ -1,22 +1,12 @@
|
|||||||
# FastAPI – Simple Route Example
|
# FastAPI – Simple Route Example
|
||||||
|
|
||||||
## Overview
|
This document demonstrates how to create a basic FastAPI application with a single HTTP route and how to run it using `uvicorn`, which is the default and recommended ASGI server for FastAPI.
|
||||||
|
|
||||||
This document explains how to create a basic FastAPI application with a single HTTP route and how to run it using `uvicorn`.
|
|
||||||
|
|
||||||
FastAPI is an ASGI web framework, and `uvicorn` is commonly used as the ASGI server to run FastAPI applications.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 1. Create a Simple FastAPI Application
|
## Create a Simple FastAPI Application
|
||||||
|
|
||||||
Create a Python file named:
|
Create a Python file named `main.py` and add the following content:
|
||||||
|
|
||||||
```text
|
|
||||||
main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Add the following code:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@@ -29,243 +19,72 @@ def home_dir():
|
|||||||
return {"message": "Home Page"}
|
return {"message": "Home Page"}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### Explanation
|
||||||
|
|
||||||
# 2. Code Explanation
|
* `FastAPI()` initializes the application instance.
|
||||||
|
* `@app.get("/")` registers an HTTP GET endpoint at the root path (`/`).
|
||||||
## Import FastAPI
|
* The `home_dir` function is the request handler and returns a JSON response.
|
||||||
|
* FastAPI automatically handles JSON serialization and response headers.
|
||||||
```python
|
|
||||||
from fastapi import FastAPI
|
|
||||||
```
|
|
||||||
|
|
||||||
This imports the `FastAPI` class from the FastAPI package.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Create the Application Instance
|
## Running the Application
|
||||||
|
|
||||||
```python
|
FastAPI applications are typically run using **uvicorn**, an ASGI server designed for high performance.
|
||||||
app = FastAPI()
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates the main FastAPI application instance.
|
### Option 1: Run Using FastAPI CLI (Development Mode)
|
||||||
|
|
||||||
The variable name `app` is important because it is later used by `uvicorn` when starting the server.
|
FastAPI provides a development-friendly CLI wrapper that uses `uvicorn` internally:
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Define a GET Route
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/")
|
|
||||||
def home_dir():
|
|
||||||
return {"message": "Home Page"}
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates an HTTP `GET` endpoint at the root path:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /
|
|
||||||
```
|
|
||||||
|
|
||||||
When a user sends a request to `/`, the `home_dir` function is executed.
|
|
||||||
|
|
||||||
The function returns a Python dictionary:
|
|
||||||
|
|
||||||
```python
|
|
||||||
{"message": "Home Page"}
|
|
||||||
```
|
|
||||||
|
|
||||||
FastAPI automatically converts this dictionary into a JSON response.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 3. Running the Application
|
|
||||||
|
|
||||||
FastAPI applications are usually run using `uvicorn`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Option 1: Run Using FastAPI CLI
|
|
||||||
|
|
||||||
You can run the application in development mode using:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
fastapi dev main.py
|
fastapi dev main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
This command starts the FastAPI application in development mode.
|
This command:
|
||||||
|
|
||||||
It is useful during local development because it supports automatic reload when the source code changes.
|
* Starts the application in development mode
|
||||||
|
* Enables auto-reload on code changes
|
||||||
|
* Automatically binds to a local development interface
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Option 2: Run Directly with Uvicorn
|
### Option 2: Run Directly with Uvicorn (Recommended)
|
||||||
|
|
||||||
You can also run the application directly using `uvicorn`:
|
For explicit control over runtime configuration, run the application directly with `uvicorn`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
||||||
```
|
```
|
||||||
|
|
||||||
This is the more explicit and commonly used method.
|
#### Command Breakdown
|
||||||
|
|
||||||
|
* `main` → Python file name (without `.py`)
|
||||||
|
* `app` → FastAPI application instance
|
||||||
|
* `--reload` → Automatically reloads the server on code changes (development only)
|
||||||
|
* `--host 0.0.0.0` → Exposes the service on all network interfaces
|
||||||
|
* `--port 1234` → Custom application port
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 4. Uvicorn Command Breakdown
|
## Accessing the Application
|
||||||
|
|
||||||
```bash
|
Once running, the application will be available at:
|
||||||
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
|
||||||
```
|
|
||||||
|
|
||||||
## `uvicorn`
|
* API Endpoint:
|
||||||
|
`http://localhost:1234/`
|
||||||
|
|
||||||
Starts the ASGI server.
|
* Interactive API Docs (Swagger UI):
|
||||||
|
`http://localhost:1234/docs`
|
||||||
|
|
||||||
## `main`
|
* Alternative API Docs (ReDoc):
|
||||||
|
`http://localhost:1234/redoc`
|
||||||
Refers to the Python file name:
|
|
||||||
|
|
||||||
```text
|
|
||||||
main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
You write `main`, not `main.py`.
|
|
||||||
|
|
||||||
## `app`
|
|
||||||
|
|
||||||
Refers to the FastAPI application instance:
|
|
||||||
|
|
||||||
```python
|
|
||||||
app = FastAPI()
|
|
||||||
```
|
|
||||||
|
|
||||||
## `--reload`
|
|
||||||
|
|
||||||
Automatically restarts the server when code changes.
|
|
||||||
|
|
||||||
This should only be used in development.
|
|
||||||
|
|
||||||
## `--host 0.0.0.0`
|
|
||||||
|
|
||||||
Makes the application listen on all available network interfaces.
|
|
||||||
|
|
||||||
This is useful when running inside Docker, virtual machines, or remote servers.
|
|
||||||
|
|
||||||
## `--port 1234`
|
|
||||||
|
|
||||||
Runs the application on port `1234`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 5. Accessing the Application
|
## Best Practices
|
||||||
|
|
||||||
After starting the server, the application will be available at:
|
* Use `--reload` only in development environments
|
||||||
|
* In production, run `uvicorn` behind a process manager (e.g., systemd, Docker, Kubernetes)
|
||||||
|
* Explicitly define host and port for containerized and cloud deployments
|
||||||
|
* Keep the application entry point (`main:app`) consistent across environments
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:1234/
|
|
||||||
```
|
|
||||||
|
|
||||||
The response will be:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"message": "Home Page"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 6. Interactive API Documentation
|
|
||||||
|
|
||||||
FastAPI automatically generates API documentation.
|
|
||||||
|
|
||||||
## Swagger UI
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:1234/docs
|
|
||||||
```
|
|
||||||
|
|
||||||
Swagger UI allows you to test API endpoints directly from the browser.
|
|
||||||
|
|
||||||
## ReDoc
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:1234/redoc
|
|
||||||
```
|
|
||||||
|
|
||||||
ReDoc provides a clean documentation view for the API.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 7. Complete Example
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def home_dir():
|
|
||||||
return {"message": "Home Page"}
|
|
||||||
```
|
|
||||||
|
|
||||||
Run it with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 8. Best Practices
|
|
||||||
|
|
||||||
Use `--reload` only during development.
|
|
||||||
|
|
||||||
Use a consistent application entry point such as:
|
|
||||||
|
|
||||||
```text
|
|
||||||
main:app
|
|
||||||
```
|
|
||||||
|
|
||||||
Explicitly define the host and port in Docker, cloud, or server environments.
|
|
||||||
|
|
||||||
Use `0.0.0.0` when the application needs to be reachable from outside the local machine.
|
|
||||||
|
|
||||||
Use `127.0.0.1` or `localhost` when the application should only be accessible locally.
|
|
||||||
|
|
||||||
Do not expose development servers directly to the internet.
|
|
||||||
|
|
||||||
In production, run FastAPI behind a proper process manager, reverse proxy, or container orchestration platform.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 9. DevOps Production Note
|
|
||||||
|
|
||||||
The following command is suitable for local development:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
|
||||||
```
|
|
||||||
|
|
||||||
For production, avoid using `--reload`.
|
|
||||||
|
|
||||||
A typical production setup may include:
|
|
||||||
|
|
||||||
```text
|
|
||||||
FastAPI
|
|
||||||
Uvicorn or Gunicorn with Uvicorn workers
|
|
||||||
Nginx or Traefik
|
|
||||||
Docker or Kubernetes
|
|
||||||
Logging and monitoring
|
|
||||||
Health checks
|
|
||||||
Environment-based configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
Example production-style command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --host 0.0.0.0 --port 1234
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
# FastAPI – GET Endpoints and JSON Responses
|
# FastAPI – GET Endpoints and JSON Responses
|
||||||
|
|
||||||
## Overview
|
This document demonstrates how to define multiple **GET endpoints** in FastAPI, return JSON responses, and use **path parameters** to retrieve specific data from an in-memory dataset.
|
||||||
|
|
||||||
This document explains how to create multiple `GET` endpoints in FastAPI, return JSON responses, and use path parameters to retrieve specific data from an in-memory dataset.
|
|
||||||
|
|
||||||
The example uses a simple list of users to simulate a database.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 1. Example Application
|
## Example Application
|
||||||
|
|
||||||
Create or update `main.py` with the following content:
|
Create or update `main.py` with the following content:
|
||||||
|
|
||||||
@@ -39,38 +35,24 @@ def get_user_by_name(name_input: str):
|
|||||||
for item in users:
|
for item in users:
|
||||||
if item["name"] == name_input:
|
if item["name"] == name_input:
|
||||||
return {"information": item}
|
return {"information": item}
|
||||||
|
|
||||||
return {"message": "User not found"}
|
return {"message": "User not found"}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 2. Application Initialization
|
## Code Overview
|
||||||
|
|
||||||
|
### Application Initialization
|
||||||
|
|
||||||
```python
|
```python
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
```
|
```
|
||||||
|
|
||||||
This creates the main FastAPI application instance.
|
Initializes the FastAPI application instance.
|
||||||
|
|
||||||
The variable `app` is used by `uvicorn` when starting the service.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload
|
|
||||||
```
|
|
||||||
|
|
||||||
In `main:app`:
|
|
||||||
|
|
||||||
```text
|
|
||||||
main = Python file name
|
|
||||||
app = FastAPI application instance
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 3. In-Memory Data Store
|
### In-Memory Data Store
|
||||||
|
|
||||||
```python
|
```python
|
||||||
users = [
|
users = [
|
||||||
@@ -80,64 +62,21 @@ users = [
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
This list is used as temporary storage.
|
* Simulates a database using a Python list
|
||||||
|
* Each user is represented as a JSON-compatible dictionary
|
||||||
Each user is represented as a Python dictionary.
|
* Suitable for development and testing purposes
|
||||||
|
|
||||||
FastAPI can automatically convert these dictionaries into JSON responses.
|
|
||||||
|
|
||||||
Example Python object:
|
|
||||||
|
|
||||||
```python
|
|
||||||
{"name": "abbas", "age": 20}
|
|
||||||
```
|
|
||||||
|
|
||||||
Example JSON response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "abbas",
|
|
||||||
"age": 20
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Important Note
|
|
||||||
|
|
||||||
This in-memory list is only suitable for learning, development, and testing.
|
|
||||||
|
|
||||||
If the application restarts, the data will be reset.
|
|
||||||
|
|
||||||
For production, use a persistent database such as PostgreSQL, MySQL, MongoDB, or another database system.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 4. Defined Endpoints
|
## Defined Endpoints
|
||||||
|
|
||||||
The application defines three `GET` endpoints:
|
### Root Endpoint
|
||||||
|
|
||||||
```http
|
|
||||||
GET /
|
|
||||||
GET /users
|
|
||||||
GET /user/{name_input}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 5. Root Endpoint
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/")
|
|
||||||
def root_dir():
|
|
||||||
return {"message": "API is working"}
|
|
||||||
```
|
|
||||||
|
|
||||||
This endpoint is available at:
|
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET /
|
GET /
|
||||||
```
|
```
|
||||||
|
|
||||||
It returns a simple JSON response:
|
**Response:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -145,96 +84,43 @@ It returns a simple JSON response:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This type of endpoint is commonly used as a simple health check.
|
Used as a health check or readiness probe.
|
||||||
|
|
||||||
For example, load balancers, monitoring systems, or Kubernetes probes can use it to check whether the API is reachable.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 6. Get All Users Endpoint
|
### Get All Users
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/users")
|
|
||||||
def get_users():
|
|
||||||
return users
|
|
||||||
```
|
|
||||||
|
|
||||||
This endpoint is available at:
|
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET /users
|
GET /users
|
||||||
```
|
```
|
||||||
|
|
||||||
It returns the full list of users.
|
**Response:**
|
||||||
|
|
||||||
Example response:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{"name": "abbas", "age": 20},
|
||||||
"name": "abbas",
|
{"name": "mmd", "age": 37},
|
||||||
"age": 20
|
{"name": "asghar", "age": 19}
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mmd",
|
|
||||||
"age": 37
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "asghar",
|
|
||||||
"age": 19
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
FastAPI automatically serializes the Python list into a JSON array.
|
Returns the full list of users as JSON.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 7. Get User by Name Endpoint
|
### Get User by Name (Path Parameter)
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/user/{name_input}")
|
|
||||||
def get_user_by_name(name_input: str):
|
|
||||||
for item in users:
|
|
||||||
if item["name"] == name_input:
|
|
||||||
return {"information": item}
|
|
||||||
|
|
||||||
return {"message": "User not found"}
|
|
||||||
```
|
|
||||||
|
|
||||||
This endpoint is available at:
|
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET /user/{name_input}
|
GET /user/{name_input}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `{name_input}` part is a path parameter.
|
**Example Request:**
|
||||||
|
|
||||||
Example request:
|
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET /user/abbas
|
GET /user/abbas
|
||||||
```
|
```
|
||||||
|
|
||||||
In this request, FastAPI assigns:
|
**Successful Response:**
|
||||||
|
|
||||||
```python
|
|
||||||
name_input = "abbas"
|
|
||||||
```
|
|
||||||
|
|
||||||
The function then searches the `users` list for a user whose name matches the input.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 8. Successful Response Example
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /user/abbas
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -245,17 +131,7 @@ Response:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
**Failure Response:**
|
||||||
|
|
||||||
# 9. Failure Response Example
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /user/ali
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -263,304 +139,31 @@ Response:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the current simple version, the API returns a normal JSON message even when the user does not exist.
|
---
|
||||||
|
|
||||||
However, in a real API, it is better to return a proper `404 Not Found` response.
|
## Path Parameters
|
||||||
|
|
||||||
|
* `name_input` is a dynamic path parameter
|
||||||
|
* Automatically validated and converted to `str` by FastAPI
|
||||||
|
* Used to filter data at runtime
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 10. Path Parameters
|
## Running the Application
|
||||||
|
|
||||||
A path parameter is a dynamic part of the URL.
|
Use `uvicorn` to start the service:
|
||||||
|
|
||||||
In this route:
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/user/{name_input}")
|
|
||||||
```
|
|
||||||
|
|
||||||
`name_input` is a path parameter.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /user/mmd
|
|
||||||
```
|
|
||||||
|
|
||||||
FastAPI extracts the value from the URL and passes it to the function:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def get_user_by_name(name_input: str):
|
|
||||||
```
|
|
||||||
|
|
||||||
Because `name_input` is defined as a string:
|
|
||||||
|
|
||||||
```python
|
|
||||||
name_input: str
|
|
||||||
```
|
|
||||||
|
|
||||||
FastAPI validates it as a string.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 11. Better Version with HTTPException
|
|
||||||
|
|
||||||
The previous version works, but it does not return the correct HTTP status code when a user is not found.
|
|
||||||
|
|
||||||
A better version uses `HTTPException`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI, HTTPException, status
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
users = [
|
|
||||||
{"name": "abbas", "age": 20},
|
|
||||||
{"name": "mmd", "age": 37},
|
|
||||||
{"name": "asghar", "age": 19},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def root_dir():
|
|
||||||
return {"message": "API is working"}
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/users")
|
|
||||||
def get_users():
|
|
||||||
return users
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/user/{name_input}")
|
|
||||||
def get_user_by_name(name_input: str):
|
|
||||||
for item in users:
|
|
||||||
if item["name"] == name_input:
|
|
||||||
return {"information": item}
|
|
||||||
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail="User not found"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 12. Improved Failure Response
|
|
||||||
|
|
||||||
Request:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /user/ali
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"detail": "User not found"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
HTTP status code:
|
|
||||||
|
|
||||||
```http
|
|
||||||
404 Not Found
|
|
||||||
```
|
|
||||||
|
|
||||||
This is better because the API response now correctly tells the client that the requested resource does not exist.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 13. Running the Application
|
|
||||||
|
|
||||||
Start the FastAPI application using `uvicorn`:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uvicorn main:app --reload
|
uvicorn main:app --reload
|
||||||
```
|
```
|
||||||
|
|
||||||
The application will be available at:
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 14. Accessing the Endpoints
|
## Best Practices
|
||||||
|
|
||||||
Root endpoint:
|
* Use structured JSON responses (`key: value`) instead of tuples
|
||||||
|
* Validate user input when moving beyond in-memory data
|
||||||
|
* Replace in-memory storage with a database for production
|
||||||
|
* Use proper HTTP status codes (`404`, `200`) in real-world APIs
|
||||||
|
* Separate routing, models, and business logic as the project grows
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000/
|
|
||||||
```
|
|
||||||
|
|
||||||
Get all users:
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000/users
|
|
||||||
```
|
|
||||||
|
|
||||||
Get user by name:
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000/user/abbas
|
|
||||||
```
|
|
||||||
|
|
||||||
Interactive API documentation:
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000/docs
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternative API documentation:
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000/redoc
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 15. Testing with curl
|
|
||||||
|
|
||||||
## Test Root Endpoint
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl http://localhost:8000/
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"message": "API is working"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Get All Users
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl http://localhost:8000/users
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "abbas",
|
|
||||||
"age": 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mmd",
|
|
||||||
"age": 37
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "asghar",
|
|
||||||
"age": 19
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test Get User by Name
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl http://localhost:8000/user/abbas
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"information": {
|
|
||||||
"name": "abbas",
|
|
||||||
"age": 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Test User Not Found
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl http://localhost:8000/user/ali
|
|
||||||
```
|
|
||||||
|
|
||||||
Response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"detail": "User not found"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 16. Complete Recommended Version
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI, HTTPException, status
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
users = [
|
|
||||||
{"name": "abbas", "age": 20},
|
|
||||||
{"name": "mmd", "age": 37},
|
|
||||||
{"name": "asghar", "age": 19},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def root_dir():
|
|
||||||
return {"message": "API is working"}
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/users")
|
|
||||||
def get_users():
|
|
||||||
return users
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/user/{name_input}")
|
|
||||||
def get_user_by_name(name_input: str):
|
|
||||||
for item in users:
|
|
||||||
if item["name"] == name_input:
|
|
||||||
return {"information": item}
|
|
||||||
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
|
||||||
detail="User not found"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 17. Best Practices
|
|
||||||
|
|
||||||
Use `GET` endpoints only for retrieving data.
|
|
||||||
|
|
||||||
Do not use `GET` requests to create, update, or delete resources.
|
|
||||||
|
|
||||||
Return structured JSON responses.
|
|
||||||
|
|
||||||
Use proper HTTP status codes.
|
|
||||||
|
|
||||||
Use `404 Not Found` when a requested resource does not exist.
|
|
||||||
|
|
||||||
Use `200 OK` when the request is successful.
|
|
||||||
|
|
||||||
Replace in-memory lists with a real database in production.
|
|
||||||
|
|
||||||
Keep route names clear and consistent.
|
|
||||||
|
|
||||||
Use plural naming for collection endpoints, such as:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /users
|
|
||||||
```
|
|
||||||
|
|
||||||
Use specific resource endpoints for single items, such as:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /users/{username}
|
|
||||||
```
|
|
||||||
|
|
||||||
As the project grows, separate code into different files for routes, models, services, and database logic.
|
|
||||||
|
|||||||
@@ -1,19 +1,12 @@
|
|||||||
# FastAPI – POST Endpoint and JSON Input
|
# FastAPI – POST Endpoint and JSON Input
|
||||||
|
|
||||||
## Overview
|
This section demonstrates how to handle **POST requests** in FastAPI to create new resources using request data and return JSON responses.
|
||||||
|
|
||||||
This document explains how to create a `POST` endpoint in FastAPI for adding new users. It covers two approaches:
|
|
||||||
|
|
||||||
1. Sending input as query parameters
|
|
||||||
2. Sending input as a JSON request body using a Pydantic model
|
|
||||||
|
|
||||||
For real API development, the Pydantic model approach is recommended because it provides a cleaner structure, better validation, and more realistic request handling.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 1. Example Application Using Query Parameters
|
## Example Application (POST Request)
|
||||||
|
|
||||||
Create or update `main.py` with the following code:
|
Extend `main.py` with the following code:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
@@ -39,172 +32,59 @@ def create_user(name: str, age: int):
|
|||||||
return {"msg": "User created successfully"}
|
return {"msg": "User created successfully"}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Explanation
|
---
|
||||||
|
|
||||||
|
## Code Overview
|
||||||
|
|
||||||
|
### POST Endpoint Definition
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@app.post("/new_user")
|
@app.post("/new_user")
|
||||||
def create_user(name: str, age: int):
|
def create_user(name: str, age: int):
|
||||||
```
|
```
|
||||||
|
|
||||||
This registers a `POST` endpoint at:
|
* Registers an HTTP **POST** endpoint at `/new_user`
|
||||||
|
* Accepts input parameters:
|
||||||
|
|
||||||
```http
|
* `name` → string
|
||||||
POST /new_user
|
* `age` → integer
|
||||||
```
|
* FastAPI automatically validates input types
|
||||||
|
|
||||||
The endpoint receives two parameters:
|
|
||||||
|
|
||||||
```python
|
|
||||||
name: str
|
|
||||||
age: int
|
|
||||||
```
|
|
||||||
|
|
||||||
FastAPI automatically validates the input types. If `age` is not an integer, FastAPI returns a validation error.
|
|
||||||
|
|
||||||
## Example Request
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST "http://localhost:8000/new_user?name=ali&age=25"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example Response
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"msg": "User created successfully"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Important Note
|
|
||||||
|
|
||||||
In this version, the data is sent through query parameters, not as a JSON body.
|
|
||||||
|
|
||||||
This is acceptable for simple testing, but it is not the best practice for real APIs that create resources.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 2. Recommended Application Using JSON Body
|
### Creating a New Resource
|
||||||
|
|
||||||
A better approach is to define a Pydantic model and receive the user data as JSON.
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from fastapi import FastAPI
|
new_user = {"name": name, "age": age}
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
class User(BaseModel):
|
|
||||||
name: str
|
|
||||||
age: int
|
|
||||||
|
|
||||||
|
|
||||||
users = [
|
|
||||||
{"name": "abbas", "age": 20},
|
|
||||||
{"name": "mmd", "age": 37},
|
|
||||||
{"name": "asghar", "age": 19},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def home_page():
|
|
||||||
return {"msg": "API is working"}
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/new_user")
|
|
||||||
def create_user(user: User):
|
|
||||||
new_user = {"name": user.name, "age": user.age}
|
|
||||||
users.append(new_user)
|
|
||||||
return {"msg": "User created successfully"}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Explanation
|
|
||||||
|
|
||||||
```python
|
|
||||||
class User(BaseModel):
|
|
||||||
name: str
|
|
||||||
age: int
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates a request model named `User`.
|
|
||||||
|
|
||||||
The model defines the expected structure of the JSON input:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "ali",
|
|
||||||
"age": 25
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
FastAPI uses this model to:
|
|
||||||
|
|
||||||
Validate incoming data
|
|
||||||
Convert JSON into a Python object
|
|
||||||
Generate automatic API documentation
|
|
||||||
Return useful validation errors when input is invalid
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 3. POST Endpoint Definition
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.post("/new_user")
|
|
||||||
def create_user(user: User):
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates a `POST` endpoint at:
|
|
||||||
|
|
||||||
```http
|
|
||||||
POST /new_user
|
|
||||||
```
|
|
||||||
|
|
||||||
The endpoint expects a JSON request body matching the `User` model.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 4. Creating a New Resource
|
|
||||||
|
|
||||||
```python
|
|
||||||
new_user = {"name": user.name, "age": user.age}
|
|
||||||
users.append(new_user)
|
users.append(new_user)
|
||||||
```
|
```
|
||||||
|
|
||||||
This code creates a new dictionary using the received request data and appends it to the `users` list.
|
* Constructs a new user object
|
||||||
|
* Appends it to the in-memory `users` list
|
||||||
The `users` list acts as temporary in-memory storage.
|
* Simulates creating a record in a database
|
||||||
|
|
||||||
This simulates inserting a new record into a database.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 5. Returning a JSON Response
|
### JSON Response
|
||||||
|
|
||||||
```python
|
```python
|
||||||
return {"msg": "User created successfully"}
|
return {"msg": "User created successfully"}
|
||||||
```
|
```
|
||||||
|
|
||||||
FastAPI automatically converts the returned dictionary into a JSON response.
|
* Returns a structured JSON response
|
||||||
|
* Automatically serialized by FastAPI
|
||||||
Example response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"msg": "User created successfully"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 6. Example Request Using curl
|
## Example Request
|
||||||
|
|
||||||
|
### Using `curl`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST "http://localhost:8000/new_user" \
|
curl -X POST "http://localhost:8000/new_user?name=ali&age=25"
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"name": "ali", "age": 25}'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Response
|
### Response
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -214,156 +94,34 @@ curl -X POST "http://localhost:8000/new_user" \
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 7. Verifying the Result
|
## Verifying the Result
|
||||||
|
|
||||||
To verify that the user was added, you should also define a `GET /users` endpoint.
|
After creating a user, retrieve the updated list:
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/users")
|
|
||||||
def get_users():
|
|
||||||
return users
|
|
||||||
```
|
|
||||||
|
|
||||||
Then send this request:
|
|
||||||
|
|
||||||
```http
|
```http
|
||||||
GET /users
|
GET /users
|
||||||
```
|
```
|
||||||
|
|
||||||
Example response:
|
Response will now include the newly added user.
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "abbas",
|
|
||||||
"age": 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "mmd",
|
|
||||||
"age": 37
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "asghar",
|
|
||||||
"age": 19
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ali",
|
|
||||||
"age": 25
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 8. Complete Recommended Version
|
## Running the Application
|
||||||
|
|
||||||
```python
|
Start the FastAPI service using `uvicorn`:
|
||||||
from fastapi import FastAPI, status
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
class User(BaseModel):
|
|
||||||
name: str
|
|
||||||
age: int
|
|
||||||
|
|
||||||
|
|
||||||
users = [
|
|
||||||
{"name": "abbas", "age": 20},
|
|
||||||
{"name": "mmd", "age": 37},
|
|
||||||
{"name": "asghar", "age": 19},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def home_page():
|
|
||||||
return {"msg": "API is working"}
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/users")
|
|
||||||
def get_users():
|
|
||||||
return users
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/new_user", status_code=status.HTTP_201_CREATED)
|
|
||||||
def create_user(user: User):
|
|
||||||
new_user = {"name": user.name, "age": user.age}
|
|
||||||
users.append(new_user)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"msg": "User created successfully",
|
|
||||||
"user": new_user
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 9. Running the Application
|
|
||||||
|
|
||||||
Start the FastAPI application using `uvicorn`:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uvicorn main:app --reload
|
uvicorn main:app --reload
|
||||||
```
|
```
|
||||||
|
|
||||||
The API will run at:
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000
|
|
||||||
```
|
|
||||||
|
|
||||||
Interactive API documentation will be available at:
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000/docs
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 10. Best Practices
|
## Best Practices
|
||||||
|
|
||||||
Use `POST` requests when creating new resources.
|
* POST requests should be used to create resources
|
||||||
|
* Avoid modifying in-memory data in production environments
|
||||||
|
* Use request bodies with Pydantic models instead of query parameters for real APIs
|
||||||
|
* Return appropriate HTTP status codes (`201 Created`)
|
||||||
|
* Validate and sanitize all client-provided input
|
||||||
|
* Replace in-memory storage with persistent databases
|
||||||
|
|
||||||
Use Pydantic models for request bodies instead of query parameters.
|
|
||||||
|
|
||||||
Return proper HTTP status codes, such as:
|
|
||||||
|
|
||||||
```http
|
|
||||||
201 Created
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not use in-memory lists for production data storage.
|
|
||||||
|
|
||||||
Use a real database such as PostgreSQL, MySQL, or MongoDB in production.
|
|
||||||
|
|
||||||
Validate and sanitize all client-provided input.
|
|
||||||
|
|
||||||
Keep model names clear and professional. For example, use `User` instead of `user_class`.
|
|
||||||
|
|
||||||
Return meaningful responses that include the created resource when useful.
|
|
||||||
|
|
||||||
Use `/docs` to test and verify API behavior during development.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 11. Production Note for DevOps
|
|
||||||
|
|
||||||
The in-memory `users` list is reset every time the application restarts. In production environments, application data must be stored in a persistent database.
|
|
||||||
|
|
||||||
For production deployment, the FastAPI application should usually run behind a process manager and reverse proxy, such as:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Gunicorn / Uvicorn workers
|
|
||||||
Nginx or Traefik
|
|
||||||
Docker or Kubernetes
|
|
||||||
PostgreSQL or another persistent database
|
|
||||||
```
|
|
||||||
|
|
||||||
The development command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload
|
|
||||||
```
|
|
||||||
|
|
||||||
should not be used in production because `--reload` is intended only for local development.
|
|
||||||
|
|||||||
122
Docs/Libs/FastAPI/09-Json-Responce.md
Normal file
122
Docs/Libs/FastAPI/09-Json-Responce.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# FastAPI – JSONResponse (Explicit JSON Responses)
|
||||||
|
|
||||||
|
This document demonstrates how to return **explicit JSON responses** in FastAPI using `JSONResponse`.
|
||||||
|
While FastAPI automatically serializes dictionaries to JSON, `JSONResponse` is useful when you need **full control** over the response.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example Application
|
||||||
|
|
||||||
|
Create or update `main.py` with the following content:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, status
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def home():
|
||||||
|
return JSONResponse(
|
||||||
|
content={"msg": "API is working"},
|
||||||
|
status_code=status.HTTP_200_OK
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Why Use `JSONResponse`
|
||||||
|
|
||||||
|
FastAPI automatically converts Python dictionaries into JSON responses.
|
||||||
|
However, `JSONResponse` is useful when you need to:
|
||||||
|
|
||||||
|
* Explicitly control the response type
|
||||||
|
* Set custom status codes dynamically
|
||||||
|
* Customize headers
|
||||||
|
* Return non-standard JSON structures
|
||||||
|
* Override default response behavior
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Response Behavior
|
||||||
|
|
||||||
|
### Endpoint
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response Body
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "API is working"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTTP Status Code
|
||||||
|
|
||||||
|
```
|
||||||
|
200 OK
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comparison: Default Response vs JSONResponse
|
||||||
|
|
||||||
|
### Default FastAPI Response
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.get("/")
|
||||||
|
def home():
|
||||||
|
return {"msg": "API is working"}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Automatically serialized to JSON
|
||||||
|
* Simpler and recommended for most cases
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explicit JSONResponse
|
||||||
|
|
||||||
|
```python
|
||||||
|
return JSONResponse(
|
||||||
|
content={"msg": "API is working"},
|
||||||
|
status_code=status.HTTP_200_OK
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
* Explicit control over response
|
||||||
|
* Useful for advanced use cases
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When to Use `JSONResponse`
|
||||||
|
|
||||||
|
* Returning conditional status codes
|
||||||
|
* Adding custom headers
|
||||||
|
* Returning responses outside standard request flow
|
||||||
|
* Building middleware or exception handlers
|
||||||
|
* Integrating with legacy systems
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Running the Application
|
||||||
|
|
||||||
|
Start the application using `uvicorn`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
* Prefer returning dictionaries for simple APIs
|
||||||
|
* Use `JSONResponse` only when additional control is required
|
||||||
|
* Keep response formats consistent across endpoints
|
||||||
|
* Avoid mixing response styles unnecessarily
|
||||||
|
* Use response models for structured APIs
|
||||||
|
|
||||||
@@ -1,514 +0,0 @@
|
|||||||
# FastAPI – Response Model and JSONResponse
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This document explains two important FastAPI response concepts:
|
|
||||||
|
|
||||||
1. Using `response_model` to control what data is returned to the client
|
|
||||||
2. Using `JSONResponse` when you need explicit control over the response body, status code, or headers
|
|
||||||
|
|
||||||
Response models are especially useful when you want to hide sensitive fields such as passwords from API responses.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 1. Response Model
|
|
||||||
|
|
||||||
## Incorrect Example
|
|
||||||
|
|
||||||
The following code has several issues:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI, status , HTTPExption
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
class usersin(BaseModel):
|
|
||||||
username: str
|
|
||||||
pass: str
|
|
||||||
|
|
||||||
class usersout(BaseModel):
|
|
||||||
username: str
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/user")
|
|
||||||
def home(user: usersin, responce_model=usersout):
|
|
||||||
return user
|
|
||||||
```
|
|
||||||
|
|
||||||
## Problems in the Code
|
|
||||||
|
|
||||||
### 1. `HTTPExption` is misspelled
|
|
||||||
|
|
||||||
Correct import:
|
|
||||||
|
|
||||||
```python
|
|
||||||
HTTPException
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. `pass` cannot be used as a field name
|
|
||||||
|
|
||||||
`pass` is a reserved keyword in Python.
|
|
||||||
|
|
||||||
Instead of:
|
|
||||||
|
|
||||||
```python
|
|
||||||
pass: str
|
|
||||||
```
|
|
||||||
|
|
||||||
Use:
|
|
||||||
|
|
||||||
```python
|
|
||||||
password: str
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. `response_model` is written in the wrong place
|
|
||||||
|
|
||||||
This is incorrect:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def home(user: usersin, responce_model=usersout):
|
|
||||||
```
|
|
||||||
|
|
||||||
`response_model` must be passed inside the route decorator.
|
|
||||||
|
|
||||||
Correct:
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.post("/user", response_model=UserOut)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. `responce_model` is misspelled
|
|
||||||
|
|
||||||
Correct spelling:
|
|
||||||
|
|
||||||
```python
|
|
||||||
response_model
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 2. Correct Response Model Example
|
|
||||||
|
|
||||||
Create or update `main.py` with the following content:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
class UserIn(BaseModel):
|
|
||||||
username: str
|
|
||||||
password: str
|
|
||||||
|
|
||||||
|
|
||||||
class UserOut(BaseModel):
|
|
||||||
username: str
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/user", response_model=UserOut)
|
|
||||||
def create_user(user: UserIn):
|
|
||||||
return user
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 3. How Response Model Works
|
|
||||||
|
|
||||||
## Input Model
|
|
||||||
|
|
||||||
```python
|
|
||||||
class UserIn(BaseModel):
|
|
||||||
username: str
|
|
||||||
password: str
|
|
||||||
```
|
|
||||||
|
|
||||||
This model defines the data that the API receives from the client.
|
|
||||||
|
|
||||||
Example request body:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"username": "abbas",
|
|
||||||
"password": "123456"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The API accepts both fields:
|
|
||||||
|
|
||||||
```text
|
|
||||||
username
|
|
||||||
password
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Output Model
|
|
||||||
|
|
||||||
```python
|
|
||||||
class UserOut(BaseModel):
|
|
||||||
username: str
|
|
||||||
```
|
|
||||||
|
|
||||||
This model defines the data that the API returns to the client.
|
|
||||||
|
|
||||||
Even though the endpoint receives the password, the response only returns the username.
|
|
||||||
|
|
||||||
Example response:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"username": "abbas"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The password is removed from the response automatically.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 4. Endpoint Definition
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.post("/user", response_model=UserOut)
|
|
||||||
def create_user(user: UserIn):
|
|
||||||
return user
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates a `POST` endpoint at:
|
|
||||||
|
|
||||||
```http
|
|
||||||
POST /user
|
|
||||||
```
|
|
||||||
|
|
||||||
The endpoint receives data based on `UserIn` and returns data based on `UserOut`.
|
|
||||||
|
|
||||||
FastAPI uses `response_model` to filter the response before sending it to the client.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 5. Example Request
|
|
||||||
|
|
||||||
## Using curl
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST "http://localhost:8000/user" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"username": "abbas", "password": "123456"}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Response
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"username": "abbas"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The password is not included in the response.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 6. Why Use Response Models
|
|
||||||
|
|
||||||
Response models are useful because they help you:
|
|
||||||
|
|
||||||
Protect sensitive data
|
|
||||||
Keep API responses consistent
|
|
||||||
Control exactly what the client receives
|
|
||||||
Improve automatic documentation
|
|
||||||
Validate response data before sending it
|
|
||||||
Separate input schemas from output schemas
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 7. Better Version with Status Code
|
|
||||||
|
|
||||||
For user creation endpoints, it is better to return `201 Created`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI, status
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
class UserIn(BaseModel):
|
|
||||||
username: str
|
|
||||||
password: str
|
|
||||||
|
|
||||||
|
|
||||||
class UserOut(BaseModel):
|
|
||||||
username: str
|
|
||||||
|
|
||||||
|
|
||||||
@app.post(
|
|
||||||
"/user",
|
|
||||||
response_model=UserOut,
|
|
||||||
status_code=status.HTTP_201_CREATED
|
|
||||||
)
|
|
||||||
def create_user(user: UserIn):
|
|
||||||
return user
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 8. JSONResponse
|
|
||||||
|
|
||||||
FastAPI automatically converts Python dictionaries into JSON responses.
|
|
||||||
|
|
||||||
For most endpoints, this is enough:
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/")
|
|
||||||
def home():
|
|
||||||
return {"msg": "API is working"}
|
|
||||||
```
|
|
||||||
|
|
||||||
However, FastAPI also allows you to use `JSONResponse` when you need more control.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 9. Example Application Using JSONResponse
|
|
||||||
|
|
||||||
Create or update `main.py` with the following content:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI, status
|
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def home():
|
|
||||||
return JSONResponse(
|
|
||||||
content={"msg": "API is working"},
|
|
||||||
status_code=status.HTTP_200_OK
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 10. Response Behavior
|
|
||||||
|
|
||||||
## Endpoint
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /
|
|
||||||
```
|
|
||||||
|
|
||||||
## Response Body
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"msg": "API is working"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## HTTP Status Code
|
|
||||||
|
|
||||||
```http
|
|
||||||
200 OK
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 11. Default Response vs JSONResponse
|
|
||||||
|
|
||||||
## Default FastAPI Response
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/")
|
|
||||||
def home():
|
|
||||||
return {"msg": "API is working"}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is the recommended style for most simple APIs.
|
|
||||||
|
|
||||||
FastAPI automatically serializes the dictionary into JSON.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Explicit JSONResponse
|
|
||||||
|
|
||||||
```python
|
|
||||||
return JSONResponse(
|
|
||||||
content={"msg": "API is working"},
|
|
||||||
status_code=status.HTTP_200_OK
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
This gives more direct control over the response.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 12. When to Use JSONResponse
|
|
||||||
|
|
||||||
Use `JSONResponse` when you need to:
|
|
||||||
|
|
||||||
Return dynamic status codes
|
|
||||||
Add custom headers
|
|
||||||
Customize the response structure manually
|
|
||||||
Return responses from exception handlers
|
|
||||||
Return responses from middleware
|
|
||||||
Override FastAPI’s default response behavior
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 13. Example: JSONResponse with Custom Status Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI, status
|
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/login")
|
|
||||||
def login():
|
|
||||||
return JSONResponse(
|
|
||||||
content={"msg": "Login successful"},
|
|
||||||
status_code=status.HTTP_200_OK
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 14. Example: JSONResponse with Headers
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI
|
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/custom")
|
|
||||||
def custom_response():
|
|
||||||
return JSONResponse(
|
|
||||||
content={"msg": "Custom response"},
|
|
||||||
headers={"X-App-Version": "1.0.0"}
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 15. Complete Example with Response Model and JSONResponse
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI, status
|
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
class UserIn(BaseModel):
|
|
||||||
username: str
|
|
||||||
password: str
|
|
||||||
|
|
||||||
|
|
||||||
class UserOut(BaseModel):
|
|
||||||
username: str
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def home():
|
|
||||||
return JSONResponse(
|
|
||||||
content={"msg": "API is working"},
|
|
||||||
status_code=status.HTTP_200_OK
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@app.post(
|
|
||||||
"/user",
|
|
||||||
response_model=UserOut,
|
|
||||||
status_code=status.HTTP_201_CREATED
|
|
||||||
)
|
|
||||||
def create_user(user: UserIn):
|
|
||||||
return user
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 16. Running the Application
|
|
||||||
|
|
||||||
Start the FastAPI application using `uvicorn`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload
|
|
||||||
```
|
|
||||||
|
|
||||||
The API will run at:
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000
|
|
||||||
```
|
|
||||||
|
|
||||||
Interactive API documentation will be available at:
|
|
||||||
|
|
||||||
```text
|
|
||||||
http://localhost:8000/docs
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 17. Best Practices
|
|
||||||
|
|
||||||
Use `response_model` to control API output.
|
|
||||||
|
|
||||||
Never return sensitive data such as passwords, tokens, or secrets.
|
|
||||||
|
|
||||||
Use separate models for input and output.
|
|
||||||
|
|
||||||
Use clear class names such as `UserIn` and `UserOut`.
|
|
||||||
|
|
||||||
Use `password` instead of `pass` because `pass` is a reserved Python keyword.
|
|
||||||
|
|
||||||
Place `response_model` inside the route decorator, not inside the function parameters.
|
|
||||||
|
|
||||||
Prefer returning normal dictionaries for simple responses.
|
|
||||||
|
|
||||||
Use `JSONResponse` only when extra control is required.
|
|
||||||
|
|
||||||
Use proper HTTP status codes, such as:
|
|
||||||
|
|
||||||
```http
|
|
||||||
200 OK
|
|
||||||
201 Created
|
|
||||||
400 Bad Request
|
|
||||||
401 Unauthorized
|
|
||||||
404 Not Found
|
|
||||||
500 Internal Server Error
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not use `uvicorn --reload` in production.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 18. DevOps Production Note
|
|
||||||
|
|
||||||
In production, the FastAPI application should usually run behind a production-grade ASGI server setup and a reverse proxy.
|
|
||||||
|
|
||||||
A common production stack is:
|
|
||||||
|
|
||||||
```text
|
|
||||||
FastAPI
|
|
||||||
Gunicorn with Uvicorn workers
|
|
||||||
Nginx or Traefik
|
|
||||||
Docker or Kubernetes
|
|
||||||
PostgreSQL or another persistent database
|
|
||||||
```
|
|
||||||
|
|
||||||
The development command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload
|
|
||||||
```
|
|
||||||
|
|
||||||
is only for local development.
|
|
||||||
|
|
||||||
For production, use a more stable process configuration, such as Gunicorn with Uvicorn workers, container health checks, logging, monitoring, and proper secret management.
|
|
||||||
@@ -1,312 +0,0 @@
|
|||||||
# HTTP Requests in Python with `requests`
|
|
||||||
|
|
||||||
This document explains how to use the **`requests`** library to send HTTP requests, handle responses, work with APIs, upload/download files, manage headers, authentication, errors, and more.
|
|
||||||
|
|
||||||
> `requests` is not part of the standard library and must be installed:
|
|
||||||
```bash
|
|
||||||
pip install requests
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Sending a Basic GET Request
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
import requests
|
|
||||||
|
|
||||||
r = requests.get("http://myip.abbascloud.ir")
|
|
||||||
|
|
||||||
print(r.url)
|
|
||||||
print(r.status_code, r.ok)
|
|
||||||
print(r.text)
|
|
||||||
print(r.content)
|
|
||||||
print(r.json())
|
|
||||||
print(r.headers)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- `requests.get(url)` sends an HTTP GET request.
|
|
||||||
- `r.url`: final URL after redirects.
|
|
||||||
- `r.status_code`: HTTP status code (e.g. 200, 404).
|
|
||||||
- `r.ok`: `True` if status code is < 400.
|
|
||||||
- `r.text`: response body as string.
|
|
||||||
- `r.content`: response body as raw bytes.
|
|
||||||
- `r.json()`: parses JSON response into Python objects.
|
|
||||||
- `r.headers`: response headers as a dictionary.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Passing Query Parameters
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
params = {
|
|
||||||
"q": "python",
|
|
||||||
"page": 1
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.get("https://api.example.com/search", params=params)
|
|
||||||
print(response.url)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- `params` are appended to the URL as query strings.
|
|
||||||
- Automatically encoded by `requests`.
|
|
||||||
- Resulting URL:
|
|
||||||
```
|
|
||||||
https://api.example.com/search?q=python&page=1
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Sending POST Requests (Form Data)
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
data = {
|
|
||||||
"username": "alex",
|
|
||||||
"password": "secret"
|
|
||||||
}
|
|
||||||
|
|
||||||
requests.post(url, data=data)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- Sends data as `application/x-www-form-urlencoded`.
|
|
||||||
- Common for HTML form submissions.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Sending POST Requests (JSON)
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
json_data = {
|
|
||||||
"name": "Alex",
|
|
||||||
"age": 25
|
|
||||||
}
|
|
||||||
|
|
||||||
requests.post(url, json=json_data)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- Automatically serializes data to JSON.
|
|
||||||
- Sets `Content-Type: application/json`.
|
|
||||||
- Preferred for REST APIs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Custom Request Headers
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
headers = {
|
|
||||||
"Authorization": "Bearer YOUR_TOKEN",
|
|
||||||
"User-Agent": "MyApp/1.0",
|
|
||||||
"Accept": "application/json"
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.get(url, headers=headers)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- Used for authentication, API versioning, and content negotiation.
|
|
||||||
- Common headers:
|
|
||||||
- `Authorization`
|
|
||||||
- `User-Agent`
|
|
||||||
- `Accept`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Authentication (Basic Auth)
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
from requests.auth import HTTPBasicAuth
|
|
||||||
|
|
||||||
requests.get(
|
|
||||||
"https://api.example.com",
|
|
||||||
auth=HTTPBasicAuth("username", "password")
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- Sends credentials using HTTP Basic Authentication.
|
|
||||||
- Automatically encodes credentials in headers.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Working with Cookies
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
response = requests.get(url)
|
|
||||||
print(response.cookies)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- Cookies are stored in a `RequestsCookieJar`.
|
|
||||||
- Useful for sessions and login persistence.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Uploading Files
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
files = {
|
|
||||||
"file": open("example.txt", "rb")
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.post(url, files=files)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- Sends files as `multipart/form-data`.
|
|
||||||
- Common for file uploads to APIs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Downloading Files
|
|
||||||
|
|
||||||
### Simple Download
|
|
||||||
|
|
||||||
```python
|
|
||||||
response = requests.get("https://example.com/image.png")
|
|
||||||
|
|
||||||
with open("image.png", "wb") as f:
|
|
||||||
f.write(response.content)
|
|
||||||
```
|
|
||||||
|
|
||||||
- Downloads entire file into memory.
|
|
||||||
- Suitable for small files.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Streaming Large Files
|
|
||||||
|
|
||||||
```python
|
|
||||||
response = requests.get(url, stream=True)
|
|
||||||
|
|
||||||
with open("big.zip", "wb") as f:
|
|
||||||
for chunk in response.iter_content(chunk_size=8192):
|
|
||||||
f.write(chunk)
|
|
||||||
```
|
|
||||||
|
|
||||||
- Downloads file in chunks.
|
|
||||||
- Prevents high memory usage.
|
|
||||||
- Recommended for large files.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Timeout and Error Handling
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
try:
|
|
||||||
response = requests.get(url, timeout=5)
|
|
||||||
response.raise_for_status()
|
|
||||||
except requests.exceptions.Timeout:
|
|
||||||
print("Request timed out")
|
|
||||||
except requests.exceptions.HTTPError as e:
|
|
||||||
print("HTTP error:", e)
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
print("Request failed:", e)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- `timeout`: maximum wait time (seconds).
|
|
||||||
- `raise_for_status()`: raises exception for 4xx / 5xx errors.
|
|
||||||
- `RequestException`: base class for all request errors.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. Using Proxies
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
proxies = {
|
|
||||||
"http": "http://127.0.0.1:8080",
|
|
||||||
"https": "http://127.0.0.1:8080"
|
|
||||||
}
|
|
||||||
|
|
||||||
requests.get(url, proxies=proxies)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- Routes requests through a proxy server.
|
|
||||||
- Useful for debugging, privacy, or corporate networks.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. SSL Verification
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
requests.get(url, verify=True) # default
|
|
||||||
requests.get(url, verify=False) # NOT recommended
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- `verify=True` checks SSL certificates.
|
|
||||||
- Disabling SSL verification is insecure and should only be used for testing.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 13. Real API Example (GitHub)
|
|
||||||
|
|
||||||
### Code
|
|
||||||
|
|
||||||
```python
|
|
||||||
import requests
|
|
||||||
|
|
||||||
API_URL = "https://api.github.com/users/octocat"
|
|
||||||
|
|
||||||
response = requests.get(API_URL)
|
|
||||||
|
|
||||||
if response.ok:
|
|
||||||
user = response.json()
|
|
||||||
print(user["login"], user["public_repos"])
|
|
||||||
else:
|
|
||||||
print("Error:", response.status_code)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
- Sends a GET request to GitHub’s public API.
|
|
||||||
- Parses JSON response.
|
|
||||||
- Accesses specific fields safely.
|
|
||||||
- Checks request success using `response.ok`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
- `requests` simplifies HTTP communication
|
|
||||||
- Supports GET, POST, headers, params, JSON, files
|
|
||||||
- Handles authentication, cookies, proxies, and SSL
|
|
||||||
- Built-in error handling improves reliability
|
|
||||||
- Widely used for REST APIs and web services
|
|
||||||
Reference in New Issue
Block a user