Django

django 单元测试

和普通的单元测试不同的是,django 单独提供了一个测试模块,所有的 TestCase 需要继承 django.test.TestCase

简单的测试

from django.test import TestCase
from myapp.models import Animal

class AnimalTestCase(TestCase):
    def setUp(self):
        Animal.objects.create(name="lion", sound="roar")
        Animal.objects.create(name="cat", sound="meow")

    def test_animals_can_speak(self):
        """Animals that can speak are correctly identified"""
        lion = Animal.objects.get(name="lion")
        cat = Animal.objects.get(name="cat")
        self.assertEqual(lion.speak(), 'The lion says "roar"')
        self.assertEqual(cat.speak(), 'The cat says "meow"')

对于需要测试服务器的测试用例,可以使用 django.test.Client

from django.test import TestCase

class SimpleTest(TestCase):
    def test_details(self):
        response = self.client.get('/customer/details/')
        self.assertEqual(response.status_code, 200)

    def test_index(self):
        response = self.client.get('/customer/index/')
        self.assertEqual(response.status_code, 200)

django 国际化

settings.py 中的设置:

“`
MIDDLEWARE_CLASSES = (

‘django.middleware.locale.LocaleMiddleware’,
)

LANGUAGE_CODE = ‘en’
TIME_ZONE = ‘UTC’
USE_I18N = True
USE_L10N = True
USE_TZ = True

LANGUAGES = (
(‘en’, (‘English’)),
(‘zh-hans’, (‘中文简体’)),
(‘zh-hant’, (‘中文繁體’)),
)

#翻译文件所在目录,需要手工创建
LOCALE_PATHS = (
os.path.join(BASE_DIR, ‘locale’),
)

TEMPLATE_CONTEXT_PROCESSORS = (

“django.core.context_processors.i18n”,
)
“`

生成需要翻译的文件:

“`
python manage.py makemessages -l zh_hans
python manage.py makemessages -l zh_hant
“`

翻译其中的 django.po 文件,注意`.po`文件是一种通用的格式,有很多专门的编辑器

编译翻译好的文件

“`
python manage.py compilemessages
“`

django 页面缓存

django 作为一个动态的网站系统,在并发访问量大的时候会遇到性能问题,这时候可以使用缓存来显著提高性能。

settings.py 中的配置

可以使用 django-redis 来使用redis作为缓存。

“`
pip install django-redis
“`

“`
CACHES = {
“default”: {
“BACKEND”: “django_redis.cache.RedisCache”,
“LOCATION”: “redis://127.0.0.1:6379/1”,
“OPTIONS”: {
“CLIENT_CLASS”: “django_redis.client.DefaultClient”,
}
}
}
“`

配置需要缓存的函数

“`
from django.views.decorators.cache import cache_page

@cache_page(60 * 15) # 秒数,这里指缓存 15 分钟,不直接写900是为了提高可读性
def index(request):
# 读取数据库等 并渲染到网页
return render(request, ‘index.html’, {‘queryset’:queryset})
“`

django 静态文件

# settings.py 中的相关配置

“`
STATIC_URL = ‘/static/’
STATIC_ROOT = os.path.join(BASE_DIR,’static’)
“`

一般来说我们只要把静态文件放在 APP 中的 static 目录下,部署时用 `python manage.py collectstatic` 就可以把静态文件收集到(复制到) STATIC_ROOT 目录,但是有时我们有一些共用的静态文件,这时候可以设置 STATICFILES_DIRS 另外弄一个文件夹,如下:

“`
STATICFILES_DIRS = (
os.path.join(BASE_DIR, “common_static”),
‘/var/www/static/’,
)
“`

这样我们就可以把静态文件放在 common_static 和 /var/www/static/中了,Django也能找到它们。

“`
MEDIA_URL = ‘/media/’
MEDIA_ROOT = os.path.join(BASE_DIR,’media’)
“`

media文件夹用来存放用户上传的文件

# nginx 部署时的配置

“`
location /media {
alias /path/to/project/media;
}

location /static {
alias /path/to/project/collected_static;
}
“`

# 在模板中引入静态文件

“`
{% load static %}
My image
“`

Django views 视图

django 使用正则指定路径,然后使用一个函数来处理对应的请求。

# 定义响应函数

响应函数如下:

“`
# views.py

from django.shortcuts import render
from django.http import HttpResponse

def add(request):
a = request.GET[‘a’]
b = request.GET[‘b’]
c = int(a) + int(b)
return HttpResponse(str(c))

def add2(request, a, b):
c = int(a) + int(b)
return HttpResponse(str(c))
“`

注意每个函数都需要接受 request 作为第一个参数,GET参数和POST参数都可以从 request 中读取。另外还可以使用从 url path 中读取数据,这些参数作为形参传递给对应的函数。

# 定义 URL 路由

“`
# urls.py

from calc import views as calc_views

urlpatterns = [
path(‘add/’, calc_views.add, name=’add’), # new
path(‘admin/’, admin.site.urls),
path(‘add///’, calc_views.add2, name=’add2′), # django 2.0 的新语法,以前都是用正则分组
]
“`

