Django community: RSS
This page, updated regularly, aggregates Community blog posts from the Django community.
-
Django-Rest-Framework 教程: 2. Requests 和 Responses
接着上篇的内容, 本篇中, 我们进一步介绍Django-rest-framework的其他核心部件. 首先我们来做准备工作: Request django-rest-framework中引入了新的Request类, 该Request继承自Django的HttpRequest, 并提供了更多灵活的request处理功能. 比如request.DATA属性便是专门用作Web API的属性, 类似于request.POST. request.POST # 只处理form数据; 只接受HTML 'POST'动作. request.DATA # 处理特定数据; 接受HTML 'POST', 'PUT' 和 'PATCH' 动作. 状态码 (status codes) django-rest-framework为每一个状态码都提供了打包好的, 更为精确的状态码完整描述供我们使用, 比如HTTP_400_BAD_REQUEST. API views django-rest-framework为function_based_view和class_based_view分别提供了@api_view修饰器和APIView类. 这些wrapper代码, 保证了view接收Request实例, 并会自动添加context至Response中. 这些wrapper还能自动返回正确的状态码和详细信息. 1. 开始 接着之前的views.py, 在这里, 我们已经不需要JSONResponse了, 因此, 首先将其删除. 然后修改views.py中的snippet_list view: snippets/views.py from rest_framework import status from rest_framework.decorators import api_view from rest_framework.response import Response from snippets.models import Snippet from snippets.serializers import SnippetSerializer @api_view(['GET', 'POST']) def snippet_list(request): """ 展示所有存在的snippet, 或建立新的snippet """ if request.method == 'GET': snippets = Snippet.objects.all() serializer = SnippetSerializer(snippets, many=True) return Response(serializer.data) elif request.method == 'POST': serializer = SnippetSerializer(data=request.DATA) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 以上代码相对于教程1中的代码更为简洁. 可以看到代码与form API的使用十分相似, 我们还使用了内置的状态码, 使返回的response意义更为清晰. 对于单独的snippet编辑: @api_view(['GET', 'PUT', 'DELETE']) def snippet_detail(request, pk): """ 展示, 更新或删除一个snippet """ try: snippet = Snippet.objects.get(pk=pk) except Snippet.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) if request.method == 'GET': serializer = SnippetSerializer(snippet) return Response(serializer.data) elif request.method == 'PUT': serializer = SnippetSerializer(snippet, data=request.DATA) if serializer.is_valid(): serializer.save() return Response(serializer.data) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) elif request.method == 'DELETE': snippet.delete() return Response(status=status.HTTP_204_NO_CONTENT) 从以上代码中可以看到, 我们不再指明request和response中的内容类型. request.DATA即可用来处理json数据类型类型, 也可以处理yaml或其他数据类型. 同时, django-rest-framework也能根据不同情况, 再response呈现正确的数据类型. 2. 使用其他数据格式 为了展示如何多数据格式的支持, 接下来, 我们为url添加后缀: http://example.com/api/items/4.json 为snippets/view.py中的snippet_list和snippet_detail增加format参数 def snippet_list(request, format=None): ... def snippet_detail(request, pk, format=None): ... 更新urls.py: from django.conf.urls import patterns, url from rest_framework.urlpatterns import format_suffix_patterns urlpatterns = patterns('snippets.views', url(r'^snippets/$', 'snippet_list'), url(r'^snippets/(?P<pk>[0-9]+)$', … -
Django-Rest-Framework 教程: 1. 序列化 (Serialization)
在本篇中, 我们将通过建立一个代码黏贴板(pastebin), 来熟悉组成REST framework的各组成部分, 并了解这些部件是如何相互协调工作的. 1. 环境设置 首先我们使用virtualenvwrapper创建新的virtualenv, 并安装需要的代码库: mkvirtualenv env pip install django pip install djangorestframework pip install pygments # 用于代码高亮 2. Django项目设置 我们建立Django项目tutorial, 和app snippets django-admin.py startproject tutorial cd tutorial python manage.py startapp snippets 设置settings.py: # tutorial/settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'database_name', 'USER': 'database_user', 'PASSWORD': 'database_password', 'HOST': '', 'PORT': '' } } INSTALLED_APPS = ( ... 'rest_framework', 'snippets', ) 设置urls.py, 将新建的snippet app中的urls.py加入到其中: # tutorial/urls.py urlpatterns = patterns('', url(r'^', include('snippets.urls')), ) 3. 创建Model 我们建立Snippet Model用于储存代码: # snippet/models.py from django.db import models from pygments.lexers import get_all_lexers from pygments.styles import get_all_styles LEXERS = [item for item in get_all_lexers() if item[1]] LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS]) 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=100) style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100) class Meta: ordering = ('created',) 启动django服务器: python manage.py syncdb 4. 创建Serializer 第一步我们需要为我们的API提供序列化和反序列化的方法, 将snippet实例转为json等方式呈现数据. 我们可以使用Serializer达到这一目的, Serializer和django forms十分相似. 我们建立snippet/serializers.py文件: # snippet/serializers.py from django.forms import widgets from rest_framework import serializers from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES class SnippetSerializer(serializers.Serializer): pk = serializers.Field() # `Field` 是无类型, 只读的. title = serializers.CharField(required=False, max_length=100) code = serializers.CharField(widget=widgets.Textarea, max_length=100000) linenos = serializers.BooleanField(required=False) language = … -
Django-Rest-Framework 教程: 快速入门
本篇中, 我们会创建一个简单的API, 用来查看和编辑django默认的user和group数据. 1. 设置 我们创建django项目tutorial, 和app quickstart: # 创建新Django项目 django-admin.py startproject tutorial cd tutorial # 使用virtualenvwrapper创建Virtualenv mkvirtualenv env workon env # 在env中安装Django 和 Django REST framework pip install django pip install djangorestframework # 创建新app python manage.py startapp quickstart 然后根据自己的数据库配置设置数据库: # tutorial/settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'database_name', 'USER': 'database_user', 'PASSWORD': 'database_password', 'HOST': '', 'PORT': '' } } ... INSTALLED_APPS = ( ... 'quickstart', 'rest_framework', ) REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',), 'PAGINATE_BY': 10 } 最后通过syncdb创建数据库 python manage.py syncdb 2. 序列化 接下来我们创建用于数据序列化的代码: # 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', 'groups') class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group fields = ('url', 'name') 值得注意的是, 我们使用的是HyperlinkedModelSerializer. 你可以使用主键或者其他关系, 但使用HyperlinkedModelSerializer是一个好的 RESTful 设计. 3. Views # quickstart/views.py from django.contrib.auth.models import User, Group from rest_framework import viewsets from quickstart.serializers import UserSerializer, GroupSerializer class UserViewSet(viewsets.ModelViewSet): """ 允许查看和编辑user 的 API endpoint """ queryset = User.objects.all() serializer_class = UserSerializer class GroupViewSet(viewsets.ModelViewSet): """ 允许查看和编辑group的 API endpoint """ queryset = Group.objects.all() serializer_class = GroupSerializer 在django_rest_framework中, 所有常见的行为都被归到了ViewSets中. 当然我们可以将这些行为分拆出来, 但使用ViewSets, 使view的逻辑更为清楚. 使用queryset和serializer_class代替model变量, 使我们能更加好的控制API行为, 这也是我们推荐的使用方式. 4. URLs # tutorial/urls.py from django.conf.urls import patterns, url, include from rest_framework import routers from quickstart import views router = routers.DefaultRouter() router.register(r'users', … -
理解OAuth 2.0
转自: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html: OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。 本文对OAuth 2.0的设计思路和运行流程,做一个简明通俗的解释,主要参考材料为RFC 6749。 1. 应用场景 为了理解OAuth的适用场合,让我举一个假设的例子。 有一个"云冲印"的网站,可以将用户储存在Google的照片,冲印出来。用户为了使用该服务,必须让"云冲印"读取自己储存在Google上的照片。 问题是只有得到用户的授权,Google才会同意"云冲印"读取这些照片。那么,"云冲印"怎样获得用户的授权呢? 传统方法是,用户将自己的Google用户名和密码,告诉"云冲印",后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点。 (1)"云冲印"为了后续的服务,会保存用户的密码,这样很不安全。 (2)Google不得不部署密码登录,而我们知道,单纯的密码登录并不安全。 (3)"云冲印"拥有了获取用户储存在Google所有资料的权力,用户没法限制"云冲印"获得授权的范围和有效期。 (4)用户只有修改密码,才能收回赋予"云冲印"的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效。 (5)只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。 OAuth就是为了解决上面这些问题而诞生的。 2. 名词定义 在详细讲解OAuth 2.0之前,需要了解几个专用名词。它们对读懂后面的讲解,尤其是几张图,至关重要。 (1) Third-liarty alililication:第三方应用程序,本文中又称"客户端"(client),即上一节例子中的"云冲印"。 (2)HTTli service:HTTli服务提供商,本文中简称"服务提供商",即上一节例子中的Google。 (3)Resource Owner:资源所有者,本文中又称"用户"(user)。 (4)User Agent:用户代理,本文中就是指浏览器。 (5)Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。 (6)Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。 知道了上面这些名词,就不难理解,OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动。 3. OAuth的思路 OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。"客户端"不能直接登录"服务提供商",只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。 "客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。 4. 运行流程 OAuth 2.0的运行流程如下图,摘自RFC 6749。 (A)用户打开客户端以后,客户端要求用户给予授权。 (B)用户同意给予客户端授权。 (C)客户端使用上一步获得的授权,向认证服务器申请令牌。 (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。 (E)客户端使用令牌,向资源服务器申请获取资源。 (F)资源服务器确认令牌无误,同意向客户端开放资源。 不难看出来,上面六个步骤之中,B是关键,即用户怎样才能给于客户端授权。有了这个授权以后,客户端就可以获取令牌,进而凭令牌获取资源。 下面一一讲解客户端获取授权的四种模式。 5. 客户端的授权模式 客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。 授权码模式(authorization code) 简化模式(implicit) 密码模式(resource owner password credentials) 客户端模式(client credentials) 6. 授权码模式 授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。 它的步骤如下: (A)用户访问客户端,后者将前者导向认证服务器。 (B)用户选择是否给予客户端授权。 (C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。 (D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。 (E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。 下面是上面这些步骤所需要的参数。 A步骤中,客户端申请认证的URI,包含以下参数: response_type:表示授权类型,必选项,此处的值固定为"code" client_id:表示客户端的ID,必选项 redirect_uri:表示重定向URI,可选项 scope:表示申请的权限范围,可选项 state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。 下面是一个例子。 GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1 Host: server.example.com C步骤中,服务器回应客户端的URI,包含以下参数: code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。 state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。 下面是一个例子。 HTTP/1.1 302 Found Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA &state=xyz D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数: grant_type:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。 code:表示上一步获得的授权码,必选项。 redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。 client_id:表示客户端ID,必选项。 下面是一个例子。 POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb E步骤中,认证服务器发送的HTTP回复,包含以下参数: access_token:表示访问令牌,必选项。 token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。 expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。 refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。 scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。 下面是一个例子。 HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter":"example_value" } 从上面代码可以看到,相关参数使用JSON格式发送(Content-Type: application/json)。此外,HTTP头信息中明确指定不得缓存。 7. 简化模式 简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。 它的步骤如下: (A)客户端将用户导向认证服务器。 (B)用户决定是否给于客户端授权。 (C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。 (D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。 (E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。 (F)浏览器执行上一步获得的脚本,提取出令牌。 (G)浏览器将令牌发给客户端。 下面是上面这些步骤所需要的参数。 A步骤中,客户端发出的HTTP请求,包含以下参数: response_type:表示授权类型,此处的值固定为"token",必选项。 client_id:表示客户端的ID,必选项。 redirect_uri:表示重定向的URI,可选项。 scope:表示权限范围,可选项。 state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。 下面是一个例子。 GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1 Host: server.example.com C步骤中,认证服务器回应客户端的URI,包含以下参数: access_token:表示访问令牌,必选项。 token_type:表示令牌类型,该值大小写不敏感,必选项。 expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。 scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。 state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。 下面是一个例子。 HTTP/1.1 302 Found Location: http://example.com/cb #access_token=2YotnFZFEjr1zCsicMWpAA &state=xyz&token_type=example&expires_in=3600 在上面的例子中,认证服务器用HTTP头信息的Location栏,指定浏览器重定向的网址。注意,在这个网址的Hash部分包含了令牌。 根据上面的D步骤,下一步浏览器会访问Location指定的网址,但是Hash部分不会发送。接下来的E步骤,服务提供商的资源服务器发送过来的代码,会提取出Hash中的令牌。 8. 密码模式 密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。 在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。 … -
Installing Python and Django on Windows
For my own reference I just wanted to document my first time installing Python and Django on Windows 8.  I have installed Python before on a Mac, but never on Windows. To begin with I installed the Python 2.7 (the version Django is compatible with at this point in time). First, I downloaded and installed Python. Python has an ‘installer’ so this was straightforward. I would recommend the install location to be short and simple c:/python27. It's a lot easier on your command prompt typing later on. Python is not added to the DOS path by default. I needed to tell windows the PATH environment variable to include paths to Python executable and additional scripts.  Under My Computer > Properties > Advanced System Settings > Environment Variables > I created a new variable called "PythonPath” and added the following… C:\Python27\;C:\Python27\Scripts; Above being my location to the Python executable. Now that I had Python installed, it was time to install Django. I download the Django files then opened the command prompt. Using the Command Prompt I changed directory (“cd”) into the extracted Django directory. I then typed the following... python setup.py install This installs Django into Python’s site-packages directory. The download … -
Resetting migration history Django South
Just something useful when it comes to resetting migration history using Django South. rm -r appname/migrations/ ./manage.py reset south ./manage.py convert_to_south appname I had to do this recently and I was somewhat apprehensive at first. No need to be tho. I just opened up terminal removed the migration folder from the app , used the reset command and then converted the app just like it was an existing one. Doing this does mean that South assumes your database is identical to the app models.py. -
Fix: vertualenv EnvironmentError: mysql_config not found
Have you ever had what should be a simple task just drive you insane? For me, it's setting up a new Django project using virtualenv on my home Mac running 10.8! For some reason this Mac likes to misbehave! I get the same error every time I try to install MySQL-python within virtualenv vertualenv EnvironmentError: mysql_config not found I cannot take any credit for this fix, after many searches I found the solution here. It's a simple fix just to edit the /bin/activate file from the virtualenv directory and add the following lines, _OLD_VIRTUAL_PATH="$PATH" PATH="$VIRTUAL_ENV/bin:$PATH" PATH="$PATH:/usr/local/mysql/bin/" export PATH That's it, just make sure you match your paths to your own configuration. Now MySQL-python installs under virtualenv as normal. -
Django 1.5 deployment on Amazon EC2 Ubuntu
Introduction Six months ago I began my Django journey. Since then I have setup, installed and utilised many different deployment mechanisms for projects. This tutorial demonstrates the installation and deployment solution I found to be the most fitting for my own Django projects. So, I’d like to share… Why Git? I’ve been using Git for deployment within our company for sometime. It was a natural fit since all our projects already used GIT for version control and our development team understands it. What’s best, it's completely free! Prerequisites One will assume you are already familiar, perhaps even using GIT and have a basic understanding of Apache, Ubuntu Server and Django. I tend to use Cloud computing such as Amazon AWS. I have tested this tutorial on a EC2 instance running Ubuntu 12. Installing our Server Software So as mentioned above I’ll be using an EC2 instance for this writeup. So first ssh in and do any updates. apt-get update apt-get upgrade Next, you will need to install Apache2, Python, and PIP using the following commands... apt-get -y install apache2-mpm-worker apache2-dev apt-get -y install python python-dev python-setuptools sudo apt-get install python-pip pip install --upgrade pip We will wget the latest mod_wsgi, … -
RESTful API 设计指南
转自http://www.ruanyifeng.com/blog/2014/05/restful_api.html 网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备......)。 因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现"API First"的设计思想。RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。我以前写过一篇《理解RESTful架构》,探讨如何理解这个概念。 今天,我将介绍RESTful API的设计细节,探讨如何设计一套合理、好用的API。我的主要参考资料是这篇《Principles of good RESTful API Design》。 1. 协议 API与用户的通信协议,总是使用HTTPs协议。 2. 域名 应该尽量将API部署在专用域名之下。 https://api.example.com 如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。 https://example.org/api/ 3. 版本(Versioning) 应该将API的版本号放入URL。 https://api.example.com/v1/ 另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。 4. 路径(Endpoint) 路径又称"终点"(endpoint),表示API的具体网址。 在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。 举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。 https://api.example.com/v1/zoos https://api.example.com/v1/animals https://api.example.com/v1/employees 5. HTTP动词 对于资源的具体操作类型,由HTTP动词表示。 常用的HTTP动词有下面五个(括号里是对应的SQL命令)。 GET(SELECT):从服务器取出资源(一项或多项)。 POST(CREATE):在服务器新建一个资源。 PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。 PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。 DELETE(DELETE):从服务器删除资源。 还有两个不常用的HTTP动词。 HEAD:获取资源的元数据。 OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。 下面是一些例子。 GET /zoos:列出所有动物园 POST /zoos:新建一个动物园 GET /zoos/ID:获取某个指定动物园的信息 PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息) PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息) DELETE /zoos/ID:删除某个动物园 GET /zoos/ID/animals:列出某个指定动物园的所有动物 DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物 6. 过滤信息(Filtering) 如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。下面是一些常见的参数。 ?limit=10:指定返回记录的数量 ?offset=10:指定返回记录的开始位置。 ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。 ?animal_type_id=1:指定筛选条件 参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。 7. 状态码(Status Codes) 服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 204 NO CONTENT - [DELETE]:用户删除数据成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。。 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 状态码的完全列表参见这里。 8. 错误处理(Error handling) 如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。 { error: "Invalid API key" } 9. 返回结果 针对不同操作,服务器向用户返回的结果应该符合以下规范。 GET /collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档 10. Hypermedia API RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。 {"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }} 上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。 Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。 { "current_user_url": "https://api.github.com/user", "authorizations_url": "https://api.github.com/authorizations", // ... } 从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。 { "message": "Requires authentication", "documentation_url": "https://developer.github.com/v3" } 上面代码表示,服务器给出了提示信息,以及文档的网址。 11. 其他 (1)API的身份认证应该使用OAuth 2.0框架。 (2)服务器返回的数据格式,应该尽量使用JSON,避免使用XML -
Packaging a python library
Note This is about packaging libraries, not applications. For now there are no special considerations for C extensions here. I think the packaging best practices should be revisited, there are lots of good tools now-days that are either unused or underused. It's generally a good thing to re-evaluate best practices all the time. I assume here that your package is to be tested on multiple Python versions, with different combinations of dependency versions, settings etc. And few principles that I like to follow when packaging: If there's a tool that can help with testing use it. Don't waste time building a custom test runner if you can just use nose or py.test. They come with a large ecosystem of plugins that can improve your testing. When possible, prevent issues early. This is mostly a matter of strictness and exhaustive testing. Design things to prevent common mistakes. Collect all the coverage data. Record it. Identify regressions. Test all the possible configurations. The structure* This is fairly important, everything revolves around this. People layout packages like this: ├─ packagename │ ├─ __init__.py │ ├─ ... │ └─ tests │ └─ ... └─ setup.py I think this is actually a bad practice, a … -
Packaging a python library
Note This is about packaging libraries, not applications. ⸻ All the advice here is implemented in a project template (with full support for C extensions): cookiecutter-pylibrary (introduction). I think the packaging best practices should be revisited, there are lots of good tools now-days that are either unused or underused. It's generally a good thing to re-evaluate best practices all the time. I assume here that your package is to be tested on multiple Python versions, with different combinations of dependency versions, settings etc. And few principles that I like to follow when packaging: If there's a tool that can help with testing use it. Don't waste time building a custom test runner if you can just use py.test or nose. They come with a large ecosystem of plugins that can improve your testing. When possible, prevent issues early. This is mostly a matter of strictness and exhaustive testing. Design things to prevent common mistakes. Collect all the coverage data. Record it. Identify regressions. Test all the possible configurations. The structure * This is fairly important, everything revolves around this. I prefer this sort of layout: ├─ src │ └─ packagename │ ├─ __init__.py │ └─ ... ├─ tests │ └─ … -
Packaging a python library
Note This is about packaging libraries, not applications. ⸻ All the advice here is implemented in a project template (with full support for C extensions): cookiecutter-pylibrary (introduction). I think the packaging best practices should be revisited, there are lots of good tools now-days that are either unused or underused. It's generally a good thing to re-evaluate best practices all the time. I assume here that your package is to be tested on multiple Python versions, with different combinations of dependency versions, settings etc. And few principles that I like to follow when packaging: If there's a tool that can help with testing use it. Don't waste time building a custom test runner if you can just use py.test or nose. They come with a large ecosystem of plugins that can improve your testing. When possible, prevent issues early. This is mostly a matter of strictness and exhaustive testing. Design things to prevent common mistakes. Collect all the coverage data. Record it. Identify regressions. Test all the possible configurations. The structure * This is fairly important, everything revolves around this. I prefer this sort of layout: ├─ src │ └─ packagename │ ├─ __init__.py │ └─ ... ├─ tests │ └─ … -
Django 1.6 最佳实践: 如何正确使用Templates模板(二)
接着上一篇博文, 我们继续讲django中templates的使用最佳实践: 4. Templates的格式 尽管在浏览器中呈现或查看HTML时, 浏览器都会为你整齐排列好, 但我们在写template代码时, 还是应该尽量保持良好的格式. 原因就像之前的文章中提到的一样, 代码的可读性对于日后的维护和修改十分重要. 在写代码时, 应当随时注意代码的缩进, 空行和分段, 当然也可以使用编辑器或IDE的自动格式化来达到这一目的. 5. Templates中block.super的使用 在templates中除了使用常见的block, extends, include等tag组织template结构外, 还有一个非常好用的tag: {% block.super %}, block.super可以引入之前block的内容, 假设原始的base.html: {# templates/base.html #} ... {% block stylesheets %} <link rel="stylesheet" type="text/css" href="css/default.css" /> {% end block %} 如果我们希望继承自base.html的stylesheets block既保留default.css, 又加入project.css: {% extends "base.html" %} {% block stylesheets %} {% block.super %} <link rel="stylesheet" type="text/css" href="css/default.css" /> {% end block %} 如果希望完全代替原有block的内容则: {% extends "base.html" %} {% block stylesheets %} <link rel="stylesheet" type="text/css" href="css/default.css" /> {% end block %} 如果不使用block stylesheets, 则继承自base.html的template保留改block: 6. 其他需要注意的点 第一, 不要将样式文件与python代码捆绑到一起, 这点应该很容易理解, 尽量将这些文件归到css或JavaScript中. 第二, 一些命名规定: 尽量使用下划线(_)代替横杠(-), 大多数python开发人员都喜欢这样, 原因的话, 可能是下划线可以存在于python变量名中吧. block名尽量使用简明, 易懂的形式 在endblock中同样加入block名例如{% endblock javascript%} 被引用(include)的模板使用"_"开头 第三, templates应放入django_project_root中, 这样是最直观的做法. 但如果你的app是作为第三方插件的话, 那么templates就可以放入app文件夹中. 第四, 简单命名context: 在使用CBVs时, context都有默认的名字object_list或object, 与此同时, 我们还应当给出一个有意义, 简明的名字, 方便调用. 第五, 不要手写URL地址到template中, 而是应当使用{% url "url_name" %}的方式获得URL地址. 第六, debug template: 当templates很复杂时, 可以在settings.py中设置TEMPLATE_STRING_IF_INVALID = "INVALID EXPRESSION: %s", 这样当有变量没有呈现时, 便会在template原有位置显示INVALID EXPRESSION: "变量名", 是的debug变得较容易. 第七, 不到替换掉django的template引擎: 你可以在个别view中使用Jinjia2等模板引擎, 但最好不要将整个django templates引擎替换掉. 7. HTML错误页 即使是最稳定的网站或应用也有错误的时候, 这没有关系, 问题在于你如何处理这些错误. 但是将黄色的报错页直接显示给用户就不是一个好的做法了. 我们至少应该在templates中放置404.html和500.html页面. 我们建议使用文件服务器呈现这些页, 因为这样的话, 当你的Django程序出问题时, 文件服务器还能独立运行, 继续显示这些页面. 如果你使用PAAS的话, 请参照PAAS的说明文件. 在github上有很好的例子: https://github.com/404 https://github.com/500 你可以发现: 所有的CSS样式文件都是inline形式的, 不需要借助其他服务器或文件帮助 所有的图像都是以数据形式存在HTML中, 没有<img>标签或其他外部URL 所有的JavaScript代码都是包含在HTML中, 不使用外部的链接加在JavaScript -
Django 1.6 最佳实践: 如何正确使用Templates模板(一)
Django设计之初的基本原则之一是限制template的功能. 这一做法极大地限制了Django templates的复杂性, 也迫使我们将业务逻辑置于Python代码这边. 1. 最简原则 我们推荐在Django templates中使用最简原则. 尊重django template中的功能限制, 并将业务逻辑使用更优化的Python代码代替之. 在Django templates中使用最简原则, 可以是我们的django app更便于再利用. 试想, 当一个template中充满了大段繁琐的内嵌循环, 复杂逻辑和数据处理代码时, 如何能重复利用其中的业务逻辑? 更不要说如何在其他代码中利用这些业务逻辑了. 2. Templates结构的2种常见模式 在template结构中比较常见的是2种模式, 一种是2层结构, 即所有的其他页面都继承自同一base.html文件: templates/ base.html index.html # extends base.html articles/ article_detail.html # extends base.html article.form.html #extends base.html 还有一种则是3层结构. 这一结构中, 整个项目拥有一个基础的base.html文件, 而每一个app都拥有自己的base _<app>.html, app中其他页面则都继承自该app的base_<app>.html文件: templates/ base.html index.html # extends base.html articles/ base_articles.html # extends base.html article_detail.html # extends base_articles.html article_form.html # extends base_articles.html 三层结构的templates, 对于每一部分都需要不同布局的网站而言最为适合. 例如一个新闻网站, 其中本地新闻, 体育新闻, 和其他分类的新闻页面都采用不同样式的布局, 则可以采用三层结构的templates. 需要注意的是, 在templates中, 扁平比嵌套好. 复杂的template结构使得debug, 修改和扩展HTML, 加入CSS文件等异常艰辛. 如果使用扁平简单的template, 和你合作的前台设计师一定会十分感激你的. 3. 在template中限制处理量 在template中应当尽量减少处理的数据良, 因此最好每次循环一个queryset时, 你都应该问自己: 这个queryset有多大? 循环超过GB级的queryset永远是个错误 从数据库中查询的objects有多大? 需要的fields是否都包含在了其中? 每次循环中, 有多少处理步骤发生? 如果发现以上三个问题中有一个不符合最简原则, 那么可能你就应该重写这个template了. 以下我们以一个收据(voucher)model为例子, 详细说明一下常见的一些错误: # vouchers/models.py from django.core.urlresolvers import reverse from django.db import models from .managers import VoucherManager class Voucher(models.Model): name = models.CharField(max_length=100) email = models.EmailField() address = models.TextField() birth_date = models.DateField(blank=True) sent = models.BooleanField(default=False) redeemed = models.BooleanField(default=False) objects = VoucherManager() 错误1: 在templates中使用聚合函数 由于我们的收据model中有生日信息, 我们可能希望按照年龄段聚合显示收据的数量. 但是我们不应当在template中使用这些聚合处理, 因为这样会大大拖累页面显示的速度, 具体而言: 不应该在template的JavaScript中循环整个收据list 不应该在template中使用add来累加收据的数量 正确的做法应当将这一聚合过程放到python代码中, 然后只在template中呈现这些数据: {# templates/vouchers/ages.html #} {% extends "base.html" %} {% block content %} <table> <thead> <tr> <th>年龄层</th> <th>收据数量</th> </tr> </thead> <tbody> {% for age_bracket in age_brackets %} <tr> <td>{{ age_bracket.title }}</td> <td>{{ age_bracket.count }}</td> </tr> {% endfor %} </tbody> </table> {% end block content%} 数据聚合处理放入model manager中, 然后通过views.py调用并传到template中: # vouchers/managers.py from dateutil.relativedelta import relativedelta from django.utils import timezone from django.db import … -
Starting with Stripe.js
We all want to make money, so learn what it takes to start accepting money on your site with stripe. Learn the basics of have a user subscribe to your site in this first part of a multi-part stripe series.Watch Now... -
Django 1.6 session 序列化的安全问题
从Django 1.6开始默认的session序列化方法发生了变化,在1.5及之前版本的Django默认使用的 […] -
The State of Tastypie
The State of Tastypie -
Release 0.7.8
We just released LFS 0.7.8. This is a yet another bugfix release of the 0.7 branch. Changes Bugfix: excludes variants of inactive parents Bugfix: fixes issue #13 in lfs_theme Bugfix: fixes removing of products on manufacturer delete Bugfix: adds field 'link' to action add form; updates docs; issue #51. Information You can find more information and help on following locations: Documentation on PyPI Demo Releases on PyPI Source code on bitbucket.org and github. Google Group lfsproject on Twitter IRC -
Django 1.6 最佳实践: Django Forms 的其他使用技巧
Django forms确实强大, 但在使用过程中, 你也许会觉得有些地方使用起来很困惑. 不过, 如果你了解forms的结构的, 那么这些困惑就能立刻变得清晰: 1. 何时使用POST方法 如果HTML form中提交的是需要修改数据内容的动作, 那么就应当使用POST, 唯一的例外是search form, 因为提交的搜索并不会修改数据: <form action="/article/add/" method="POST"> 还有需要注意的是, 不要关闭django 的CSRF保护, 详细可以查看https://docs.djangoproject.com/en/1.6/ref/contrib/csrf/ 2. Django Forms的验证过程 上一篇中, 我们介绍了怎么自定义forms的验证. 进一步理解Forms验证的过程, 可能会帮助你编写更好的代码. 接下来让我们看一下forms的验证过程. 当执行form.is_valid()时, 以下步骤会一步接着一步发生: 1.如果form已捆绑数据(bound data), 那么form.is_valid()会调用form.full_clean()方法: 2.form.full_clean()将依次验证每个field: a.通过to_python()方法将field中数据转化为对应python数据类型 b.针对不同field类型, 进行field类型验证和自定义validator验证 c.通过自定义的clean<field>()方法验证 3.执行form.clean()方法 4.如果是ModelForm, 则调用form.post_clean()方法: a.无论form.is_valid()返回是True或False, 将form的数据传到对应的model实例中 b.调用model的clean()方法 (使用ORM保存model时, 并不会调用model的clean()方法) 我们可以看出, form的数据先保存到from实例中, 如果是ModelForm, 则再保存到model实例中. 因为在没有使用form.save()之前, model实例是不会被写入数据库的. 这样, 我们就有机会记录并保存用户的错误输入: # utils/models.py from django.db import models class FailureHistory(models.Model): form_data = models.TextField() model_data = models.TextField() # myapp/models.py import json from django.core import serializers from django.views.generic import CreateView from utils.models import FailureHistory class ArticleCreateView(CreateView): ... def form_invalid(self, form): form_data = json.dumps(form.cleaned_data) model_data = serializers.serialize("json", [form.instance])[1:-1] FailureHistory.objects.create( form_data = form_data, model_data = model_data ) return super(ArticleCreateView, self).form_invalid(form) -
Django 1.6 最佳实践: 如何正确使用 Django Forms
Django forms使用容易, 又方便扩展, 因此Django admin和CBVs基本都基于forms使用. 事实上, 由于django forms的强大验证功能, 大多数Django API 框架都是用forms作为其验证的一部分. 虽然django forms的学习需要花费一点时间, 但如果将forms, models和views结合起来使用, 我们可以花费很少的经历来完成庞大的工作. 1. Django Forms的强大之处 有些django项目并不直接呈现HTML, 二是以API框架的形式存在, 但你可能没有想到, 在这些API形式的django项目中也用到了django forms. django forms不仅仅是用来呈现HTML的, 他们最强的地方应该是他们的验证能力. 下面我们就介绍几种和Django forms结合使用的模式: 2. 模式一: ModelForm和默认验证 最简单的使用模式便是ModelForm和model中定义的默认验证方式的组合: # myapp/views.py from django.views.generic import CreateView, UpdateView from braces.views import LoginRequiredMixin from .models import Article class ArticleCreateView(LoginRequiredMixin, CreateView): model = Article fields = ('title', 'slug', 'review_num') class ArticleUpdateView(LoginRequiredMixin, UpdateView): model = Article fields = ('title', 'slug', 'review_num') 正如以上代码中看到的一样: ArticleCreateView和ArticleUpdateView中设置model为Article 两个view都基于Article model自动生成了ModelForm 这些ModelForm的验证, 是基于Article model中定义的field转换而来的 3. 模式二, 在ModelForm中修改验证 在上面的例子中, 如果我们希望每篇article title的开头都是"new", 那么应该怎么做呢? 首先我们需要建立自定义的验证(validator): # utils/validator.py from django.core.exceptions import ValidationError def validate_begins(value): if not value.startswith(u'new'): raise ValidationError(u'Must start with new') 可见, 在django中的验证程序就是不符合条件便抛出ValidationError的function, 为了方便重复使用, 我们将它们放在django app utils的validators.py中. 接下来, 我们可以在model中加入这些validator, 但为了今后的方便修改和维护, 我们更倾向于加入到ModelForm中: # myapp/forms.py from django import forms from utils.validators import validate_begin from .models import Article class ArticleForm(forms.ModelForm): dev __init__(self, *args, **kwargs): super(ArticleForm, self).__init__(8args, **kwargs) self.fields["title"].validators.append(validate_begin) class Meta: model = Article Django的edit views(UpdateView和CreateView等)的默认行为是根据view中model属性, 自动创建ModelForm. 因此, 我们需要调用我们自己的Modelform来覆盖自动创建的: # myapp/views.py from django.views.generic import CreateView, UpdateView from braces.views import LoginRequiredMixin from .models import Article from .forms import ArticleForm class ArticleCreateView(LoginRequiredMixin, CreateView): model = Article fields = ('title', 'slug', 'review_num') form_class = ArticleForm class ArticleUpdateView(LoginRequiredMixin, UpdateView): model = Article fields = ('title', 'slug', 'review_num') form_class = ArticleForm 4. 模式三, 使用form的clean()和clean_<field>&()方法 如果我们希望验证form中的多个field, 或者验证涉及到已经存在之后的数据, 那么我们就需要用到form的clean()和clean_<field>&()方法了. 以下代码检查密码长度是否大于7位, 并且password是否和password2相同: … -
Newsletter #3
Newsletter #3, May 21st, 2014 Huge Success at the DjangoCon Europe 2014 Raffle We provide in-kind sponsorship for conferences. What that means is that in return for conferences listing us in their sponsors, we send them a signed copy of Two Scoops of Django 1.6 and a signed art print by Audrey Roy. We encourage conferences to raffle them off. This way our book is used as a means for conferences to provide financial assistance for developers who otherwise could not afford to attend their event. This is exactly what DjangoCon Europe did. They raffled off the book, which earned DjangoCon Europe 2015 a whopping €700, or about $950 US. Needless to say, we're amazed and astounded. As two people who have both attended conferences thanks to the assistance of others, we're delighted that our work is being used in this manner. Sponsoring More Conferences! Here are upcoming conferences that we are sponsoring. In alphabetical order: Kiwi PyCon PyCon Australia PyCon India If your conference is interested in us sponsoring your event, please take a look at our in-kind sponsorship program. Django Packages Service on Github We added a service for Django Packages on Github. If you maintain an open-source … -
Newsletter #3
Newsletter #3, May 21st, 2014 Huge Success at the DjangoCon Europe 2014 Raffle We provide in-kind sponsorship for conferences. What that means is that in return for conferences listing us in their sponsors, we send them a signed copy of Two Scoops of Django 1.6 and a signed art print by Audrey Roy. We encourage conferences to raffle them off. This way our book is used as a means for conferences to provide financial assistance for developers who otherwise could not afford to attend their event. This is exactly what DjangoCon Europe did. They raffled off the book, which earned DjangoCon Europe 2015 a whopping €700, or about $950 US. Needless to say, we're amazed and astounded. As two people who have both attended conferences thanks to the assistance of others, we're delighted that our work is being used in this manner. Sponsoring More Conferences! Here are upcoming conferences that we are sponsoring. In alphabetical order: Kiwi PyCon PyCon Australia PyCon India If your conference is interested in us sponsoring your event, please take a look at our in-kind sponsorship program. Django Packages Service on Github We added a service for Django Packages on Github. If you maintain an open-source … -
Newsletter #3
Newsletter #3, May 21st, 2014 Huge Success at the DjangoCon Europe 2014 Raffle We provide in-kind sponsorship for conferences. What that means is that in return for conferences listing us in their sponsors, we send them a signed copy of Two Scoops of Django 1.6 and a signed art print by Audrey Roy. We encourage conferences to raffle them off. This way our book is used as a means for conferences to provide financial assistance for developers who otherwise could not afford to attend their event. This is exactly what DjangoCon Europe did. They raffled off the book, which earned DjangoCon Europe 2015 a whopping €700, or about $950 US. Needless to say, we're amazed and astounded. As two people who have both attended conferences thanks to the assistance of others, we're delighted that our work is being used in this manner. Sponsoring More Conferences! Here are upcoming conferences that we are sponsoring. In alphabetical order: Kiwi PyCon PyCon Australia PyCon India If your conference is interested in us sponsoring your event, please take a look at our in-kind sponsorship program. Django Packages Service on Github We added a service for Django Packages on Github. If you maintain an open-source … -
Django 1.6 最佳实践: 如何正确使用 CBVs (Class-based views)
Class-based views是Django为解决建站过程中的常见的呈现模式而建立的. 在这节中, 我们着重讲一下CBVs的使用技巧和一般原则. 1. CBVs的使用原则 代码越少越好 永远不要重复代码 View应当只包含呈现逻辑, 不应包括业务逻辑 保持view逻辑清晰简单 不要将CBVs用作403, 404, 500的错误处理程序 保持mixin简单明了 2. 如何使用mixin 在编程中mixin是指为继承它的class提供额外的功能, 但它自身却不能单独使用的类. 在具有多继承能力的编程语言中, mixin可以为类增加额外功能或方法. 在Django中, 我们可以使用mixin为CBVs提供更多的扩展性, 当然在类继承过程中, 我们推荐以下原则: Django自身提供的View永远在最右边 mixin依次在以上view的左边 mixin永远继承自Python的object类型 在这里顺便推荐一个很好的django库: django-braces. 该库中提供众多django的mixin, 可以方便我们日常使用. 以下是一个简单地例子, TemplateView是django自身提供的基本View, 因此在最右边; FreshFruitMixin则在TemplateView左边; FreshFruitmixin继承自object: from django.views.generic import TemplateView class FreshFruitMixin(object): def get_context_data(self, **kwargs): context = super(FreshFruitMixin, self).get_context_data(**kwargs) context["has_fresh_fruit"] = True return context class FruitFlavorView(FreshFruitMixin, TemplateView): template_name = "fruit_flavor.html" 3. 如何使用Django自身的CBV CBVs在功能上的可扩展性, 牺牲的是简单性, 一个CBV最多的时候拥有8个import关系. (如果希望进一步了解这些继承关系, 可以使用Classy Class-Based Views进行查看.) 所以要弄懂那个View最适合当下的场景对于开发人员也是一个挑战. 为了减少CBVs的使用难度, 我们将这些View和基本的用法列在下表中, 为了显示方便, 名字前的django.views.generic前缀皆省去: 名字目的例子 View 基本View, 可以在任何时候使用 见后面详细介绍 RedirectView 重新定向到其他URL 将访问"/log-in/"的用户重新定向到"/login/" TemplateView 显示Django HTML template 一般网站中使用模板显示的页 ListView 显示对象列表 文章列表页 DetailView 显示对象详情 文章详细页 FormView 提交From 网站联系我们或emai订阅form CreateView 创建对象 创建新文章页 UpdateView 更新对象 修改文章页 DeleteView 删除对象 删除文章页 Generic date views 显示一段时间内的对象 按时间归类的博客 4. CBVs的使用技巧 a. 限定访问权限 在django tutorial中介绍了如何一起使用django.contrib.auth.decorators.login_required和CBV, 这是一个典型的错误例子. 还好, 我们有django-braces. 在django-braces中已经提供了一个LoginRequiredMixin: # myapp/views.py from django.views.generic import DetailView from braces.views import LoginRequiredMixin from .models import Article class ArticleDetailView(LoginRequiredMixin, DetailView): model = Article b. 在form提交成功后执行代码 当需要在form提交成功后执行自定义的代码时, 可以使用form_valid()方法, form_valid()方法返回的是django.http.HttpResponseRedirect: # myapp/views.py from django.views.generic import CreateView from braces.views import LoginRequiredMixin from .models import Article class ArticleCreateView(LoginRequiredMixin, CreateView): model = Article field = ('title', 'slug', 'content') def form_valid(self, form): # 自定义的代码逻辑写在这里 return super(ArticleCreateView, self).form_valid(form) c. 在form提交不成功后执行代码 当需要在form提交不成功后执行自定义的代码时, 可以使用form_invalid()方法, form_invalid()方法返回的也是django.http.HttpResponseRedirect: # myapp/views.py from django.views.generic import CreateView from braces.views import LoginRequiredMixin from .models import Article class ArticleCreateView(LoginRequiredMixin, … -
A Different View (part 2)
In the previous post, we saw how we could use a single, raw query in Django to combat ORMs’ tendency to generate query explosions within loops. We used raw() with joins and some column renaming to ensure all the data we needed came back in one go. We had to modify the property names in the template slightly (e.g. book.jacket.image became book.jacket_image) and the result was a RawQuerySet which had a fair number of limitations but, we avoided the typical order-of-magnitude increase in query count. Super models There is a way we can use a raw query, to get all the benefits described in the previous post, and return a real QuerySet. We need a Django model and so need an underlying table. A much underused feature of SQL databases is the view. Views are virtual tables derived from queries. Most are currently implemented as read-only, but they provide many benefits – again another blog post would be needed to explain them all – but they include: adding a level of abstraction, effectively an API (which is also available to other applications) protecting the application from underlying table and column changes giving fine-grained control of permissions, especially row-level providing a …