Django-REST-framework使用技巧(一)

  • 1.Quickstart
    • 1.1 項目搭建
    • 1.2 序列化
    • 1.3 視圖
    • 1.4 URLs
    • 1.5 設置
  • 2.Serialization
    • 2.1 創(chuàng)建一個模型
    • 2.2 創(chuàng)建一個序列化類
    • 2.3 使用Serializers
    • 2.4 使用ModelSerializers
    • 2.5 使用Serializer編寫常規(guī)的Django視圖
  • 3.測試我們在Web API上的第一次訪問

1放妈、Quickstart

環(huán)境

Python 3.7.2

MacOS High Sierra

django-2.2.1

djangorestframework-3.9.4

1.1 項目

我們將創(chuàng)建一個簡單的API來允許管理員用戶查看和編輯和系統(tǒng)中的用戶和組扼劈。

項目創(chuàng)建

創(chuàng)建目錄

mkdir tutorial
cd tutorial

創(chuàng)建virtualenv,環(huán)境隔離我們本地的依賴包關系

virtualenv env # 會得到下面結果回應
    Using base prefix '/usr/local/Cellar/python/3.7.2_2/Frameworks/Python.framework/Versions/3.7'
New python executable in /Users/administrator/Desktop/Django-REST-Framework/tutorial/env/bin/python3.7
Also creating executable in /Users/administrator/Desktop/Django-REST-Framework/tutorial/env/bin/python
Installing setuptools, pip, wheel...
done.
source env/bin/activate #激活虛擬環(huán)境蹋辅,可以看到以下結果顯示
(env) AdministratordeiMac:tutorial administrator$ 

在虛擬環(huán)境安裝Django 和 Django REST Framework

pip install --upgrade pip #先升級pip
pip install django # 安裝django
pip install djangorestframework # 安裝djangorestframework
pip install pygments #代碼高亮

創(chuàng)建項目檬姥,并建立一個app

django-admin startproject tutorial . #建立Django項目噪猾。后面"."表示在當前文件夾建立項目
python manage.py startapp quickstart 或則django-admin.py startapp quickstart #新建一個APP

項目布局應該如下:

startarchitecture.png
quickstart.png

第一次同步數據庫

python manage.py migrate #第一次同步數據庫阻塑,如下:
migrate.png

創(chuàng)建超級用戶客们,用戶名admin歉眷,密碼123456(超級用戶隨便起牺六,但是要記住名字和密碼,后面要用到)

python manage.py createsuperuser
superuser.png

1.2 序列化

首先定義一些序列化器汗捡,創(chuàng)建一個名為tutorial/quickstart/serializers.py的文件淑际,我們將用它來表示數據。

#文件路徑tutorial/quickstart/serializers.py

from django.contrib.auth.models import User, Group
from rest_framework import serializers


class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'email', 'group')
        

class GroupSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Group
        field = ('url', 'name')

注意這里扇住,我們使用了超鏈接關系(HyperlinkedModelSerializer)春缕。你還可以使用主鍵和其他關系,但是這種超媒體引
擎驅動是不是很棒的RESTful設計呢艘蹋。

1.3 視圖

在tutorial/quickstart/views.py中锄贼,輸入:

#文件路徑tutorial/quickstart/views.py

from rest_framework import viewsets
from .serializers import UserSerializer, GroupSerializer
from django.contrib.auth.models import User, Group


class UserViewSet(viewsets.ModelViewSet):

    # 允許用戶查看或者編輯API端點

    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer


class GroupViewSet(viewsets.ModelViewSet):
    
    # 允許組查看或者編輯API端點

    queryset = Group.objects.all()
    serializer_class = GroupSerializer

我們將所有通用行為分組到ViewSets類中,而不是編寫多個視圖女阀。
如果需要宅荤,我們可以輕松第將這些視圖分解為單個視圖(這個后面會細說的),但是使用視圖集可以使視圖邏輯組織的非常好,
并且非常簡潔浸策。

1.4 URLs

配置路由

# tutorial/urls.py

from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from rest_framework import routers
from quickstart import views


router = routers.DefaultRouter()

router.register('users', views.UserViewSet)
router.register('group', views.GroupViewSet)

# 使用自動URL路由連接API
# 另外冯键,我們還包括可以瀏覽API的登錄URL
urlpatterns = [
    path('admin/', admin.site.urls),
    url('^', include(router.urls)),
    url('^api-auth/', include('rest_framework', namespace='rest_framework')),
]

