第5章 Django模板

内容来源于《Web接口开发与自动化测试——基于Python语言》虫师编著,如有涉及版权问题,归虫师本人所有。请大家支持虫师的著作:http://www.broadview.com.cn/book/4811
源码下载:https://github.com/defnngj/guest

内容:这一章需要重新开发发布会管理和嘉宾管理、以及签到等页面。

5.1 Django-bootstrap3

本章将使用Bootstrap前端框架结合Django来开发Web页面。

什么是Bootstrap?
Bootstrap来自Twitter,是目前很受欢迎的前端框架。Bootstrap是基于HTML、CSS、JavaScript的,它简洁灵活,使得Web开发更加快捷。它由Twitter的设计师Mark otto和Jacob Thornton合作开发,是一个CSS/HTML框架。Bootstrap提供了优雅的HTML和CSS规范,它由动态CSS语言Less写成。
Bootstrap中文网:http://www.bootcss.com

什么是Django-bootstrap3?
Django-bootstrap3项目是将Bootstrap3(3表示Bootstrap的版本号)集成到Django中,作为Django的一个应用提供。好处是在Django中用Bootstrap会变得更加方便。
Django-bootstrap3 在PyPI仓库的地址:https://pypi.python.org/pypi/django-bootstrap3
安装完成,在.../guest/settings.py文件中添加“bootstrap3”应用。
先安装:

(venv) liujindeMacBook-Pro:guest liujin$ pip3 install django-bootstrap3
Collecting django-bootstrap3
  Downloading https://files.pythonhosted.org/packages/18/a8/f12d8491155c7f237084b883b8600faf722e3a46e54f17a25103b0fb9641/django-bootstrap3-10.0.1.tar.gz (40kB)
    100% |████████████████████████████████| 40kB 65kB/s 
Building wheels for collected packages: django-bootstrap3
  Running setup.py bdist_wheel for django-bootstrap3 ... done
  Stored in directory: /Users/liujin/Library/Caches/pip/wheels/db/cf/08/e8c774135d7b68a54f678387996c1e542af5405a3fd0a4e6e8
Successfully built django-bootstrap3
Installing collected packages: django-bootstrap3
Successfully installed django-bootstrap3-10.0.1

settings.py中增加以下配置:

# Application definition

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

5.2 发布会管理

本节完成发布会管理列表与发布会名称搜索功能的开发。

5.2.1 发布会列表

继续回到视图层的开发,打开.../sign/views.py文件,修改event_manage()视图函数。

......
from sign.models import Event
......
# 发布会管理
@login_required()
def event_manage(request):
    # username = request.COOKIES.get('user', '')  # 读取浏览器cookie
    event_list = Event.objects.all()
    username = request.session.get('user', '')  # 读取浏览器session
    return render(request, "event_manage.html", {"user": username, "events": event_list})

导入Model中的Event类,通过Event.objects.all()查询所有发布会对象(数据),并通过render()方法附加在event_manage.html页面返回给客户端。
编辑event_manage.html页面。