其中的 name 可以用在模板中,这样就不用写死 url 了。`link`

## 设定响应的 headers

response 对象可以当做字典使用,向其中复制就可以设定响应的头部

“`
from django.http import HttpResponse

def add(request):
a = request.GET[‘a’]
b = request.GET[‘b’]
c = int(a) + int(b)
response = HttpResponse(str(c))
response[‘Powered-By’] = ‘django’
return response
“`

## url reverse

在 urls.py 中可以设定 url 到具体函数的映射,但是 url 也是经常要随业务改动的,比如从 `add/` 变成了 `plus/`。当我们在某个网页中需要链接到某个页面的时候,不希望写死 url,这时候可以使用 url reverse 的功能,使用 name 反向获取 url。

“`
# urls.py
path(‘add///’, calc_views.add2, name=’add2′)

# other.py
from django.urls import reverse
url = reverse(‘add2’)
“`

django forms

django 中的 form 和 model 的用法很像,都是定义一个类,然后指定一些字段就可以了

最简单的form

“`
from django import forms

class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
email = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)

def clean_message(self):
message = self.cleaned_data[‘message’]
num_words = len(message.split())
if num_words < 4: raise forms.ValidationError("Not enough words!") return message ``` ``` def contact(request): if request.method == 'POST': form = ContactForm(request.POST) if form.is_valid(): cd = form.cleaned_data send_mail( cd['subject'], cd['message'], cd.get('email', 'noreply@example.com'), ['siteowner@example.com'], ) return HttpResponseRedirect('/contact/thanks/') else: form = ContactForm() return render(request, 'contact_form.html', {'form': form}) ``` ```

{{ form.as_table }}

{% csrf_token %}

“`

方法 | 用法
——|——
form.__str__() | return table representation
form.as_p() | return p representation
form.as_li() | return li representation
form.__getitem__() | return element tag
form.__init__(dict) | fill values
form.is_bound |
form.is_valid() |
form.cleaned_data |

Note not include table/ul/form tags, just the inside tags

# ajax

## ajax 中如何指定 crsf token

axios 中:

import axios from ‘axios’;
axios.defaults.xsrfHeaderName = “X-CSRFTOKEN”;
axios.defaults.xsrfCookieName = “csrftoken”;

settings.py 中

CSRF_COOKIE_NAME = “csrftoken”

[参考](https://stackoverflow.com/questions/39254562/csrf-with-django-reactredux-using-axios)

django dump to csv

“`
import csv
from django.db.models.loading import get_model

def dump(qs, outfile_path):
“””
Takes in a Django queryset and spits out a CSV file.

Usage::

>> from utils import dump2csv
>> from dummy_app.models import *
>> qs = DummyModel.objects.all()
>> dump2csv.dump(qs, ‘./data/dump.csv’)

Based on a snippet by zbyte64::

http://www.djangosnippets.org/snippets/790/
“””
model = qs.model
writer = csv.writer(open(outfile_path, ‘w’))

headers = []
for field in model._meta.fields:
headers.append(field.name)
writer.writerow(headers)

for obj in qs:
row = []
for field in headers:
val = getattr(obj, field)
if callable(val):
val = val()
if type(val) == unicode:
val = val.encode(“utf-8”)
row.append(val)
writer.writerow(row)
“`

django auth and user

# 激活

django 自带的 auth 模块需要收先创建数据库才能够使用:

“`
python manage.py migrate auth
python manage.py migrate
“`

request.user.is_anonymous 检查用户是否

django comes with login/logout forms and views

“`
from django.conf.urls import url
from django.contrib import admin
from django.contrib.auth import views as auth_views
urlpatterns = [
url(r’^login/$’, auth_views.login, name=’login’),
url(r’^logout/$’, auth_views.logout, name=’logout’),
url(r’^admin/’, admin.site.urls),
]
“`

By default, the django.contrib.auth.views.login view will try to render the registration/login.html template.  and will redirct to the /accouts/profile page

“`
{% extends ‘base.html’ %}
{% block title %}Login{% endblock %}
{% block content %}

Login

{% csrf_token %}
{{ form.as_p }}

{% endblock %}
“`

You can change what django renders

`url(r’^login/$’, auth_views.login, {‘template_name’: ‘core/login.html’}, name=’login’),`

You can change where django redirects

`LOGIN_REDIRECT_URL = ‘home’ # in settings.py`

logout

by default, renders the registration/logged_out.html

`url(r’^logout/$’, auth_views.logout, {‘template_name’: ‘logged_out.html’}, name=’logout’),`

`url(r’^logout/$’, auth_views.logout, {‘next_page’: ‘/’}, name=’logout’),`

Note the difference, you don’t have to do anything when visiting /logout, the system will just log you out and send you to another page or render a logged out page

~~Side notes: why changing the redirect url is different with login? find it out later~~

django templates

# marco
 
there is no marco in django template, you just have to use include with parameters

“`
{% include “marco.html” with arg=parameter %}
“`

# variable

“`
{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}
“`

If a variable resolves to a callable, the template system will call it with no arguments and use its result instead of the callable.

# tags

# filters