因為我們使用視圖集而不是視圖,所以我們可以通過簡單地向路由器類注冊視圖集的榛,來自動為我們的API生成URL conf琼了。
同樣,如果我們需要更多地控制API URL夫晌,我們可以簡單地使用常規(guī)的基于類的視圖雕薪,并明確地編寫URL conf。
最后晓淀,我們將包括默認的和注銷視圖所袁,以用于可以瀏覽的API,這是可選的凶掰。但是如果你的API需要身份驗證燥爷,并且想使用可瀏覽的API蜈亩,則會很有用。

1.5 設置(Setting)

將rest_framework 添加到INSTALLED_APPS.

設置路徑:tutorial/setting.py中:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    
]

第一階段已經完成前翎,下面測一下我們的API:開啟服務

python manage.py runserver

我們可以使用從命令行curl等工具訪問我們api:

(env) AdministratordeiMac:tutorial administrator$ curl -H 'Accept: application/son; indent=4' -u admin:123456 http://127.0.0.1:8000/users/

[
    {
        "url": "http://127.0.0.1:8000/users/1/",
        "username": "admin",
        "email": "1232@qq.com",
        "groups": []
    }
]

或者命令行工具 httpie ...

(env) AdministratordeiMac:tutorial administrator$ http -a admin:123456http://127.0.0.1:8000/users/

HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

[
    {
        "url": "http://127.0.0.1:8000/users/1/",
        "username": "admin",
        "email": "1232@qq.com",
        "groups": []
    }
]

或者直接通過瀏覽器訪問 URL http://127.0.0.1:8000/users/ 稚配,如下如所示:

apiresult.png

2、Serialization(序列化)

添加上文創(chuàng)建APP quickstart

在tutorial/setting.py中:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'quickstart',
]

2.1 創(chuàng)建一個模型

在quickstart/models.py文件中添加模型

from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles


# 提取出pyment 支持的所有語言的語法分析程序
LEXERS = [item for item in get_all_lexers() if item[1]]

# 提取除了'pyments' 支持的所有語言列表
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])

# 提取出'pyment' 支持的所有格式化風格列表
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())


class Snippet(models.Model):

    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)  # 是否顯示行號
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=120)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=120)

    class Meta:
        ordering = ('-created',)

然后執(zhí)行數據遷移和同步

python manage.py makemigrations

Migrations for 'quickstart':
quickstart/migrations/0001_initial.py
- Create model Snippet


python manage.py migrate

Operations to perform:
Apply all migrations: admin, auth, contenttypes, quickstart, sessions
Running migrations:
Applying quickstart.0001_initial... OK

2.2 創(chuàng)建一個序列化類

我們開始使用Web API 的第一件事就是提供一種港华,將代碼片段的實例序列化或者反序列化為表示形式(json等)的方法道川。我們可以通過聲明與Django forms非常相似的序列化器(serializers)實現。在上面我們已經創(chuàng)建了serializers.py的文件立宜,現在修改如下:

    # 路徑quickstart/serializers.py 

from rest_framework import serializers
from .models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=120)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default="python")
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

    def create(self, validated_data):

        # 給定驗證過的數據創(chuàng)建并返回一個新的Snippet實例冒萄。
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):

        # 根據已驗證的數據更新并返回已經存在的Snippet實例

        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance

序列化器列的第一部分定義了獲得序列化/反序列化的字段。create()update()方法定義了在調用serializer.save()時如何創(chuàng)建和修改完整的實例橙数。

序列化器列與Django form類非常類似尊流,在各種字段中,包含類似驗證標志灯帮,例如require,max_lengthdefault

字段標識還可以控制serializer在某些情況下如何顯示徙邻,比如渲染HTML時候蚁吝,上面{'base_template':'textarea.html'},標志等同于在Django Form類中使用widget=widget.Textarea.這對于控制如何顯示可以瀏覽的API 特別有用箕戳,我們將在后面講解看到确憨。

我們實際上也可以通過使用ModelSerializer類來節(jié)省一些時間,我們稍后會看到瞪醋,但是現在我們將使用我們明確定義的serializer忿晕。

2.3 使用Serializers

再進一步了解之間,我們先熟悉使用我們的新類Serializer類银受。讓我們進入Django sehll

python manage.py shell

我們已經導入了幾個模塊践盼,然后開始創(chuàng)建一些片段代碼來處理

AdministratordeiMac:tutorial administrator$ python manage.py shell
Python 3.7.2 (default, Feb 12 2019, 08:16:38) 
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)

>>> from quickstart.models import Snippet
>>> from quickstart.serializers import SnippetSerializer
>>> from rest_framework.renderers import JSONRenderer
>>> from rest_framework.parsers import JSONParser
>>> 
>>> snippet = Snippet(code='foo="bar"\n')
>>> snippet.save()
>>> 
>>> snippet = Snippet(code='print "Hello , World"\n')
>>> snippet.save()

我們已經有幾個可以使用的片段實例,讓我們看看序列化中的一個實例:

>>> serializer = SnippetSerializer(snippet)
>>> serializer.data
{'id': 2, 'title': '', 'code': 'print "Hello , World"\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
>>> 

此時宾巍,我們將模型實例轉換成為python原生數據類型咕幻。要完成序列化過程,我們將數據渲染成json

>>> content = JSONRenderer().render(serializer.data)
>>> content
b'{"id":2,"title":"","code":"print \\"Hello , World\\"\\n","linenos":false,"language":"python","style":"friendly"}'
>>> 

反序列化是類似的顶霞。首先我們將一個流解析為python原生的數據類型:

>>> from django.utils.six import BytesIO
>>> 
>>> stream = BytesIO(content)
>>> data = JSONParser().parse(stream)
>>> data
{'id': 2, 'title': '', 'code': 'print "Hello , World"\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
>>> 

然后我們將這些原生的數據類型恢復正常的對象實例肄程。

>>> serializer = SnippetSerializer(data=data)
>>> serializer.is_valid()
True
>>> serializer.validated_data
OrderedDict([('title', ''), ('code', 'print "Hello , World"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
>>> serializer.save()
<Snippet: Snippet object (3)>

注意API工作形式和表單(form)是多么相似。但我們開始使用我門的序列化類編寫視圖的時候选浑,相似性將會更加明顯蓝厌。
我們也可以序列化查詢代替模型實例。為此古徒,我們只需要在序列化參數中添加一個mangy = True

>>> serializer = SnippetSerializer(Snippet.objects.all(),many = True)
>>> serializer.data
[OrderedDict([('id', 3), ('title', ''), ('code', 'print "Hello , World"'), ('linenos', False), 
('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), 
('code', 'print "Hello , World"\n'), ('linenos', False), ('language', 'python'), ('style', 
'friendly')]), OrderedDict([('id', 1), ('title', ''), ('code', 'foo="bar"\n'), ('linenos', 
False), ('language', 'python'), ('style', 'friendly')])]
>>> 

2.4 使用ModelSerializers

我們在SnippetSerializer類中重復包含Snippet模型中的信息拓提。如果能保持代碼簡潔,就像大家經常說的don't repeat yourself隧膘。就像Django 提供了Form類和MoldelForm類一樣代态,RESTFramework包括Serializer類和ModelSerializers類一樣寺惫。我們試著看看使用ModelSerializer類重構我們的序列化類。再次打開quickstrat/是蹦疑、serializers類西雀。并將SnippetSerializer類的內容替換為下面內容:
以前的SnippetSerializer類的內容:

from rest_framework import serializers
from .models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=120)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default="python")
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

    def create(self, validated_data):

        # 給定驗證過的數據創(chuàng)建并返回一個新的Snippet實例。
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):

        # 根據已驗證的數據更新并返回已經存在的Snippet實例

        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance

替換如下:

from rest_framework import serializers
from .models import Snippet

class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

序列化程序有一個非常棒的屬性必尼,就是可以通過打印其結構(representation)來檢查序列化實例中的所有字段蒋搜。

>>> from quickstart.serializers import SnippetSerializer
>>> serialzier = SnippetSerializer()
>>> print(repr(serializer))
SnippetSerializer(<QuerySet [<Snippet: Snippet object (3)>, <Snippet: Snippet object (2)>, <Snippet: Snippet object (1)>]>, many=True):
    id = IntegerField(read_only=True)
    title = CharField(allow_blank=True, max_length=120, required=False)
    code = CharField(style={'base_template': 'textarea.html'})
    linenos = BooleanField(required=False)
    language = ChoiceField(choices=[('abap', 'ABAP'), ('abnf', 'ABNF'), ('ada', 'Ada'), ('adl', 'ADL'), ('agda', 'Agda'), .... ('zephir', 'Zephir')], default='python')
    style = ChoiceField(choices=[('abap', 'abap'),.... ('trac', 'trac'), ('vim', 'vim'), ('vs', 'vs'), ('xcode', 'xcode')], default='friendly')
>>> 

注意ModelSerializer類并不會做任何特別神奇的事情,他們只是創(chuàng)建序列化器類的快捷方式:

  • 自動確定一組字段(不用重復去定義類屬性)判莉。
  • 默認簡單的實現create()update()方法。

2.5 使用我們的Serializer編寫常規(guī)的Django視圖

我們嘗試使用我們的Serializer類編寫一些API視圖育谬。目前我們不用任何REST Framework 的其他功能券盅,我么只編寫常規(guī)的Django視圖。

路徑 : quickstart/view.py膛檀,下面是我們開始寫的內容
from rest_framework import viewsets
from .serializers import UserSerializer, GroupSerializer
from django.contrib.auth.models import User, Group


class UserViewSet(viewsets.ModelViewSet):

    # 允許用戶查看或者編輯API端點

    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer


class GroupViewSet(viewsets.ModelViewSet):

    # 允許組查看或者編輯API端點

    queryset = Group.objects.all()
    serializer_class = GroupSerializer

然后替換如下:


quickstart/view.py #

from django.http import HttpResponse,JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from quickstart.models import Snippet
from quickstart.serializers import SnippetSerializer


@csrf_exempt
def snippet_list(request):
    # 列出所有代碼 snippet锰镀, 或者創(chuàng)建一個新的snippet

    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JsonResponse(serializer.data, safe=False)
    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)

    return JsonResponse(serializer.errors, status=400)

注意,因為我們希望能夠從不具有CSRF令牌的客戶端POST到此視圖咖刃,所以我們需要將該視圖標記為@csrf_exempt.這不是你通常想要做的事情泳炉,并且RESTframework視圖實際上比這更具實用行為,但是他現在足夠達到我們的目的嚎杨。
我們還需要一個與單個相對應的視圖花鹅,并可用于檢索、更新或者刪除snippet.

quickstart/view.py #

@csrf_exempt
def snippet_detail(request, pk):
    # 獲取枫浙、更新或者刪除一個代碼 snippet

    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)
    
    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return JsonResponse(serializer.data)
    
    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(snippet, data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data)
        return JsonResponse(serializer.errors, status=400)
    
    elif request.method == 'DELETE':
        snippet.delete()
        return HttpResponse(status=204)

最后刨肃,我們需要把這些駛入鏈接起來,創(chuàng)建quickstart/urls.py 文件


from django.conf.urls import url
from .views import snippet_list, snippet_detail

urlpatterns = [
    url('^quickstart/$', snippet_list),
    url('^quickstart/(?P<pk>[0-9]+)/$', snippet_detail),
]

另外需要在tutorial/urls.py中吧root urlconf來包含我們的quickstart應用的urls箩帚。修改為下面內容:

from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include

urlpatterns = [
    path('admin/', admin.site.urls),
    url('^', include('quickstart.urls')),
]

3. 測試我們在Web API上的第一次訪問

啟動quickstart服務真友,我們先退出shell

quit()

啟動Django開發(fā)服務

AdministratordeiMac:tutorial administrator$ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
May 27, 2019 - 07:13:55
Django version 2.2.1, using settings 'tutorial.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

在瀏覽器中輸入http://127.0.0.1:8000/quickstart/

quickjson.png

或者安裝httpie

pip install httpie

然后在終端輸入:http http://127.0.0.1:8000/quickstart/,如下

httpiejson.png
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市紧帕,隨后出現的幾起案子盔然,更是在濱河造成了極大的恐慌,老刑警劉巖是嗜,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愈案,死亡現場離奇詭異,居然都是意外死亡叠纷,警方通過查閱死者的電腦和手機刻帚,發(fā)現死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涩嚣,“玉大人崇众,你說我怎么就攤上這事掂僵。” “怎么了顷歌?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵锰蓬,是天一觀的道長。 經常有香客問我眯漩,道長芹扭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任赦抖,我火速辦了婚禮舱卡,結果婚禮上,老公的妹妹穿的比我還像新娘队萤。我一直安慰自己轮锥,他們只是感情好,可當我...
    茶點故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布要尔。 她就那樣靜靜地躺著舍杜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赵辕。 梳的紋絲不亂的頭發(fā)上既绩,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天,我揣著相機與錄音还惠,去河邊找鬼饲握。 笑死,一個胖子當著我的面吹牛吸重,可吹牛的內容都是我干的互拾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嚎幸,長吁一口氣:“原來是場噩夢啊……” “哼颜矿!你這毒婦竟也來了?” 一聲冷哼從身側響起嫉晶,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤骑疆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后替废,有當地人在樹林里發(fā)現了一具尸體箍铭,經...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年椎镣,在試婚紗的時候發(fā)現自己被綠了诈火。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡状答,死狀恐怖冷守,靈堂內的尸體忽然破棺而出刀崖,到底是詐尸還是另有隱情,我是刑警寧澤拍摇,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布亮钦,位于F島的核電站,受9級特大地震影響充活,放射性物質發(fā)生泄漏蜂莉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一混卵、第九天 我趴在偏房一處隱蔽的房頂上張望映穗。 院中可真熱鬧,春花似錦淮菠、人聲如沸男公。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至澄阳,卻和暖如春拥知,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背碎赢。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工低剔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肮塞。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓襟齿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親枕赵。 傳聞我的和親對象是個殘疾皇子猜欺,可洞房花燭夜當晚...
    茶點故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內容