<!DOCTYPE html>
<html>
    <head>
        <!-- 加载bootstrap3应用、CSS、Javascript文件,% %为Django模板标签语言 -->
        {% load bootstrap3 %}
        {% bootstrap_css %}
        {% bootstrap_javascript %}
        <meta charset="utf-8">
        <!-- 设置页面标题 -->
        <title>Guest Manage</title>
    </head>

    <body role="document">
        <!-- 导航栏 -->
        <nav class="navbar navbar-inverse navbar-fixed-top">
            <div class="container">
                <div class="navbar-header">
                    <a class="navbar-brand" href="/event_manage/">Guest Manage System</a>
                </div>
                <div id="navbar" class="collapse navbar-collapse">
                    <ul class="nav navbar-nav">
                        <!-- 设置页面导航栏,class=active表示当前菜单处于选中状态 -->
                        <li class="active"><a href="#">发布会</a><li>
                        <!-- href=/guest_manage/用于跳转到嘉宾管理页面 -->
                        <li><a href="/guest_manage/">嘉宾</a><li>
                    </ul>
                    <ul class="nav navbar-nav navbar-right">
                        <!-- 两个大括号为Django模板语言,用于定义显示变量,user为客户端获取的浏览器sessionid对应的登录用户名 -->
                        <li><a href="#">{{user}}</a></li>
                        <!-- href=/logout/用于跳转到退出路径 -->
                        <li><a href="/logout/">退出</a></li>
                    </ul>
                </div>
            </div>
        </nav>
        <!-- 发布会列表 -->
        <!-- style属性中padding-top用于设置元素的上内边距,如果不设置该属性,发布会列表可能会被导航栏遮挡 -->
        <div class="row" style="padding-top: 80px;">
            <div class="col-md-6">
                <table class="table table-striped">
                    <thead>
                        <tr>
                            <th>id</th><th>名称</th><th>状态</th><th>地址</th><th>时间</th>
                        </tr>
                    </thead>
                    <tbody>
                    <!-- 通过Django模板语言,使用for循环,循环打印发布会的id、name等字段,注意for循环语句需要有对应的endfor来表示语句的结束 -->
                        {% for event in events %}
                            <tr>
                                <td>{{event.id}}</td>
                                <td>{{event.name}}</td>
                                <td>{{event.status}}</td>
                                <td>{{event.address}}</td>
                                <td>{{event.start_time}}</td>
                            </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
    </body>
</html>

接下来分段解析页面中的代码。

{% load bootstrap3 %}
{% bootstrap_css %}
{% bootstrap_javascript %}
加载bootstrap3应用、CSS和JavaScript文件。{% %}为Django模板语言的标签。

<title>Guest Manage</title>
设置页面标题为Guest Manager。

<li class="active"><a href="#">发布会</a><li>
<li><a href="/guest_manage/">嘉宾</a><li>
设置页面导航栏,class="active"表示当前菜单处于选中状态。href="/guest_manager/"用于跳转到嘉宾管理页,稍后开发该页面。

<li><a href="#">{{user}}</a></li>
<li><a href="/logout/">退出</a></li>
{{ }}为Django的模板语言标签,用于定义显示变量。user为客户端获取的浏览器sessionid对应的登录用户名。href="/logout/"定义退出路径,稍后开发该功能。

<div class="row" style="padding-top: 80px;">
在style属性中,padding-top用于设置元素的上内边距,如果不设置该属性,那么发布会列表可能会被导航栏遮挡。

{% for event in events %}
<tr>
<td>{{event.id}}</td>
<td>{{event.name}}</td>
<td>{{event.status}}</td>
<td>{{event.address}}</td>
<td>{{event.start_time}}</td>
</tr>
{% endfor %}
通过Django模板语言,循环打印发布的id、name、status、address和start_time等字段。Django模板语言与Python语法并非完全一样。for循环语句需要有对应的endfor来表示语句的结束;同样,if分支语句也需要有endif来表示语句的结束。

通过http://127.0.0.1:8000/event_manage/访问页面:

event_manager页面.png

5.2.2 搜索功能

在event_manager.html文件中添加搜索表单。

        <!-- 发布会搜索表单 -->
        <div class="page header" style="padding-top: 60px;">
            <div id="navbar" class="navbar-collapse collapse">
                <form class="navbar-form" method="get" action="/search_name/">
                    <div class="form-group">
                        <input name="name" type="text" placeholder="名称" class="form-control">
                    </div>
                    <button type="submit" class="btn btn-success">搜索</button>
                </form>
            </div>
        </div>

action="/search_name/"搜索请求路径;name="name"搜索输入框的name属性值。
在urls.py中添加搜索路径的路由。

urlpatterns = [
.......
    re_path(r'^search_name/$', views.search_name)
]

在views.py中创建search_name()视图函数。

# 发布会名称搜索
@login_required
def search_name(request):
    username = request.session.get('user', '')
    search_name = request.GET.get("name", "")
    event_list = Event.objects.filter(name__contains=search_name)
    return render(request, "event_manage.html", {"user": username, "events": event_list})

通过Get方法接收搜索关键字,并通过模糊查询,匹配发布会name字段,然后把匹配到的发布会列表返回给客户端。


