1. 程式人生 > >Django中REST framework與MongoDB的搭配使用

Django中REST framework與MongoDB的搭配使用

Django框架下,因其功能強大可為廣大使用者提供各種資料庫的配套使用方法。由於要學習學長的專案,所以我需要學習MongoDB資料庫搭配REST framework的使用,在網上查找了一些資料後發現大部分資料都是關於其自帶的sqlite3的,所以在這裡分享一下我在網上查詢到的一些MongoDB搭配REST framework的使用方法。

新增依賴

python的第三方庫非常的強大,可以說是什麼都有,只有你想不到的。要在REST framework中使用MongoDB就必須新增以下兩個依賴庫。

pip install djangorestframework
pip Django-rest-framework-mongoengine 3.3.1

想要具體瞭解rest_framework_mongoengine的api詳見http://umutbozkurt.github.io/django-rest-framework-mongoengine/index.html 

建立專案

然後當然就是開始建立你的專案了。建立專案具體過程我就不囉嗦了。

#建立專案vis23
django-admin.py startproject vis23
#建立APP datas
python manage.py startapp datas

註冊APP

在datas的settings中找到INSTALLED_APPS

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
     #新增
    'rest_framework',
    'rest_framework_mongoengine',
    'datas',
]

向MongoDB中匯入資料

匯入資料的具體過程我也不詳細說了,在我的上一篇部落格中有寫到。這裡我匯入的是我們專案中17年的資料。

建立一個名為data2mongo的.py檔案,具體程式碼如下:

from pymongo import MongoClient
from vis.settings import DBCONFIG
import csv

class Data1:
    filename = '網咖資訊.csv'
    path = 'C:\\Users\\Administrator\\Desktop\\2017\\'
    c_raw1 = 'raw_data1'
    colname = ['siteid', 'title', 'lng', 'lat']
    res = []
    max_len = 100000

    def __init__(self):
        self.files = []
        self.files.append(self.filename)
        self.client = MongoClient(DBCONFIG['HOST'], DBCONFIG['PORT'])
        self.db = self.client.__getattr__(DBCONFIG['NAME'])
        self.dst_raw1 = self.db[self.c_raw1]

    def __del__(self):
        self.client.close()

    @staticmethod
    def convert_fileaffix(fileaffix):
        if len(fileaffix) == 0:
            return ''
        while fileaffix[0] == '.':
            fileaffix = fileaffix[1:]
        if '.' in fileaffix:
            return None
        else:
            return fileaffix

    def data1(self):
        self.dst_raw1.remove({})
        self.res.clear()
        print('正在處理檔案:%s' % (self.path + self.filename))
        with open(self.path + self.filename) as infile:
                reader = csv.reader(infile)
                next(reader)
                for row in reader:
                    fileaffix = self.convert_fileaffix(str(row[3]))
                    if fileaffix is None:
                        continue
                    self.res.append({
                        'siteid': str(row[0]),
                        'title': str(row[1]),
                        'lng': str(row[2]),
                        'lat': str(row[3]),
                    })
                    if self.res.__len__() == self.max_len:
                        self.dst_raw1.insert_many(self.res)
                        self.res.clear()
        if self.res.__len__() > 0:
            self.dst_raw1.insert_many(self.res)
            self.res.clear()

class Data2:
    filename = 'hydata_swjl_{}.csv'
    path = 'C:\\Users\\Administrator\\Desktop\\2017\\'
    count = range(0, 2)
    c_raw2 = 'raw_data2'
    colname = ['personid', 'siteid', 'xb', 'customername', 'onlinetime',
               'offlinetime', 'areaid', 'birthday']
    res = []
    max_len = 100000

    def __init__(self):
        self.files = []
        for i in self.count:
            self.files.append(self.filename.format(i))
        self.client = MongoClient(DBCONFIG['HOST'], DBCONFIG['PORT'])
        self.db = self.client.__getattr__(DBCONFIG['NAME'])
        self.dst_raw2 = self.db[self.c_raw2]


    def __del__(self):
        self.client.close()

    @staticmethod
    def convert_fileaffix(fileaffix):
        if len(fileaffix) == 0:
            return ''
        while fileaffix[0] == '.':
            fileaffix = fileaffix[1:]
        if '.' in fileaffix:
            return None
        else:
            return fileaffix

    def data2(self):
        self.dst_raw2.remove({})
        self.res.clear()
        for i in self.count:
            print('正在處理檔案:%s' % (self.path + self.filename.format(i)))
            with open(self.path + self.filename.format(i)) as infile:
                reader = csv.reader(infile)
                next(reader)
                for row in reader:
                    fileaffix = self.convert_fileaffix(str(row[3]))
                    if fileaffix is None:
                        continue
                    self.res.append({
                        'personid': str(row[0]),
                        'siteid': str(row[1]),
                        'xb': str(row[2]),
                        'customername': str(row[3]),
                        'onlinetime': str(row[4]),
                        'offlinetime': str(row[5]),
                        'areaid': str(row[6]),
                        'birthday': str(row[7]),
                    })
                    if self.res.__len__() == self.max_len:
                        self.dst_raw2.insert_many(self.res)
                        self.res.clear()
        if self.res.__len__() > 0:
            self.dst_raw2.insert_many(self.res)
            self.res.clear()

