上药三品,神与气精

曾因酒醉鞭名马 生怕情多累美人


  • 首页

  • 关于

  • 分类

  • 标签

  • 归档

  • 搜索

ftp-download

发表于 2017-08-03 | 分类于 web | 阅读次数:
字数统计: 734 | 阅读时长 ≈ 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#!/usr/bin/env python
# encoding: utf-8

import os
import sys
import ftplib
import traceback


class FtpDownloader(object):
PATH_TYPE_UNKNOWN = -1
PATH_TYPE_FILE = 0
PATH_TYPE_DIR = 1

def __init__(self, host, user=None, passwd=None, port=21, timeout=10):
self.conn = ftplib.FTP(
host=host,
user=user,
passwd=passwd,
timeout=timeout
)

def dir(self, *args):
'''
by defualt, ftplib.FTP.dir() does not return any value.
Instead, it prints the dir info to the stdout.
So we re-implement it in FtpDownloader, which is able to return the dir info.
'''
info = []
cmd = 'LIST'
for arg in args:
if arg:
cmd = cmd + (' ' + arg)
self.conn.retrlines(cmd, lambda x: info.append(x.strip().split()))
return info

def tree(self, rdir=None, init=True):
'''
recursively get the tree structure of a directory on FTP Server.
args:
rdir - remote direcotry path of the FTP Server.
init - flag showing whether in a recursion.
'''
if init and rdir in ('.', None):
rdir = self.conn.pwd()
tree = []
tree.append((rdir, self.PATH_TYPE_DIR))

dir_info = self.dir(rdir)
for info in dir_info:
attr = info[0] # attribute
name = info[-1]
path = os.path.join(rdir, name)
if attr.startswith('-'):
tree.append((path, self.PATH_TYPE_FILE))
elif attr.startswith('d'):
if (name == '.' or name == '..'): # skip . and ..
continue
tree.extend(self.tree(rdir=path,init=False)) # recurse
else:
tree.append(path, self.PATH_TYPE_UNKNOWN)

return tree

def downloadFile(self, rfile, lfile):
'''
download a file with path %rfile on a FTP Server and save it to locate
path %lfile.
'''
ldir = os.path.dirname(lfile)
if not os.path.exists(ldir):
os.makedirs(ldir)
f = open(lfile, 'wb')
self.conn.retrbinary('RETR %s' % rfile, f.write)
f.close()
return True

def treeStat(self, tree):
numDir = 0
numFile = 0
numUnknown = 0
for path, pathType in tree:
if pathType == self.PATH_TYPE_DIR:
numDir += 1
elif pathType == self.PATH_TYPE_FILE:
numFile += 1
elif pathType == self.PATH_TYPE_UNKNOWN:
numUnknown += 1
return numDir, numFile, numUnknown


def downloadDir(self, rdir='.', ldir='.', tree=None,
errHandleFunc=None, verbose=True):
'''
download a direcotry with path %rdir on a FTP Server and save it to
locate path %ldir.
args:
tree - the tree structure return by function FtpDownloader.tree()
errHandleFunc - error handling function when error happens in
downloading one file, such as a function that writes a log.
By default, the error is print to the stdout.
'''
if not tree:
tree = self.tree(rdir=rdir, init=True)
numDir, numFile, numUnknown = self.treeStat(tree)
if verbose:
print 'Host %s tree statistic:' % self.conn.host
print '%d directories, %d files, %d unknown type' % (
numDir,
numFile,
numUnknown
)

if not os.path.exists(ldir):
os.makedirs(ldir)
ldir = os.path.abspath(ldir)

numDownOk = 0
numDownErr = 0
for rpath, pathType in tree:
lpath = os.path.join(ldir, rpath.strip('/').strip('\\'))
if pathType == self.PATH_TYPE_DIR:
if not os.path.exists(lpath):
os.makedirs(lpath)
elif pathType == self.PATH_TYPE_FILE:
try:
self.downloadFile(rpath, lpath)
numDownOk += 1
except Exception as err:
numDownErr += 1
if errHandleFunc:
errHandleFunc(err, rpath, lpath)
elif verbose:
print 'An Error occurred when downloading '\
'remote file %s' % rpath
traceback.print_exc()
print
if verbose:
print 'Host %s: %d/%d/%d(ok/err/total) files downloaded' % (
self.conn.host,
numDownOk,
numDownErr,
numFile
)
elif pathType == self.PATH_TYPE_UNKNOWN:
if verbose:
print 'Unknown type romote path got: %s' % rpath