发布会名称查询.png

5.3 嘉宾管理

嘉宾管理页面的开发与发布会管理页面基本相同。

5.3.1 嘉宾列表

新建.../templates/guest_manage.html页面。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    {% load bootstrap3 %}
    {% bootstrap_css %}
    {% bootstrap_javascript %}
    <meta charset="UTF-8">
    <title>Guest Manage</title>
</head>

<body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <a class="navbar-brand" href="/event_manage/">Guest Manage System</a>
            </div>
            <div id="navbar" class="collapse navbar-collapse">
                <ul class="nav navbar-nav">
                    <li><a href="/event_manage/">发布会</a></li>
                    <li class="active"><a href="#">嘉宾</a></li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    <li><a href="#">{{user}}</a></li>
                    <li><a href="/logout/">退出</a></li>
                </ul>
            </div>
        </div>
    </nav>
    <!-- 嘉宾列表 -->
    <div class="row" style="padding-top: 80px;">
        <div class="col-md-6">
            <table class="table table-striped">
                <thead>
                    <tr>
                        <th>id</th><th>名称</th><th>手机</th><th>Email</th><th>签到</th>
                        <th>发布会</th>
                    </tr>
                </thead>
                <tbody>
                    {% for guest in guest %}
                        <tr>
                            <td>{{ guest.id }}</td>
                            <td>{{ guest.realname }}</td>
                            <td>{{ guest.phone }}</td>
                            <td>{{ guest.eamil }}</td>
                            <td>{{ guest.sign }}</td>
                            <td>{{ guest.event }}</td>
                        </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>

</body>
</html>

与event_manage.html页面结构基本相同,不过依然要注意两个地方:

<title>Guest Manage</title>
页面标题为Guest Manage。

<li><a href="/event_manage/">发布会</a></li>
<li class="active"><a href="#">嘉宾</a></li>
当前处理嘉宾管理页面,所以设置嘉宾菜单处于选中状态class="active"。为发布会菜单设置跳转路径href="/event_manage/"。

{% for guest in guest %}
<tr>
<td>{{ guest.id }}</td>
<td>{{ guest.realname }}</td>
<td>{{ guest.phone }}</td>
<td>{{ guest.eamil }}</td>
<td>{{ guest.sign }}</td>
<td>{{ guest.event }}</td>
</tr>
{% endfor %}
通过Django模板语言的for循环读取嘉宾列表,并显示这些字段。

在urls.py文件添加嘉宾路径的路由。

urlpatterns = [
......
    re_path(r'^guest_manage/$', views.guest_manage),
]

打开views.py文件,创建guest_mansge()视图函数。

# 嘉宾管理
from sign.models import Event, Guest
......
@login_required
def guest_manage(request):
    username = request.session.get('user', '')
    guest_list = Guest.objects.all()
    return render(request, "guest_manage.html", {"user": username, "guests": guest_list})

导入Model中的Guest类,通过Guest.objects.all()查询所有嘉宾对象数据,并通过render()方法附加在guest_manage.html页面,并返回给客户端。
嘉宾管理页面见下图:


嘉宾管理页面.png

下面写嘉宾搜索功能。
先修改guest_manage.html文件。

    <!-- 嘉宾搜索表单 -->
        <div class="page header" style="padding-top: 60px;">
            <div id="navbar" class="navbar-collapse collapse">
                <form class="navbar-form" method="get" action="/guest_search/">
                    <div class="form-group">
                        <input name="realname" type="text" placeholder="请输入嘉宾名称" class="form-control">
                    </div>
                    <button type="submit" class="btn btn-success">搜索</button>
                </form>
            </div>
        </div>

再修改urls.py。

urlpatterns = [
.......
    re_path(r'^guest_search/$', views.guest_search),
]

最后修改views.py,创建guest_search()视图函数。

# 嘉宾名称搜索
@login_required
def guest_search(request):
    username = request.session.get('user', '')
    guest_search = request.GET.get("realname", "")
    guest_list = Guest.objects.filter(realname__contains=guest_search)
    return render(request, "guest_manage.html", {"user": username, "guests": guest_list})

