---
# System prepended metadata

title: Django學習記錄
tags: [誠式碎碎念]

---

# Django學習記錄
太久沒有碰網頁後端框架了，真的忘的很快，記錄一下各項配製方法。
而且網路上很多Django的教學為舊版本，希望這篇教學可以幫助到有需要的人們！！
這篇僅是Django最基礎的操作以及運作原理，幫助大家快速上手這個好用的後端框架。
## 0. Why Django? Django和PHP的差別？
Django是一個以Python構建的後端大型框架，可以用各種Python的Package做一些進階功能，而且Python處理字串的能力遠比PHP強的多。
Django是一種MTV的框架:
![MTV框架圖](https://miro.medium.com/v2/resize:fit:992/format:webp/0*7C7POqWkWX4ThOBK.png)  
整個框架由Model(資料庫), template(html), View(前端畫面)構成。不同於PHP一個檔案對應到一個網頁(後端)，Django則是view.py內的一個function對應到一個網頁，多個function組成一個app,所有app有組成一個Project。
Django的另一特點就是所有的路徑都由後端設定，而不仰仗于檔案實際的位置，因此就算某些檔案路徑不同了也不會影響到網頁。
## 1. 關於Django的Project和App
### Project
一個Project共用網路層面的設定，也就server的主要設定，例如資料庫、網頁Request、或是Url導向。
建立Django project指令如下：

```bash!
django-admin startproject project_name
```
會產生以下的檔案：
```
project_name
├── manage.py //指令集
└── project_name
    ├── settings.py //主要設定
    ├── urls.py // 網址路徑設定
    ├── wsgi.py //web request 設定
    ├── asgi.py //asynchronize request 設定
```
看不懂檔案的用途沒關係，僅有settings.py比較常用到而已。
wsgi.py用來設定要連到哪一個server(通常是Nginx)。
Project建好使用runserver指令：
```bash!
python manage.py runserver
```
localhost:8000 如果有以下畫面就是成功了！！
![截圖 2024-02-05 下午5.29.34](https://hackmd.io/_uploads/H1r9qXA56.png)
備註：
這個runserver只是跑一個暫時性的python server, 方便debug而已。其效能跟安全性都非常差，要上架時還是會需要其他server幫助！！

### App
一個App就是多個功能比較相近的網頁，那究竟哪些網頁才算是功能相近？這沒有一定的規則，只要自己覺得好管理就可以了。
當然想要把所有網頁放在同一個App或是一個網頁一個App也是沒問題的啦～。  
創建App的指令：
```bash!
python manage.py startapp app_name
```
記得要跟manage.py同一個路徑下執行。
整個專案資料夾會變成這樣：
```
project_name
├── manage.py //指令集
├── project_name
└── app_name
    ├── admin.py
    ├── apps.py
    ├── models.py
    ├── tests.py
    └── views.py
```
最後記得把新創建的App加到settings.py的installed_app列表內：
```python!
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "app_name", #新創建的app
]
```
1. admin.py
用來設定Django admin內要顯示的東西，也可以自定義一些csv匯入之類，詳細會在後面Django admin細講。
2. apps.py
幾乎不會動到的設定，可以定義app啟動時的一些function等等。
3. models.py
資料庫的table定義，後面資料庫操作會再細談。
4. tests.py
用來寫一些測試小腳本的檔案，可以用來測試資料庫又不影響到真正的資料，不過我也不會用QQ。
官方文檔：
https://docs.djangoproject.com/en/5.0/intro/tutorial05/
5. views.py
定義各個網頁要怎麼顯示，最重要的一個檔案！!。  

可以發現一個App內會有共用的view, models。所以通常我會把常用到一樣table的網頁定義成一個App，或者常用到同一個function的頁面放在同一個App內。

## 2. template和View。
### 構建前端的第一步
先在App資料夾底下新增一個templates資料夾，把index.html檔案放進去：
```
app_name
    ├──templates
        ├──index.html
    ├── admin.py
    ├── apps.py
    ├── models.py
    ├── tests.py
    └── views.py
```
index.html如下：
```
<!doctype html>
<html>
  <head>
    <title>This is the title of the webpage!</title>
  </head>
  <body>
    <p>This is an example paragraph. Anything in the <strong>body</strong> tag will appear on the page, just like this <strong>p</strong> tag and its contents.</p>
  </body>
</html>
```
配置好了template, 就要把這個畫面給選染到view上面啦～
view.py:
```python!
from django.shortcuts import render
#新加入的function
def testPage(request):
    return render(request, "index.html")
```
這樣就完成了view function跟template的對應，最後把function配置一個url。
urls.py:
```python!
from django.contrib import admin
from django.urls import path
from app_name import views #記得要import app的view
urlpatterns = [
    path("admin/", admin.site.urls),
    path("home", views.testPage) #給剛剛寫好的function設定一個url
]
```
這時候runserver後，瀏覽器網址輸入localhost:8000/home就可以看到剛剛寫的網頁啦！
![截圖 2024-02-09 中午12.38.14](https://hackmd.io/_uploads/B1aS27Qo6.png)
所有檔案要被瀏覽器存取，一定都要經過urls.py的分配，這種方法也加強了網頁的安全性。
如果urls內path放空字串，網頁就會被map到localhost:8000。
urls dispatch還有其他進階的用法，詳細看一下官方document：
https://docs.djangoproject.com/en/5.0/topics/http/urls/
### static files
Django內的static files(JS, CSS, IMG)也是統一由框架管理，不再透過file system的相對路徑去存取。
在settings.py裡面找到以下設定：
```python!
STATIC_URL = "static/"
```
這段定義每個app內的static資料夾，可以作為static file存取的路徑。我們在App建立一個static 資料夾，裡面放我們要的css檔案(或是任何靜態文件)：
```
app_name
    ├──templates
        ├──index.html
    ├── static
        ├──index.css
    ├── admin.py
    ├── apps.py
    ├── models.py
    ├── tests.py
    └── views.py
```
index.css:
```css
p {
    color: rebeccapurple;
}
```
可以用網址localhost:8000/static/index.css確定是否能存取檔案，若可以則代表static配置正確。
![截圖 截圖 2024-02-09 下午5.03.15](https://hackmd.io/_uploads/r16P9wXs6.png)
那html中如何引用呢？
首先要在html的最上面加上:
{% load static %}
只要放在static資料夾內的檔案都能用{% static "path_to_file"%}去存取。
```htmlembedded
<link href='{% static "index.css" %}' rel="stylesheet"/>
```
![截圖 2024-02-09 下午4.46.58](https://hackmd.io/_uploads/H1Iq8wXia.png)
可以看到我們的網頁已經有吃到CSS檔案啦～
img的src也是用同樣的方法去存取。
### include template
如果有一些常用的html code可以獨立成一個html檔案，在需要的時候include就好。像是網站的許多頁面都會用相同的navbar和footer，我們可以分別為footer和navbar創建html檔案。
```
app_name
    ├──templates
        ├──index.html
        ├──navbar.html
        ├──footer.html
    ├── static
        ├──index.css
    ├── admin.py
    ├── apps.py
    ├── models.py
    ├── tests.py
    └── views.py
```
然後在需要的位置include就好：
```htmlembedded!
{% include "navbar.html"%}
<p>This is an example paragraph. Anything in the <strong>body</strong> tag will appear on the page, just like this <strong>p</strong> tag and its contents.</p>
{% include "footer.html"%}

```
但有些template不只一個App需要，需要跨App也能存取該怎麼辦呢？這時候我們就要定義global template dirctory。
在settings.py裡面找到template的設定，把global template dir放到DIRS選項的位置：
```python!
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"], #定義global templates的位置
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]
```
BASE_DIR代表的是Project的位置。有趣的是Django setting內的所有路徑都可以直接用 **/** 符號連接，不需要用OS.path.join()。
APP_DIRS 如果改成False，django就不會再從App內找templates資料夾了，只有DIRS裡面的會被當成template檔案。

***備註***
我常常網頁會用到一大堆plugin，jquery、boostrap、jspanel....，每次都要複製一大堆link code很麻煩，我就會把這些引用的code做成一個template，每個網頁去include就好。
### Global static file
剛剛有提到跨App要存取template要設定Global template，同理跨App要存取static也要設定global static dirs。直接在settings.py加入以下設定:
```python!
STATICFILES_DIRS = [
    BASE_DIR / "static_file",
]
```
就可以把project file底下的static_file資料內的開放給所有App存取。
### send data to template
Django可以將要用的資料打包成一個dictionary傳給前端。只要在view function return render時把資料放在最後一個參數。  
view.py:
```python!
def testPage(request):
    i = [x for x in range(1,10)] #一個0~9的list
    data = {
        "data1":"資料1",
        "data2":"資料2",
        "list":i,
    }
    return render(request, "index.html", data)
```
前端用{{變數}}:  
```htmlembedded!
<!doctype html>
{% load static %}
<html>
  <head>
    <title>This is the title of the webpage!</title>
    <link href='{% static "index.css" %}' rel="stylesheet" />
  </head>
  <body>
    {{data2}}
    <p>This is an example paragraph. Anything in the <strong>body</strong> tag will appear on the page, just like this <strong>p</strong> tag and its contents.</p>
    {{list}}
  </body>
</html>
```
![截圖 2024-02-10 下午5.16.10](https://hackmd.io/_uploads/H1sbkaEop.png)
如果要取出list內特定index：
```htmlembedded!
{{list.3}}
```

### for tag
或是用for loop tag：
```htmlembedded!
<!doctype html>
{% load static %}
<html>
  <head>
    <title>This is the title of the webpage!</title>
    <link href='{% static "index.css" %}' rel="stylesheet" />
  </head>
  <body>
    {{data2}}
    <p>This is an example paragraph. Anything in the <strong>body</strong> tag will appear on the page, just like this <strong>p</strong> tag and its contents.</p>
    {% for item in list%}
    <p>{{item}}</p>
    {% endfor %}
  </body>
</html>

```
![截圖 2024-02-10 下午6.03.06](https://hackmd.io/_uploads/HJjecpVip.png)
### if tag
當然有for tag也有if else tag：
```htmlembedded!
{% if ... %}
{% else if ...%}
{% else %}
{% endif %}
```
像是宿舍公共空間借用系統的空間選擇欄位，要依據目前選擇的空間改變selected：
![截圖 2024-02-20 晚上9.18.11](https://hackmd.io/_uploads/B10TL7fna.png)

```htmlembedded!
<select class="wide" id="dropdown">
                
    <option value="1" selected="">雨樹L棟會議室</option>
                
    <option value="10">雨樹藝文空間</option>
                
</select>
```
就是使用for tag以及 if tag完成的：
```htmlembedded=
<select class="wide" id="dropdown">
    {% for space in region_space %}
    <option value={{space.id}} {% if space.id == request.GET.space_id %} selected {% endif %}>{{space.space_name}}</option>
    {% endfor %}
</select>
```
要特別注意的是，對於Django前端的變數還是有資料型態的差別的，所以如果==兩端的資料型態不同，是需要資料型態轉換的！

基本上都和python語法差不多，只是最後都要加上一個end tag。
還有很多酷酷tag和filter，包括變數資料型態的轉換(stringformat)或是變數的運算(add)，請參考官方文檔：
https://docs.djangoproject.com/en/5.0/ref/templates/builtins
備註：
MTV這種形式的好處就是前後端做到完全分離，前端code只會有變數跟一些必要的for loop。
### 什麼？前端可以存取POST?
不只是POST, GET、session、cookie，這些跟request有關的都可以直接在template用變數存取。
不過要確定setting.py內的templates context_processors有沒有這個設定：
```python!
"django.template.context_processors.request"
```
這樣就可以在前端用request.POST.name存取POST的內容！！(session, get, cookie同理)

```htmlembedded!
<p id="hidden_id" >{{request.GET.space_id}}</p>
```
### url tag
**補充**：
還有很重要的tag是url tag, 用法:
{% url "site_name" %}
只要給頁面一個別名，就可以用這個tag自動生成去往這個頁面的url。
這東西好用在哪裡呢？以宿服借用系統的navbar為例，點擊navbar上的標題就可以回到主頁。
但是如果用相對路徑，有一些頁面可能../就可以回到主頁，有一些要../../兩次才可以或是更多，如果用絕對路徑domain更改時又要改code，所以我們可以直接給主頁一個url name，在urls.py裡面:
```python!
path("", views.home, name="home"),
```
這樣我們就可以用{% url "home" %}回到主頁：
```htmlembedded=
<nav class="navbar navbar-expand-lg navbar-dark bg-516464">
    <div class="container px-5">
        <a class="navbar-brand" href="{% url 'home'%}">中山大學宿舍公共空間借用系統</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav ms-auto mb-2 mb-lg-0">
                <li class="nav-item"><a class="nav-link" href="https://housing-osa.nsysu.edu.tw/">宿服組網站</a></li>
                <li class="nav-item"><a class="nav-link" href="https://housing-osa.nsysu.edu.tw/p/412-1092-18050.php?Lang=zh-tw">借用須知</a></li>
            </ul>
        </div>
    </div>
</nav>
```


## 3.資料庫
### 資料庫設定
Django 原先預設是使用sqllite，不過我自己習慣使用MySQL。
在 settings.py內找到DATABASES的設定，並改成：
```python!
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "databaseName",
        "USER": "databaseUser",
        "PASSWORD": "databasePassword",
        "HOST": "localhost",
        "PORT": "portNumber",
    }
}
```
若沒有安裝pymysql package:
```bash!
pip install pymysql
```
最後記得要在project/__init__.py 或是 settings.py的最上面補上以下程式，
這段程式的目的是讓python可以直接透過pymysql package去連結資料庫：
```python!
import pymysql
pymysql.install_as_MySQLdb()
```
Django不管你連接什麼資料庫，都會轉為ORM，簡單來說就是一個table就會是一個Python class，table內的column就是class的attribute，這些class定義在app的models.py裡面。  
**ps:** ***model就沒分local model或是global model了，如果要用到別的app的model, 直接from app import model就好了。***

```python=
class Space(models.Model):
    id = models.AutoField(primary_key=True)  # Field name made lowercase.
    space_name = models.CharField(max_length=255)  # Field name made lowercase.
    region = models.CharField(max_length=255)
    link = models.CharField(max_length=255, blank=True, null=True)
    eng_name = models.CharField(max_length=255)

    class Meta:
        managed = True #代表需要Django幫你在資料庫建立這個table
        db_table = 'Space' #資料庫內table的名字，預設會是django_space
        
class Register(models.Model):
    signature = models.CharField(max_length=255, primary_key=True, null=False, blank=False)
    start_time = models.IntegerField()  # Field name made lowercase.
    space = models.ForeignKey(Space, on_delete=models.CASCADE)  # Field name made lowercase. The composite primary key (Space_id, Start_time, Date) found, that is not supported. The first column is selected.
    date = models.CharField(max_length=20)  # Field name made lowercase.
    usable = models.IntegerField()
    user_id = models.CharField(max_length=255, blank=True, null=True)
    user_name = models.CharField(max_length=255, blank=True, null=True)
    user_phone = models.CharField(max_length=255, blank=True, null=True)
    user_dormnumber = models.IntegerField(blank=True, null=True)
    change_pwd = models.CharField(max_length=255, blank=True, null=True)

    class Meta:
        managed = True
        db_table = 'Register'
        unique_together = ('space', 'start_time', 'date') #這三個欄位合在一起必須唯一



class BlackList(models.Model):
    stu_id = models.CharField(primary_key=True, max_length=20)
    expire_time = models.DateField(blank=True, null=True)
    banned_reason = models.CharField(max_length=20, blank=True, null=True)

    class Meta:
        managed = True
        db_table = 'black_list'
```
定義或是修改class後輸入指令：
```bash!
python manage.py makemigrations
python manage.py migrate
```
這時就會看到我們的資料庫多了許多table:
![Screenshot from 2024-02-21 20-52-33](https://hackmd.io/_uploads/Bk9XfuXha.png)
除了Space, Register, black_list以外，其他都可以不用管，那些是django自己產生的一些紀錄。

### 常見的資料庫欄位格式
資料欄位Options常用的有:
<table>
    <tr>
        <th>選項</th>
        <th>意義</th>
    </tr>
    <tr>
        <td>null</td>
        <td>是否可以為null</td>
    </tr>
    <tr>
        <td>blank</td>
        <td>是否可以為空白字串</td>
    </tr>
    <tr>
        <td>primary_key</td>
        <td>是否為pk</td>
    </tr>
    <tr>
        <td>default</td>
        <td>欄位預設值</td>
    </tr>
    <tr>
        <td>choices</td>
        <td>限定欄位值只能填哪些, 很好用</td>
    </tr>
    <tr>
        <td>unique</td>
        <td>欄位值是否唯一</td>
    </tr>
</table>


常用的資料型態則有:
```python=
models.AutoField() #autoIncrement Integer
models.CharField() #varchar
models.IntegerField() #Integer
models.DateField() #Date, 在python內可以當datetime.date物件處理
#偷偷抱怨一下，php要處理date真的超級麻煩，所以之前我都用varchar去存日期
models.DateTimeField() #Datetime, 時間加上日期，在python內可以當datetime.datetime物件處理
models.FileField() #Mysql沒有這種欄位，直接可以handle檔案的存取!超讚!!
```
**備註:**
Django不支援multi-key primary key, 所以只能放一個autoincrement的pk, 然後再用unique_together
### 特殊的資料欄位
這些欄位是用來定義relational DB表格之間的關係，像是1對1資料、1對多資料...  
Django都會自動轉換成對應的表格，不需要自己從ER diagram轉成table。並且當資料違反原則時會自動報錯。
```python=
models.OneToOneField() #1對1關係
models.ForeignKey() #1對多關係
models.ManyToManyField() #多對多關係，資料庫會自動多建立一個表格
```
這些特殊欄位除了上面提到Option，還多了幾個:
<table>
    <tr>
        <th>選項</th>
        <th>意義</th>
    </tr>
    <tr>
        <td>to</td>
        <td>有關係的table</td>
    </tr>
    <tr>
        <td>on_delete</td>
        <td>連結的資料消失時採去的動作</td>
    </tr>
    <tr>
        <td> parent_link</td>
        <td>繼承用的，不重要我也不會用QQ</td>
    </tr>
</table>

詳細更多的Field還有Options請見官方文檔:
https://docs.djangoproject.com/en/5.0/ref/models/fields/#django.db.models.FileField

### Query
Django支援原本的SQL語法或是ORM對資料庫進行Query，個人建議簡單的操作像是SELECT, DELETE, INSERT用ORM，至於比較複雜的JOIN, GROUP_BY, 甚至是AGGREGATION就還是乖乖寫SQL吧!!

#### SELECT

```python!
#SELECT * from space where id = 1 AND space_name = "武嶺會議室";
space = Space.object.filter(id=1, space_name="武嶺會議室")
#SELECT id, space_name from space where id = 1;
space = Space.object.filter(id=1).values("id", "space_name")
```
注意！！以上兩種function回傳不太一樣，前者是QuerySet(類似list的物件)，裡面包著Space object;後者是QuerySet裡面包著dict。
例如:
```python!
>>> Blog.objects.filter(name__startswith='Beatles').values('id', 'name', 'tagline')
[{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]

```
注意!!雖然QuerySet物件類似list, 操作也差不多，但在某些function只能接受list還是得要轉換。  
**補充(不怎麼重要，可以跳過):**  
如果使用values_list，則所有值會攤開成一個tuple:
```python!
>>> Blog.objects.filter(name__startswith='Beatles').values_list('id', 'name')
[(1, 'Beatles Blog')]
```
如果再加上flat=TRUE就會攤開變成:
```python!
>>> Blog.objects.filter(name__startswith='Beatles').values_list('id', 'name', flat=True)
[1, 'Beatles Blog']

```
**補充結束**

#### INSERT INTO/UPDATE
```python!
new_space = Space(id=2, space_name="武嶺交誼廳")
new_space.save()

#INSERT INTO Space (id, space_name) values (2, space_name);
```
也可以用dic創建一個物件，在該物件有一大堆Fields時比較省力:
```python!
record = {
    'start_time' : request.POST.get('Start_time'),
    'user_id' : request.POST.get('user_id'),
    'user_dormnumber' : request.POST.get('user_dormnumber'),
    'user_phone' : request.POST.get('user_phone'),
    'change_pwd' : request.POST.get('change_pwd'),
    'date' : request.POST.get('date'),
    'user_name' : request.POST.get('name'),
    'usable': 1
}
new_record = Register(**record)
new_record.save()
```
如果要update，只要把物件Query出來修改後，用save()就可以了
#### DELET FROM
```python!
space = Space.object.filter(id=1, space_name="武嶺會議室")
space.delete()

#DELET FROM Space where id=1;
```

### 用SQL QUERY
```python!
from django.db import connection
with connection.cursor() as cursor:
    cursor.execute("YOUR SQL STATEMENT")
    result = cursor.fetchall()
```

**補充**:  
以防有人不知道，python的with是一個專門把檔案、連線物件關住的東西，在離開with時會自動執行cursor.close()，避免連線資源未關閉。  

更多進接或是常用操作可以看:
https://www.twblogs.net/a/5b8793c82b71775d1cd7d91c

## 4. 其他網頁response
前面我們view function回傳都是render()，也就是一個網頁頁面。但有時候我們不希望回傳一整個頁面，像是是Ajax request就不需要回傳一個整個頁面，只要一個字串或是一些資料而已，這時候我們就會用HttpResponse(回傳一個字串)或是JsonResponse。
### HttpResponse
例如登入管理者模式的Ajax:
```javascript!
 $.ajax({
    url : "{% url 'private'%}",
    type: "post",
    data: {"pwd": pwd, "mode": "login"},
    success: function(response) {
        alert(response);
        location.reload();
    },
    error: function(jqXHR, textStatus, errorThrown){
        alert("AJAX error" + errorThrown);
        console.log('Error: ' + errorThrown);
    }
});
```
這時private這個view function就會是這樣:
```python!
def admin_mode(request):
    if request.method == "POST":
        if request.POST.get("mode") == "login":
            if request.POST.get("pwd") == "76211194":
                request.session['identity'] = "private"
                return HttpResponse("登入成功，切換為管理者模式")
            else:
                return HttpResponse("密碼錯誤！")
        else:
            request.session['identity'] = "normal"
            return HttpResponse("登出成功，切換為一般模式")
    else:
        raise Http404("Page not exit")
```

### JsonResponse
通常用來回傳資料庫的內容給端。例如我們的預約介面是用Ajax取得預約資料的，view function就會長這樣:
```python=
def get_regist(request):
    if request.method == "POST":
        space = request.POST.get('id')
        all_regist = Register.objects.filter(space=space).values("start_time", "space_id", "date", "user_id", "user_name", "user_phone", "user_dormnumber")
        all_regist = list(all_regist)
        if request.session['identity'] == "normal":
            for record in all_regist:
                record['user_id'] = record['user_id'][:-5] + "XXXXX"
                record['user_name'] = record['user_name'][0] + "X" + record['user_name'][2:]
                record['user_phone'] = ""
        return JsonResponse(all_regist, safe=False)
    else:
        raise Http404("Page not exit")
```

## 5. 那些有點重要但我懶得打的內容
以下這些有可能會用到(和網站安全有關)但是較為繁瑣我懶得打，要麻煩各位自己找一下資料:
1. CSRF token --- Django避免跨站攻擊的機制
2. Django form --- 也是Django避免sql injection的一個東東

## 6. Utility codes
這裡放一些常常用的function和code:  
### cursor dicfetchall
cursor.fetchall()回傳的會是一個tuple(tuple())，這樣會不利於取得資料欄位(只能用index很不方便)，以下是讓cursor回傳tuple(dict())的code。  
```python=
def dicfetchall(cursor):
    colums = [col[0] for col in cursor.description]
    return [
        dict(zip(colums, row))
        for row in cursor.fetchall()
    ]
```