if verbose:
print 'Host %s directory %s download finished:' % (
self.conn.host, rdir
)
print '%d directories, %d(%d failed) files, %d unknown type.' % (
numDir,
numFile,
numDownErr,
numUnknown
)
return numDir, numFile, numUnknown, numDownErr


if __name__ == '__main__':
import sys
import traceback
from pprint import pprint as pr

flog = open('err.log', 'wb')

def run(host):
try:
fd = FtpDownloader(
host=host,
user='test',
passwd='test',
port=21,
timeout=10
)
numDir, numFile, numUnknown, numDownErr = fd.downloadDir(
rdir='.',
ldir='download',
tree=None,
errHandleFunc=None,
verbose=True
)
flog.write(
'%s\nok\n'
'%d directories, %d(%d failed) files, %d unknown type\n\n\n' % (
host,
numDir,
numFile,
numDownErr,
numUnknown
)
)
except Exception as err:
traceback.print_exc()
flog.write(
'%s\nerror\n%s\n\n\n' % (
host,
traceback.format_exc()
)
)

pr(run(sys.argv[1]))
flog.close()

adaptor

发表于 2017-08-01 | 分类于 pattern | 阅读次数:
字数统计: 240 | 阅读时长 ≈ 1

直接看代码吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# -*- coding: utf-8 -*-

class OldCourse(object):
"""
老的课程类
"""


def show(self):
"""
显示关于本课程的所有信息
"""

print("show description")
print("show teacher of course")
print("show labs")


class Page(object):
"""
使用课程对象的客户端
"""


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

def render(self):
self.course.show()


class NewCourse(object):
"""
新的课程类, 为了模块化显示课程信息,实现了新的课程类
"""

def show_desc(self):
"""
显示描述信息
"""

print("show description")

def show_teacher(self):
"""
显示老师信息
"""

print("show teacher of course")

def show_labs(self):
"""
显示实验
"""

print("show labs")


class Adapter(object):
"""
适配器, 尽管实现了新的课程类,但是在很多代码中还是需要使用 OldCourse.show() 方法
"""


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

def show(self):
"""
适配方法,调用真正的操作
"""

self.course.show_desc()
self.course.show_teacher()
self.course.show_labs()


if __name__ == '__main__':
old_course = OldCourse()
page = Page(old_course)
page.render()
print("")
new_course = NewCourse()
# 新课程类没有 show 方法,我们需要使用适配器进行适配
adapter = Adapter(new_course)
page = Page(adapter)
page.render()

merge-sort

发表于 2017-07-16 | 阅读次数:
字数统计: 84 | 阅读时长 ≈ 1

理解不深入的缘故,每次写算法都要对着例子抄。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def mergesort(seq):
mid = len(seq)
lft, rgt = seq[:mid], seq[mid:]
if len(lft) > 1:
lft = mergesort(lft)
if len(rgt) > 1:
rgt = mergesort(rgt)

res = []
while lft and rgt:
if lft[-1] >= rgt[-1]:
res.append(lft.pop())
else:
res.append(rgt.pop())
res.reverse()
return (lft or rgt) + res

这些都是吃饭的家伙。。

django-rest-framework-5

发表于 2017-07-15 | 分类于 web | 阅读次数:
字数统计: 1.2k | 阅读时长 ≈ 4

到目前为止,在我们的API中关系(relationship)还是通过主键来表示的。在这部分的教程中,我们将用超链接方式来表示关系,从而提升API的统一性和可发现性。

##1. 为API根创建一个endpoint

到目前为止,我们已经有了’snippets’和’users’的endpoint, 但是我们还没有为我们的API单独创立一个端点入口。我们可以用常规的基于函数的view和之前介绍的 @api_view 修饰符来创建。

from rest_framework import renderers
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse

@api_view((‘GET’,))
def api_root(request, format=None):
return Response({
‘users’: reverse(‘user-list’, request=request, format=format),
‘snippets’: reverse(‘snippet-list’, request=request, format=format)
})
请注意,我们用 REST framework 的 reverse 函数来返回完全合规的URLs.