最后的效果图如下:


嘉宾名称查询.png

5.3.2 分页器

对于嘉宾数据量比较大,所以需要分页功能。
Django提供了Paginator类来实现分页功能。分页功能略微复杂,首先进入Django的shell练习。

(venv) liujindeMacBook-Pro:guest liujin$ python3 manage.py shell
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 26 2018, 23:26:24) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)

>>> from django.core.paginator import Paginator  # 导入Paginator类
>>> from sign.models import Guest  # Guest下的所有表
>>> guest_list = Guest.objects.all()  # 查询Guest表的所有数据
>>> p = Paginator(guest_list, 2)  # 创建每页2条数据的分页器
<string>:1: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 'sign.models.Guest'> QuerySet.
>>> p.count  # 查看共有多少条数据
6
>>> p.page_range  # 查看分多少页,每页2条数据的话
range(1, 4)
# 第一页
>>> page1 = p.page(1)  # 获取第一页数据
>>> page1  # 当前第几页
<Page 1 of 3>
>>> page1.object_list  # 当前页的对象
<QuerySet [<Guest: 张三>, <Guest: 李四>]>
>>> for g in page1:  # 循环打印第一页嘉宾的realname
...     g.realname
... 
'张三'
'李四'
# 第二页
>>> page2 = p.page(2)  # 获取第二页数据
>>> page2.start_index()  # 第二页的第一条数据
3
>>> page2.end_index()  # 第二页的最后一条数据
4
>>> page2.has_previous()  # 是否有上一页
True
>>> page2.has_next()  # 是否有下一页
True
>>> page2.previous_page_number()  # 上一页是第几页
1 
>>> page2.next_page_number()  # 下一页是第几页
3
# 第三页
>>> page3 = p.page(3)  # 获取第三页数据
>>> page3.has_next()  # 是否有下一页
False
>>> page3.has_previous()  # 是否有上一页
True
>>> page3.has_other_pages()  # 是否有其他页
True
>>> page3.previous_page_number()  # 上一页是第几页
2

打开views.py文件,修改guest_manage()视图函数。


嘉宾管理分页功能.png

5.4 签到功能

对于发布会签到系统来说,最重要的功能就是签到,而发布会管理功能和嘉宾管理功能,都要服务于签到功能。

5.4.1 添加签到链接

最好的方式就是在发布列表中,给每一条发布会都提供一个“签到”链接,用来打开对于的签到页面。
在event_manage.html页面,增加一列签到链接。

        <!-- 发布会列表 -->
        <div class="row" style="padding-top: 80px;">
            <div class="col-md-6">
                <table class="table table-striped">
                    <thead>
                        <tr>
                            <th>id</th><th>名称</th><th>状态</th><th>地址</th><th>时间</th><th>签到</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for event in events %}
                            <tr>
                                <td>{{event.id}}</td>
                                <td>{{event.name}}</td>
                                <td>{{event.status}}</td>
                                <td>{{event.address}}</td>
                                <td>{{event.start_time}}</td>
                                <td>
                                    <a href="/sign_index/{{ event.id }}/" target="{{ event.id }}_blank">sign</a>
                                </td>
                            </tr>
                        {% endfor %}
                    </tbody>
......

这样,发布会管理列表多了一列签到链接。


签到链接.png

当单击“sign”链接时,路径默认跳转到“/sign_index/{{ event_id }}/”路径。其中,{{ event_id }}为发布会id。target="{{ event.id }}_blank"属性设置链接在新窗口打开。
接下来在urls.py文件中添加签到页面路径的路由。

urlpatterns = [
......
    re_path(r'^sign_index/(?P<eid>[0-9]+)/$', views.sign_index),
]

这里的(?P<eid>[0-9]+)匹配发布会的id,而且必须为数字。

5.4.2 签到页面

打开views.py文件,创建sign_index()视图函数。

from django.shortcuts import render, get_object_or_404
......
# 签到页面
@login_required
def sign_index(request, eid):
    event = get_object_or_404(Event, id=eid)
    return render(request, 'sign_index.html', {'event': event})

