django+mysql的CURD入門體驗

在django官網的入門教程中,採用的是循序漸進的方式,基於一個問答投票的案例進行的講解。另外也使用了Admin自動生成頁面,這確實挺強大的。但看完後感覺有點亂亂的,有太多分支介紹,感覺不是最佳實踐,只是爲了介紹django的各種功能。

這裏我根據平時項目中正常的流程順序,重新梳理一下思路流程,形成一條主線以便記憶。

準備工作

  1. 安裝django(install)
python -m pip install Django
  1. 創建項目(startproject)
django-admin startproject mysite
  1. 啓動項目(runserver)
cd mysite
python manage.py runserver

看到如下輸出,打開鏈接顯示歡迎頁

...
December 30, 2019 - 02:11:37
Django version 3.0.1, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

首次啓動後,在項目根目錄下會產生db.sqlite3文件
4. 創建應用(startapp)

python manage.py startapp polls
  1. 引入應用

mysite/settings.py添加polls應用配置

INSTALLED_APPS = [
    'polls.apps.PollsConfig',
]

數據庫相關

在官方示例中,默認使用的sqlite3,庫表都是自動創建的,很強大但隱藏了很多細節。

這裏我改成常用的Mysql

  1. 首先手動創建數據庫
create schema my_site character set utf8;

修改settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'my_site',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': '192.168.223.43',
        'PORT': '3306'
    }
}

重啓項目如果沒報錯,說明數據庫鏈接成功

  1. 生成默認表

根據INSTALLED_APPS中的依賴,自動生成相關的表到數據庫中

python3 manage.py migrate

執行完後可以到數據庫中查看多了auth、django打頭的表。

這也是django的一大特性,即在提供功能插件的時候是全套的,包括數據庫的自動生成,不用自己額外的去獲取SQL,並且數據庫的生成方式很優雅。

mysql> show tables;
+----------------------------+
| Tables_in_my_site          |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+
  1. 創建業務表

參照官方的投票示例建表,這裏並沒有先建Model然後用migrate生成表,而是使用我們平時開發的流程先手動建表,然後再到代碼中建Model

-- 問題表
create table polls_question (
    id int auto_increment primary key ,
    question_text varchar(200) not null,
    pub_date timestamp NOT NULL default current_timestamp
);

-- 投標表
create table polls_choice (
    id int auto_increment primary key,
    question_id int not null,
    choice_text varchar(200) not null,
    votes int not null,
    foreign key (question_id) references polls_question(id)
);
  1. 定義模型(Model)

polls/models.py

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200, blank=False, null=False)
    pub_date = models.DateTimeField()


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

繼承models.Model,定義了兩個Model,裏面的字段同數據庫中的字段,另外指定了字段類型及相應的屬性,也就是同數據庫中的定義保持一致。

爲什麼不先定義Model再自動生成數據表呢,僅僅是爲了理解來龍去脈,理解後誰先誰後可以隨意。

按照裝官方文檔,下一步是createsuperuser,通過django強大的admin功能,自動生成頁面來對剛纔的兩個Model進行CURD操作。有需要的可以參照文檔體驗一下。

  1. 定義視圖-新增(View)

接口的定義有兩種方式

  • 函數定義
  • 通用類定義

視圖使用RESTful風格,但並不準備使用其他插件框架

通過繼承View通用類來定義view,因爲它可以通過定義get、post、put、delete的方法來匹配Http Method,返回類型都用JsonResponse,爲了方便寫了個BaseResult

common.py

class BaseResult:
    success = False
    error = None

    def __init__(self, success, error):
        self.success = success
        self.error = error

    def __init__(self, error):
        self.error = error

    def __init__(self):
        pass

    def toJSON(self):
        return {'success': self.success, 'error': self.error}

先看新增,post作爲數據新增使用,那麼就要涉及表單數據的接收和校驗

views.py

from django.views import View
from django.core.paginator import Paginator
from .models import Question
from django.core.exceptions import ObjectDoesNotExist
from django.http import QueryDict, JsonResponse
from .common import BaseResult
from .forms import QuestionForm

class QuestionView(View):
    def post(self, request):
        result = BaseResult()
        form = QuestionForm(request.POST)

        if form.is_valid():
            form.save()
            result.success = True
        else:
            result.error = form.errors
        return JsonResponse(result.toJSON())

django提供來ModelForm來簡化表單的處理

forms.py