這裡解釋一下程式碼:其中, from vis.settings import DBCONFIG 中的 DBCONFIG 是我在datas的setting中定義的一個方便連線MongoDB的函式;因為我要匯入的一共是三張.csv 的表,分別是 網咖資訊.csv,hydata_swjl_0.csv,hydata_swjl_1.csv,其中後兩表表內格式一致,所以建立了兩個類用於分別匯入。

這裡需要特別注意的是:為了去掉表頭,使用了

reader = csv.reader(infile)    

next(reader)

這兩句話,它的作用是在匯入表時去掉表頭中的文字,但在執行時總是報錯,錯誤資訊為:

next(reader)
UnicodeDecodeError: 'gbk' codec can't decode byte 0xb7 in position 112: illegal multibyte sequence

 這是由於讀取檔案的字元編碼問題,在with open()中這樣改一下就好了

with open(self.path + self.filename.format(i), 'r', encoding='UTF-8') as infile:

當然data2Mongo只是匯入的方法,要執行還得需要一個函式來呼叫它,handles.py,程式碼如下:

from data2mongo import Data1
from data2mongo import Data2
import time
if __name__ == '__main__':
    start = time.clock()
    tomongo1 = Data1()
    tomongo1.data1()
    tomongo2 = Data2()
    tomongo2.data2()

到這之後,終於該進入正題了。在此之前我們需要在datas的setting中新增兩句話,並且將DATABASES中的類容設定為空

DATABASES = {
    'default': {
        'ENGINE': None,  #設定為空
    }
}
from mongoengine import connect  #連線MongoDB的庫
connect('vis')  #連線vis資料庫

編寫Datas

1.建立模型,使用mongoengine.Document。一般在編寫api的時候我們在models中都是這樣寫的

class Student(models.Model):

但由於我們要使用mongoDB所以我們需要這樣改一下class data1(Document):

class data1(Document):

具體程式碼如下:

from __future__ import unicode_literals
from mongoengine import *
# Create your models here.
connect('vis', host='127.0.0.1', port=27017) #連線資料庫
class data1(Document):
    siteid = StringField(max_length=45)
    title = StringField(max_length=45)
    lng = StringField(max_length=45)
    lat = StringField(max_length=45)

    meta = {'collection': 'raw_data1'} #資料庫中的集合

    def __unicode__(self):
        return self.name
class data2(Document):
    personid = StringField(primary_key=True)
    siteid = StringField(max_length=45)
    xb = StringField(max_length=45)
    customername = StringField(max_length=45)
    onlinetime = StringField(max_length=45)
    offlinetime = StringField(max_length=45)
    areaid = StringField(max_length=45)
    birthday = StringField(max_length=45)

    meta = {'collection': 'raw_data2'} #資料庫中的集合

    def __unicode__(self):
        return self.name 

2.編寫序列,對serializer需要稍作變化,使用rest_framework_mongoengine.serializers

from rest_framework_mongoengine import serializers
from . import models
class data1Serializer(serializers.DocumentSerializer):
    class Meta:
        model = models.data1
        fields = '__all__'
class data2Serializer(serializers.DocumentSerializer):
    class Meta:
        model = models.data2
        fields = '__all__'

3.在view中同理

from . import models
from . import serializers
from rest_framework_mongoengine import generics
class data1View(generics.ListCreateAPIView):
    queryset = models.data1.objects.all().order_by('siteid')
    serializer_class = serializers.data1Serializer
class data2View(generics.ListCreateAPIView):
    queryset = models.data2.objects.all().order_by('personid')
    serializer_class = serializers.data2Serializer

4.設定路由

(1)在datas中的urls中定義路由

from django.urls import path
from .views import data1View
from .views import data2View
urlpatterns = [
    path('data1/', data1View.as_view(), name='data1'),
    path('data2/', data2View.as_view(), name='data2')
]

(2)在總路由中新增路由地址

from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('datas.urls')),
]

做完這些之後,我們就可以運行了

python manage.py runserver

在瀏覽器輸入http://127.0.0.1:8000/api/data1或data2 我們就能看到:

這樣一個基本的api就實現了,當然其中我們可以看到顯示的字元有一些問題,這是由於當初向資料庫中匯入資料是定義的型別的問題,後面我們會加以改正。