##2. 为高亮的Snippet创建一个endpoint

我们目前还没有为支持代码高亮的Snippet创建一个endpoints.

与之前的API endpoints不同, 我们将直接使用HTML呈现,而非JSON。在 REST framework中有两种风格的HTML render, 一种使用模板来处理HTML,一种则使用预先处理的方式。在这里我们使用后者。

另一个需要我们考虑的是,对于高亮代码的view并没有具体的泛型view可以直接利用。我们将只返回实例的一个属性而不是对象实例本身。

没有具体泛型view的支持,我们使用基类来表示实例,并创建我们自己的 .get() 方法。在你的 snippets.views 中增加:

from rest_framework import renderers
from rest_framework.response import Response

class SnippetHighlight(generics.SingleObjectAPIView):
model = Snippet
renderer_classes = (renderers.StaticHTMLRenderer,)

def get(self, request, *args, **kwargs):
    snippet = self.get_object()
    return Response(snippet.highlighted) 

和以往一样,我们需要为新的view增加新的URLconf,如下增加urlpatterns:

url(r’^$’,’api_root’),
还需要为代码高亮增加一个urlpatterns:

url(r’^snippets/(?P[0-9]+)/highlight/$’, views.SnippetHighlight.as_view()),

##3. API超链接化

在Web API设计中,处理实体间关系是一个有挑战性的工作。我们有许多方式来表示关系:

使用主键;
使用超链接;
使用相关实体唯一标识的字段;
使用相关实体的默认字符串表示;
在父级表示中嵌入子级实体;
其他自定义的表示。
REST framework支持所有这些方式,包括正向或者反向的关系,或者将其应用到自定义的管理类中,例如泛型外键。

在这部分,我们使用超链接方式。为了做到这一点,我们需要在序列化器中用 HyperlinkedModelSerializer 来替代之前的 ModelSerializer.

HyperlinkedModelSerializer 与 ModelSerializer 有如下的区别:

缺省状态下不包含 pk 字段;

具有一个 url 字段,即HyperlinkedIdentityField类型.

用HyperlinkedRelatedField表示关系,而非PrimaryKeyRelatedField.

我们可以很方便的改写现有代码来使用超连接方式:

class SnippetSerializer(serializers.HyperlinkedModelSerializer): owner = serializers.Field(source=’owner.username’) highlight = serializers.HyperlinkedIdentityField(view_name=’snippet-highlight’, format=’html’)

class Meta:
model = models.Snippet
fields = (‘url’, ‘highlight’, ‘owner’,
‘title’, ‘code’, ‘linenos’, ‘language’, ‘style’)
class UserSerializer(serializers.HyperlinkedModelSerializer): snippets = serializers.HyperlinkedRelatedField(many=True, view_name=’snippet-detail’)

class Meta:
model = User
fields = (‘url’, ‘username’, ‘snippets’)
注意:我们也增加了一个新的 ‘highlight’ 字段。该字段与 url 字段相同类型。不过它指向了 ‘snippet-highlight’的 url pattern, 而非’snippet-detail’ 的url pattern.

因为我们已经有一个 ‘.json’的后缀,为了更好的表明highlight字段链接的区别,使用一个 ‘.html’ 的后缀。

##4. 正确使用URL patterns

如果要使用超链接API,就必须确保正确的命名和使用 URL patterns. 我们来看看我们需要命名的 URL patterns:

指向 ‘user-list’ 和 ‘snippet-list’ 的API根.
snippet的序列化器,包括一个 ‘snippet-highlight’字段.
user序列化器,包含一个 ‘snippet-detail’字段.
snippet 和user的序列化器,包含 ‘url’ 字段(会缺省指向’snippet-detail’ 和 ‘user-detail’.
一番工作之后,最终的 ‘urls.py’ 文件应该如下所示:

API endpoints

urlpatterns = format_suffix_patterns(patterns(‘snippets.views’,
url(r’^$’, ‘api_root’),
url(r’^snippets/$’,
views.SnippetList.as_view(),
name=’snippet-list’),
url(r’^snippets/(?P[0-9]+)/$’,
views.SnippetDetail.as_view(),
name=’snippet-detail’),
url(r’^snippets/(?P[0-9]+)/highlight/$’,
views.SnippetHighlight.as_view(),
name=’snippet-highlight’),
url(r’^users/$’,
views.UserList.as_view(),
name=’user-list’),
url(r’^users/(?P[0-9]+)/$’,
views.UserDetail.as_view(),
name=’user-detail’)
))

Login and logout views for the browsable API

urlpatterns += patterns(‘’,
url(r’^api-auth/‘, include(‘rest_framework.urls’,
namespace=’rest_framework’)),
)

##5. 添加分页

列表view有时会返回大量的实例结果,所以我们应该把结果分页显示,以便用户使用。

通过在 settings.py 中添加如下配置,我们就能在结果列表中增加分页的功能:

REST_FRAMEWORK ={‘PAGINATE_BY’:10}
请注意REST framework的所有配置信息都是存放在一个叫做 ‘REST_FRAMEWORK’的dictionary中,以便于其他配置区分。

如有必要,你也可以自定义分页的方式,这里不再赘述。

django-rest-framework-tutorial-4

发表于 2017-07-14 | 分类于 web | 阅读次数:
字数统计: 1.9k | 阅读时长 ≈ 7

drf框架教程4(djangorestframework)

写在前面

在外包的这段日子,不知如何描述滋味,虽然没有网络可以说限制了工作时间逛网页这种,但是自我感觉收获满足不了自己。是时候重新出发了。

工作当中有用到drf框架,在csdn中找到了还不错的中文翻译文档,受益匪浅。想着自己也做一部分翻译的工作。如有错误之处,请多多指教。

额外说明

目前版本为 3.6.3

马太胖老师的前三篇翻译

框架github地址

文档地址

认证和权限

目前为止,我们的API对谁能够编辑或者删除snippet(代码片段)还没有任何限制。我们将增加一些扩展功能来确保以下:

  • snippets总关联一个创建者
  • 只有认证后的用户才能创建一个snippets
  • 只有创建者才能更新或者删除snippet
  • 非认证的请求只拥有只读的权限

对model做一些修改,增加一个表示创建者,另外增加一个用来存储代码中的HTML高亮。

1
2
3
4
# snippets/models.py
owner = models.ForeignKey('auth.User',
related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()

另外 我们需要确保模型保存的时候,我们能够生成高亮的字段,这里使用pygments代码高亮库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight


def save(self, *args, **kwargs):
"""
Use the `pygments` library to create a highlighted HTML
representation of the code snippet.
"""
lexer = get_lexer_by_name(self.language)
linenos = self.linenos and 'table' or False
options = self.title and {'title': self.title} or {}
formatter = HtmlFormatter(style=self.style, linenos=linenos,
full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
super(Snippet, self).save(*args, **kwargs)

为用户模型增加endpoints

现在我们有一些用户了,我们最好也把用户增加到API上,创建一个新的序列化脚本serializers.py

1
2
3
4
5
6
7
8
9
# snippets/serializers.py
from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):
snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())

class Meta:
model = User
fields = ('id', 'username', 'snippets')

因为snippets和user是反向关联,所以在使用ModelSerializers时不会缺省加入,因此需要显示加入。

我们还需要创建一些对用户呈现而言的views,最好使用只读的view,所以使用ListAPIView和RetrieveAPIView泛型类Views。

1
2
3
4
5
6
7
8
9
10
11
12
# snippets/views.py
from django.contrib.auth.models import User


class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer

确保导入了UserSerializers类

1
from snippets.serializers import UserSerializer

修改url

1
2
url(r'^users/$', views.UserList.as_view()),
url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),

snippets与users关联

现在,如果我们创建一个code snippet,还没有方法指定其创建者。User并没有作为序列化内容的一部分发送,而是作为request的一个属性。

这里的处理方法是重载snippet view中的.pre_save()方法,它可以让我们处理request中隐式的信息。

在SnippetList和SnippetDetail的view类中,都需要添加如下的方法:

1
2
def perform_create(self, serializer):
serializer.save(owner=self.request.user)

更新序列器

现在snippets已经和创建者关联起来了,我们接下来还需要更新SnippetSerializer,在其定义中增加一个新的字段:

1
owner = serializers.ReadOnlyField(source='owner.username')