from django import forms
from django.forms import ModelForm
from .models import Question


class QuestionForm(ModelForm):
    question_text = forms.CharField(error_messages={'required': '問題不能爲空', 'max_length': '長度不能超過10'})

    class Meta:
        model = Question
        fields = "__all__"
  • model指定基於哪個Model,因爲常規情況下表單的數據是對應Model的,所以這裏不需要重複定義字段,直接借用
  • fields用於配置需要用到的字段有哪些,如果字段比較多可以一個__all__搞定,當然也可以一個個的寫,或者反着來過濾某些字段都可以,具體看官方文檔有介紹
  • form的第二個作用是要做參數校驗,默認使用Model中的字段屬性要求要做校驗,例如長度和非空,但提示語是默認的,如果要自定義校驗的提示語,就是上面error_messages的用法

再回過頭看post方法,直接將request.POST丟給QuestionForm就可以拿到form提交的數據了。

緊接着做校驗,成功就save,出錯就返回errors。這裏的一個強大之處是,form直接可以保存Model

接口定義好了,要去配置一下url

mysite/urls.py

from polls.views import QuestionView
urlpatterns = [
    #...
    path('polls/question/', QuestionView.as_view()),
]

爲了方便調試去除settings.py中MIDDLEWARE的 django.middleware.csrf.CsrfViewMiddleware,否則會提示CSRF cookie not set

測試一下post接口

$ curl http://127.0.0.1:8000/polls/question/ -X POST -d 'question_text=喜歡的語言&pub_date=2019-12-30 17:00:00'

{"success": true, "error": null} 
  1. get分頁查詢
class QuestionView(View):
    def get(self, request):
        kwargs = {}
        if request.GET.get('question_text') is not None:
            kwargs['question_text__startswith'] = request.GET.get('question_text')
        if request.GET.get('pub_year') is not None:
            kwargs['pub_date__year'] = request.GET.get('pub_year')

        question_page = Paginator(Question.objects.filter(**kwargs),
                                  request.GET.get('size')).page(request.GET.get('page'))

        result_list = map(lambda q: {
            'question': q.question_text,
            'pub_date': q.pub_date
        }, question_page)

        return JsonResponse({
            "count": question_page.paginator.count,
            "data": list(result_list)
        }, safe=False, json_dumps_params={'ensure_ascii':False})
  • 由於查詢條件是可選的,所以判斷是否None,然後放入字典中,字典的key爲Model query的表達式,字段與匹配方式之間用兩個下劃線連接。
  • 使用Paginator進行分頁查詢
  • 使用lambda對分頁結果進行爲JSON格式
$ curl -s "http://127.0.0.1:8000/polls/question/?page=1&size=5"   

{"count": 1, "data": [{"question": "喜歡的語言", "pub_date": "2019-12-30T09:00:00Z"}]}  
  1. put更新
class QuestionView(View):
    def put(self, request):
        result = BaseResult()

        put = QueryDict(request.body)
        try:
            q = Question.objects.get(pk=put.get('id'))
        except ObjectDoesNotExist:
            result.error = "ID不存在"
            return JsonResponse(result.toJSON())

        form = QuestionForm(put, instance=q)
        if form.is_valid():
            form.save()
            result.success = True
        else:
            result.error = form.errors

        return JsonResponse(result.toJSON())
  • 由於django的request對象中沒有PUT屬性,所以不能像GET、POST直接去除數據,得通過body轉字典
  • 使用主鍵獲取原數據,然後通過QuestionForm合併新舊數據
$ curl http://127.0.0.1:8000/polls/question/ -X PUT -d 'question_text=喜歡的語言&pub_date=2019-12-30 17:50:00&id=1'

{"success": true, "error": null}
  1. 刪除數據
class QuestionView(View):
    def delete(self, request):
        result = BaseResult()
        req = QueryDict(request.body)
        Question.objects.get(pk=req.get('id')).delete()
        result.success = True
        return JsonResponse(result.toJSON())

同PUT一樣,delete請求的參數也需要通過body轉dict再get獲取

$ curl http://127.0.0.1:8000/polls/question/ -X DELETE -d 'id=3'

{"success": true, "error": null}  

結束

自此一個非常簡單的CURD就結束了,當然這不能體現django的流行和強大,僅僅作爲一個入門路線,基於此再去擴展嘗試django其他特性。

發佈了46 篇原創文章 · 獲贊 30 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章