sign_index()函数获取从URL配置得到的eid,作为发布会id的查询条件。
创建..../templates/sign_index.html签到页面。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    {% load bootstrap3 %}
    {% bootstrap_css %}
    {% bootstrap_javascript %}
    <meta charset="UTF-8">
    <title>发布会签到</title>
</head>
<body>
    <!-- 导航栏 -->
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <a class="navbar-brand" href="#">{{ event.name }}</a>
            </div>
            <div id="navbar" class="collapse navbar-collapse">
                <ul class="nav navbar-nav">
                    <li><a href="/event_manage/">发布会</a></li>
                    <li><a href="/guest_manage/">嘉宾</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- 签到功能 -->
    <div class="page-header" style="padding-top: 80px;">
        <div id="navbar" class="navbar-collapse collapse">
            <form class="navbar-form" method="post" action="/sign_index_action/{{ event.id}}/">
                <div class="form-group">
                    <input name="phone" type="text" placeholder="请输入手机号" class="form-control">
                </div>
                <button type="submit" class="btn btn-success">签到</button>
                <font color="red">
                    <br>{{ hint }}
                    <br>{{ guest.realname }}
                    <br>{{ guest.phone }}
                </font>
            </form>
        </div>
    </div>

</body>
</html>

<a class="navbar-brand" href="#">{{ event.id }}</a>
将页面标题设置为发布会名称。

<li><a href="/event_manage/">发布会</a></li>
<li><a href="/guest_manage/">嘉宾</a></li>
设置发布会与嘉宾导航链接。

<form class="navbar-form" method="post" action="/sign_index_action/{{ event.id}}/">
签到表单通过POST请求将签到手机号提交到/sign_index_action/{{ event.id}}/路径。

嘉宾签到页面.png

5.4.3 签到动作

打开urls.py文件,添加签到动作路径的路由。

urlpatterns = [
......
    re_path(r'^sign_index_action/(?P<eid>[0-9]+)/$', views.sign_index_action)
]

打开views.py文件,创建sign_index_action()视图函数。

from django.views.decorators.csrf import csrf_exempt
......
# 签到动作
@login_required
@csrf_exempt
def sign_index_action(request, eid):
    event = get_object_or_404(Event, id=eid)
    phone = request.POST.get('phone', '')
    print(phone)

    result = Guest.objects.filter(phone=phone)
    if not result:
        return render(request, 'sign_index.html', {'event': event, 'hint': 'phone error.'})

    result = Guest.objects.filter(phone=phone, event_id=eid)
    if not result:
        return render(request, 'sign_index.html', {'event': event, 'hint': 'event id or phone error.'})

    result = Guest.objects.get(phone=phone, event_id=eid)
    if result.sign:
        return render(request, 'sign_index.html', {'event': event, 'hint': "user has sign in."})
    else:
        Guest.objects.filter(phone=phone, event_id=eid).update(sign='1')
        return render(request, 'sign_index.html', {'event': event, 'hint': 'sign in success!', 'guest': result})

通过一些列的判断来展示对应的结果。
下图是签到成功的截图:


嘉宾签到成功.png

5.5 退出系统

打开uls.py,添加退出路径的路由。

urlpatterns = [
......
    re_path(r'^logout/$', views.logout),
]

打开views.py,创建logout()视图函数。

# 退出登录
@login_required
def logout(request):
    auth.logout(request)
    response = HttpResponseRedirect('/index/')
    return response

退出后就到登录界面。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

  • # Awesome Python [![Awesome](https://cdn.rawgit.com/sindr...
    emily_007阅读 2,203评论 0 3
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    小迈克阅读 2,967评论 1 3
  • 版权: https://github.com/haiiiiiyun/awesome-django-cn Aweso...
    若与阅读 23,037评论 3 241
  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    aimaile阅读 26,454评论 6 428
  • 这个世界很暖,暖到冬天不是冷的,夏天不是热的。阳光不仅仅晒了我的衣服,还暖了我的心。彩虹不仅仅是漂亮的一瞬间,也是...
    丫头那一微笑阅读 308评论 0 0