Note: 确定你在嵌入类Meta的字段列表中也加入了’owner’。

这个字段所做的十分有趣。source参数表明增加一个新字段,可以指定序列化实例任何属性。它可以采用如上的点式表示(dotted notation),这时他可以直接遍历到指定的属性。在Django’s template中使用时,也可以采用类似的方式。

我们增加字段是一个无类型Field类,而我们之前的字段都是有类型的,例如CharField,BooleanFieldetc… 无类型字段总是只读的,它们只用在序列化表示中,而在反序列化时(修改model)不被使用。

给views增加必需的权限

现在代码片段 snippets 已经关联了用户,我们需要确保只有认证用户才能增、删、改snippets.

REST framework 包括许多权限类可用于view的控制。这里我们使用IsAuthenticatedOrReadOnly, 它可确保认证的request获取read-write权限,而非认证的request只有read-only 权限.

现需要在views模块中增加 import。

from rest_framework import permissions

然后需要在SnippetList和SnippetDetailview类中都增加如下属性:

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

可视化API增加登陆

如果你打开浏览器,访问可浏览API,你会发现只有登录后才能创建新的snippet了。

我们可以编辑URLconf来增加一个登录view。首先增加新的import:

from django.conf.urls import include

然后,在文件末尾增加一个pattern来为browsable API增加 login 和 logout views.

urlpatterns += [
    url(r'^api-auth/', include('rest_framework.urls',
                           namespace='rest_framework')),

]

具体的,r’^api-auth/‘部分可以用任何你想用的URL来替代。这里唯一的限制就是 urls 必须使用’rest_framework’命名空间。

现在如果你打开浏览器,刷新页面会看到页面右上方的 ‘Login’ 链接。如果你用之前的用户登录后,你就又可以创建 snippets了。

一旦你创建了一些snippets,当导航至’/users/‘时,你会看到在每个user的snippets字段都包含了一系列snippet的pk。

对象级权限

我们希望任何人都可以浏览snippets,但只有创建snippet的用户才能编辑或删除它。

为了实现这个需求,我们需要创建定制的权限(custom permission)。

在 snippets 应用中,创建一个新文件:permissions.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""

def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True

# Write permissions are only allowed to the owner of the snippet.
return obj.owner == request.user

现在我们可以为snippet实例增加定制权限了,需要编辑SnippetDetail 类的permission_classes属性:

permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                  IsOwnerOrReadOnly,)

别忘了import 这个IsOwnerOrReadOnly类。

from snippets.permissions import IsOwnerOrReadOnly

现在打开浏览器,你可以看见 ‘DELETE’ 和 ‘PUT’ 动作只会出现在那些你的登录用户创建的snippet页面上了.

通过API验证

我们已经有了一系列的权限,如果我们需要编辑任何snippet,我们需要认证我们的request。因为我们还没有建立任何authentication classes, 所以目前是默认的SessionAuthentication和BasicAuthentication在起作用。

当我们通过Web浏览器与API互动时,我们登录后、然后浏览器session可以为所有的request提供所需的验证。

如果我们使用程序访问这些API,我们则需要显式的为每个request提供认证凭证(authentication credentials)。

如果我们试图未认证就创建一个snippet,将得到错误如下:

1
2
3
4
5
http POST http://127.0.0.1:8000/snippets/ code="print 123"

{
"detail": "Authentication credentials were not provided."
}

如果我们带着用户名和密码来请求时则可以成功创建:

1
2
3
4
5
6
7
8
9
10
11
http -a tom:password123 POST http://127.0.0.1:8000/snippets/ code="print 789"

{
"id": 1,
"owner": "tom",
"title": "foo",
"code": "print 789",
"linenos": false,
"language": "python",
"style": "friendly"
}

我们已经为我们的Web API创建了相当细粒度的权限控制和相应的系统用户。

在教程第5部分part 5,我们将把所有的内容串联起来,为我们的高亮代码片段创建HTML节点,并利用系统内的超链接关联来提升API的一致性表现。

1…103104105…109
John Cheung

John Cheung

improve your python skills

543 日志
33 分类
45 标签
RSS
GitHub Email
© 2020 John Cheung
本站访客数:
|
主题 — NexT.Pisces v5.1.4
博客全站共226.3k字