摘要
版本:python3.6.4+django2.0.3
Demo: https://github.com/hanqunfeng/DjangoHelloWorld参考资料:
官方资料
Django 1.8.2 文档
Django 1.11.6 文档
Django 2.0.2文档
Django中文教程
1.安装
pip install Django
python -m django --version
2.创建新项目
django-admin startproject mysite
# mysite就是项目名称
3.创建新的应用
python manage.py startapp polls
# polls是应用名称
settings.py中加入新应用配置
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
4.创建和更新数据库:
python manage.py makemigrations
# 全部应用都会创建迁移文件
python manage.py makemigrations polls
# 只创建指定的应用
python manage.py migrate
# 执行迁移文件到数据库
查看迁移文件生成的sql:
sqlmigrate命令接收迁移文件的名字并返回它们的SQL语句:#只是打印出要执行的sql语句
python manage.py sqlmigrate polls 0001
# 这里迁移文件的后缀_initial.py不需要。
5.启动服务器
Django的管理后台站点是默认启用的。 让我们启动开发服务器,然后探索它。
如果服务器没有运行,像下面这样启动它:
python manage.py runserver
现在,打开一个浏览器访问你本地域名中的 “/admin/” — 例如http://127.0.0.1:8000/admin/。
启动:
python manage.py runserver 9000
#指定启动端口
python manage.py runserver 0.0.0.0:9000
#指定启动ip+端口
6.测试:
python manage.py test
#运行整个项目的全部tests.py
python manage.py test django2
#运行指定模块的tests.py
python manage.py test django2.tests.Django2Test
#测试指定模块的指定测试类
python manage.py test django2.tests.Django2Test.test_sql
#测试指定模块的指定测试类指定方法
7.检查代码覆盖率:
pip install coverage
coverage run my_program.py arg1 arg2
django检查方法:
coverage run --source='.' manage.py test myapp
之后可以运行
coverage report
:显示结果
coverage html
:生成html 测试会在当前项目下生成htmlcov目录,运行index.html即可查看
8.mysql:
brew install mysql-connector-c
pip install mysqlclient
需要提前创建好数据库
settings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django',
'USER': 'django',
'PASSWORD': 'django',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
数据库更新:
一般情况下,我们使用如下两个命令更新数据库
python manage.py makemigrations
#生成数据库模型文件
python manage.py migrate
#执行模型文件
或者:
python manage.py migrate --database=users
#指定数据库,默认为default
如果由于默写原因删除了数据库中对应的表,则再次执行上面的命令是不能重新创建成功的,原因是每次django执行模型文件时都会在django_migrations表中新增对应的log记录,删掉对应的log记录即可重新执行成功。
9.多数据源配置
django配置连接多个数据库,自定义表名称:
https://www.cnblogs.com/dreamer-fish/p/5469141.html
使用models文件夹维护model时,一定要在其下的init.py中添加对model的引用,
否则python manage.py makemigrations
命令不会创建出对应的迁移文件
比如:
from .person import Person
from .user import User
from .identity_card import IdentityCard
from .car import Car
数据库路由:
settings.py:
DATABASE_ROUTERS = ['django2.router.django2_router.Django2Router', ]
可以将对应的迁移文件的sql导入到指定的db,所以路由器的设置很重要
def allow_migrate(self, db, app_label, model_name=None, **hints):
if db == 'django2_db': #如果指定了数据库
return app_label == 'django2' #并且model被设置了正确的app_label,则可以执行迁移文件
elif app_label == 'django2':
return False
设置好数据库路由器后,执行python manage.py migrate --database=django2_db
10.缓存
说明:不推荐使用站点级缓存和页面级缓存,除非是展示信息类的网站,如果是频繁修改的站点,最好手工在代码中维护缓存。
1).memcached
brew install memcached
启动:memcached -d -p 11211 -c 1024 -m 64
-d:后台运行
-p:端口
-c:最大连接数
-m:最多分配内存
1.使用memcached:pip install python-memcached
2.settings
# 缓存设置
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
'TIMEOUT': 600, # 单位秒,默认300s, 60s * 10 = 10min
'KEY_PREFIX': 'myapp', # 缓存键的字符串前缀
}
}
3.代码中
from django.core.cache import caches
cache = caches['default']
#如果希望使用默认的default,也可以
from django.core.cache import cache
cache.set('user_list', user_list)
user_list = cache.get('user_list')
user_list = cache.delete('user_list')
2).redis
参考资料:http://django-redis-chs.readthedocs.io/zh_CN/latest/
1.brew install redis
启动:redis-server /usr/local/etc/redis.conf
2.pip install django-redis
3.settings
# 缓存设置
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
'TIMEOUT': 600, # 单位秒,默认300s, 60s * 10 = 10min
'KEY_PREFIX': 'myapp', # 缓存键的字符串前缀
},
"redis": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
'TIMEOUT': 600,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"SOCKET_CONNECT_TIMEOUT": 5, # in seconds socket 建立连接超时设置
"SOCKET_TIMEOUT": 5, # in seconds 连接建立后的读写操作超时设置
"COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor", # 压缩支持
"IGNORE_EXCEPTIONS": True, # 如果redis服务关闭,不会引起异常,memcached默认支持
"CONNECTION_POOL_KWARGS": {"max_connections": 100} # 连接池
}
}
}
# redis记录异常日志
DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True
4.代码中
from django.core.cache import caches
redis_cache = caches['redis']
redis_cache.set('user_list', user_list)
user_list = redis_cache.get('user_list')
user_list = redis_cache.delete('user_list')
11.注册模板自定义方法:
1.创建myapp.libraries.utils.py
from django import template
register = template.Library()
color = ((1, 'red'), (2, 'black'), (3, 'blue'))
# @register.filter使用方法,最多两个参数
# {{ car.carColor|getcolorstr }}
# {{ car.carColor|getcolorstr:param2 }} 前面的表示第一个参数
@register.filter
def getcolorstr(colorNum):
return color[colorNum - 1][1]
# @register.simple_tag使用方法,不限制参数个数
# {% getcolorstr2 car.carColor %}
# {% getcolorstr2 param1 param2 param3 %}
@register.simple_tag
def getcolorstr2(colorNum):
return color[colorNum - 1][1]
2.settings:在模板配置中加入libraries配置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'libraries': { # Adding this section should work around the issue.
'utils': 'myapp.libraries.utils',
},
},
},
]
3.模板页面中使用
{% load utils %}
{{ car.carColor|getcolorstr }}
{% getcolorstr2 car.carColor %}
12.模板
1.转义:
由于模板系统没有“转义”的概念,为了显示模板标签中使用的一个位,必须使用{% templatetag %}标记。
论据 输出
openblock {%
closeblock %}
openvariable {{
closevariable }}
openbrace {
closebrace }
opencomment {#
closecomment #}
例如:
{% templatetag openblock %} url 'entry_list' {% templatetag closeblock %}
或者使用如下方式:被包含的内容不会被模板引擎转意,将直接输出
{% verbatim myblock %}
Avoid template rendering via the {% verbatim %}{% endverbatim %} block.
{% endverbatim myblock %}
2.for:
变量 描述
forloop.counter 循环的当前迭代(1索引)
forloop.counter0 循环的当前迭代(0索引)
forloop.revcounter 循环结束的迭代次数(1索引)
forloop.revcounter0 循环结束的迭代次数(0索引)
forloop.first 如果这是第一次通过循环,则为真
forloop.last 如果这是最后一次循环,则为真
forloop.parentloop 对于嵌套循环,这是围绕当前循环的循环
13.自定义400、403、404、500页面
1.settings.py中DEBUG = False,否则自定义页面不起作用
2.在任意模块下的views.py中增加如下方法,也可以在主模块中创建一个views.py
方法处理逻辑可以参考:~venv/lib/python3.6/site-packages/django/views/defaults.py中对各个方法的定义
from django.shortcuts import render
def bad_request(request, exception, template_name='400.html'):
return render(request, template_name)
def permission_denied(request, exception, template_name='403.html'):
return render(request, template_name)
def page_not_found(request, exception, template_name='404.html'):
context = {'exception': exception}
return render(request, template_name, context=context)
def server_error(request, template_name='500.html'):
return render(request, template_name)
3.在项目根目录下的templates下创建对应的400.html、403.html、404.html、500.html,内容更加需要自定义,也可以参考~venv/lib/python3.6/site-packages/django/views/templates下的对应文件
4.在主模块下urls.py中增加如下配置:
handler400 = 'DjangoHelloWorld.views.bad_request' #模块名称.views.方法名称
handler403 = 'DjangoHelloWorld.views.permission_denied'
handler404 = 'DjangoHelloWorld.views.page_not_found'
handler500 = 'DjangoHelloWorld.views.server_error'
14.Django配置session超时
配置失效时间为半个小时
SESSION_COOKIE_AGE = 60*30
关闭浏览器清除cookie
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
15.json与xml
1.json
创建一个JSONUtil工具类,用于返回json数据
import json
from django.core.serializers import serialize, deserialize
from django.db import models
from django.db.models.query import QuerySet
from django.http import JsonResponse
# 反序列化
def json_to_list(json):
if json[0] == '[':
deserializedObjectList = deserialize('json', json)
else:
deserializedObjectList = deserialize('json', '[' + json + ']')
list = []
for deserializedObject in deserializedObjectList:
list.append(deserializedObject.object)
return list
# 序列化
def to_json(obj):
if isinstance(obj, models.Model):
obj = [obj] # 因为serialize只支持可迭代对象,比如querySet对象
data = serialize("json", obj)
return data
# 该方法没有做严格的验证,只支持dict,models.Model,models.QuerySet,可以根据需要自行扩展
def render_json(data, dict_key='data', **response_kwargs):
if isinstance(data, dict):
return JsonResponse(data)
data = to_json(data)
if 'safe' in response_kwargs and response_kwargs['safe'] is False:
pass
else:
data = {dict_key: data} # 默认必须传递字典数据
if isinstance(data, str): # 由于非字典类型的数据会被当做字符串处理,即返回结果两边都有引号,所以此处将其转换为对象,否则ajax调用时不方便处理
data = json.loads(data)
return JsonResponse(data, **response_kwargs)
view.py中:
def user_query_json(request):
user_list = User.objects.all()
return JSONUtil.render_json(user_list, safe=False) # safe=False可以传递对象,否则必须传递一个dict,ajax请求时这样要设置safe=False,这样页面可以直接获取到对象
返回结果,可以看到两边没有引号:
[{"model": "myapp.user", "pk": 4, "fields": {"name": "\u54c8\u54c8", "birth_day": "2018-04-09", "phone": "None", "email": "None"}}, {"model": "myapp.user", "pk": 9, "fields": {"name": "\u5929\u738b\u5c71", "birth_day": "2018-09-10", "phone": "123", "email": "[email protected]"}}]
def user_query_json_get(request, user_id):
user = User.objects.get(pk=user_id)
# user = User.objects.filter(pk=user_id)
return JSONUtil.render_json(user, dict_key='user', safe=True)
返回结果:[{"model": "myapp.user", "pk": 1, "fields": {"name": "\u97e9\u7fa4\u5cf0", "birth_day": "2018-04-07", "phone": "None", "email": "[email protected]"}}]
模板中:
#注意这里必须有闭合标签,否则显示会有问题
2.xml
XMLUtil.py
# -*- coding=utf-8 -*-
from django.core import serializers
from django.db import models
from django.db.models.query import QuerySet
from django.http import HttpResponse
def render_xml(data):
data = to_xml(data)
response = HttpResponse(data)
response['Content-Type'] = 'application/xml'
return response
# 序列化
def to_xml(data):
if isinstance(data, models.Model):
data = [data] # 因为serialize只支持可迭代对象,比如querySet对象
elif isinstance(data, QuerySet):
data = data
else:
pass
data = serializers.serialize("xml", data)
return data
# 反序列化
def xml_to_list(xml):
deserializedObjectList = serializers.deserialize("xml", xml)
list = []
for deserializedObject in deserializedObjectList:
list.append(deserializedObject.object)
return list
views.py
from utils import XMLUtil
def user_query_xml(request):
user_list = User.objects.all()
return XMLUtil.render_xml(user_list)
def user_query_xml_get(request, user_id):
user = User.objects.get(pk=user_id)
return XMLUtil.render_xml(user)
输出结果
js:
$.ajax({
url:"{% url 'myapp:user_query_xml' %}",
type:"GET",
dataType:'xml',
success:function(xml){
$(xml).find("object").each(function(i) {
//获取id
var id=$(this).attr("pk");
var content = "";
$(this).find("field").each(function(j){
content += $(this).attr('name') + "==" + $(this).text() + "#"
})
$("#userdivxml").append(id+ "#" + content +"
")
});
},
error:function(){ alert("加载失败"); }
})
16.response添加相应头
一般我们返回视图时都是调用
from django.shortcuts import render的render(request, 'myapp/user/index.html', context)
实际上它返回的是一个HttpResponse对象,我们可以这样为其添加返回头
response = render(request, 'myapp/user/index.html', context)
response['Last-Modified'] = date.strftime('%a, %d %b %Y %H:%M:%S GMT')
return response
17.多语言
参考:https://code.ziqiangxuetang.com/django/django-internationalization.html
1.brew install gettext
2.pip的bug,需要手工处理
/venv/lib/python3.6/site-packages/pip-9.0.1-py3.6.egg/pip/_vendor/webencodings/
修改3个文件:
init.py,
tests.py,
x_user_defined.py,
将:utf8 修改为 utf-8.
3.settings.py
LANGUAGE_CODE = 'zh-hans' # 英文是en,这里是中文,注意这里必须配置为zh-hans,而下面创建和编译语言文件是要使用zh_hans
USE_I18N = True
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'myapp/locale'), # 应用下的路径
os.path.join(BASE_DIR, 'locale'),
)
注意:这里『locale』文件夹需要手工创建,默认就是项目根路径下的locale目录。
这里需要注意一点,如果应用下面创建了locale并且配置到LOCALE_PATHS中,则后面执行创建命令时,无论是在项目根路径下执行还是在应用下执行,都只会将语言文件创建到应用下的locale中。如果应用下没用locale目录则需要在项目根路径下执行命令,并且创建到项目根路径下的locale目录中。
4.在代码中加入一些多语言对应的内容
代码中
from django.utils.translation import ugettext as _
output = _('Today is %(month)s %(day)s.') % {'month': m, 'day': d}
模板页面中可以直接使用下划线的别名形式
{{ _('Django site admin') }}
{{ _('my test local') }}
这里注意,如果要使用『trans』标签,必须在页面中加载{% load i18n %}
{% load i18n %}
{% trans "my test local" %}
{#将翻译结果保存到变量中#}
{% trans "my test local" as mylocal %}
{{ mylocal }}
{#设置局部显示的语言,下面的内容将显示对应的英文内容,但只在区块内有效#}
{% language 'en' %}
{% get_current_language as LANGUAGE_CODE %}
Current language: {{ LANGUAGE_CODE }}
#区块内显示en
{{ _('Django site admin') }}
{% endlanguage %}
{% get_current_language as LANGUAGE_CODE %}
Current language: {{ LANGUAGE_CODE }}
#区块外显示zh-hans
如果没有找到对应的key值,则会直接显示待翻译的key值字符串;
如果对应的语言包下没有找到key值,而默认语言包下有对应的key值,则会显示默认的语言,如LANGUAGE_CODE = 'zh-hans'
PS:如果需要翻译的内容包含变量,比如_('Today is %(month)s %(day)s.') ,最好在后台处理好后做为变量传递到模板页面上,目前暂不知道如何在模板中直接处理。
5.创建或更新语言文件
django-admin makemessages -l en
# 英文
django-admin makemessages -l zh_hans
#指定中文语言,注意这里不要写成zh-hans
会在locale目录下生成对应的语言包django.po
django-admin makemessages -a
#全部语言
说明:如果在项目根路径下执行,会将项目中所有应用都扫描一遍并汇总合并到一起,如果在某个应用下执行命令,则只会扫描当前应用,并在其下的locale目录下创建文件,优先级根据settings中配置的LOCALE_PATHS的顺序而定。
6.编译
django-admin compilemessages --locale zh_hans
#指定语言
django-admin compilemessages
# 全部语言
- django.po---->diango.mo
7.语言切换
1)在settings中的中间件配置中加入如下配置:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
2)url中加入配置:
path('i18n/', include('django.conf.urls.i18n')), #对应下面的{% url 'set_language' %}
变更后的语言会保存在session中,可以通过request.session['_language']
获得
3)在模板页面中需要切换语言的地方加入如下代码:
说明:
redirect_to:如果不设置就会返回当前页面,设置的话就会跳转到设置的页面
这里get_available_languages会显示所有支持的语言,不过一般项目不会支持这么多的语言,所以可以在settings中增加配置来明确语言范围:
LANGUAGES = (
('en', ('English')),
('zh-hans', ('中文简体')),
('zh-hant', ('中文繁體')),
)
4)js中使用多语言
js需要单独处理,比如我们写了一个js文件,路径为project/myapp/static/myapp/js/test.js
a = gettext('wwww hhhh')
alert(a)
模板中引入:
下面这个是动态js,必须引入,否则gettext方法不起作用
urls加入对javascript-catalog的支持:
`path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),``
执行如下命令:
django-admin makemessages -d djangojs -l zh_hans
此时会在应用下的locale中生成djangojs.po文件(如果配置了应用locale,否则会在项目下的locale中创建)
django-admin compilemessages --locale zh_hans
此时会将djangojs.po编译为djangojs.mo
如果直接将带翻译的js代码写在模板页面中,暂时不清楚要通过什么命令实现,不过可以有个折中的办法,就是创建一个js文件,然后将所有需要翻译的内容都加上,然后运行上面两个命令,这样django在运行模板中的js时同样可以完成翻译
模板中:
js中:只要js代码中出现翻译方法的地方都会被加入翻译,这个js不需要被任何模板引入,也不需要被同步到静态文件夹中,仅仅是为生成翻译文件而存在
gettext('hello js')
gettext('o my god')
18.日志
1.settings
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 是否禁用logger,建议设置为False
'formatters': { # 日志格式,提供给handler使用,非必须,如果不设置格式,默认只会打印消息体
'verbose': { # 格式名称
# INFO 2018-04-25 15:43:27,586 views 8756 123145350217728 这是一个日志
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
'simple': {
# INFO 这是一个日志
'format': '%(levelname)s %(message)s'
},
'standard': {
# 2018-04-25 16:40:00,195 [Thread-7:123145575223296] [myapp.log:282] [views:user_query_json_get] [INFO]- 这是一个日志
'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'
},
},
'filters': { # 过滤器,提供给handler使用,非必须
'require_debug_true': { # 要求DEBUG=True时才打印日志
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': { # 处理器,设置日志记录方式,必须
'console': { # 处理器名称
'level': 'DEBUG', # 设置级别
'filters': ['require_debug_true'], # 设置过滤器,多个用逗号分割
'class': 'logging.StreamHandler', # 处理器,这里是控制台打印
'formatter': 'verbose' # 设置日志格式
},
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler', # 记录到文件
'filename': '/Users/hanqunfeng/python_workspace/log/file.log',
'formatter': 'verbose'
},
'rotatingFile': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 按文件大小切割日志
# 'filename': 'log/all.log', # 日志输出文件 默认在当前项目根路径下
'filename': '/Users/hanqunfeng/python_workspace/log/rotatingFile.log', # 日志输出文件
'maxBytes': 1024 * 1024 * 5, # 每个文件大小
'backupCount': 5, # 保留日志份数,只保留最后5份,如果都保留,设置为0,默认就是0
'formatter': 'standard', # 使用哪种formatters日志格式
},
'timedRotatingFile': {
'level': 'DEBUG',
'class': 'logging.handlers.TimedRotatingFileHandler', # 按时间切割日志
'filename': '/Users/hanqunfeng/python_workspace/log/timedRotatingFile.log', # 日志输出文件
'when': 'D', # 按天分割
'backupCount': 5, # 保留日志份数,只保留最后5份,如果都保留,设置为0,默认就是0
'formatter': 'standard', # 使用哪种formatters日志格式
},
},
'loggers': {#日志记录器
'django': {#日志名称路径前缀,即logging.getLogger(__name__)获取logger对象时,_name__得到的前缀与之匹配即可,比如__name__得到的是django.server
'handlers': ['console'],
'propagate': True,
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG'), # 只有设置DEBUG = True时,该配置才会打印sql信息
},
'django.request': {
'handlers': ['rotatingFile'],
'level': 'ERROR',
'propagate': False, # 设置为False,表示不像其父级别传递日志内容
},
'myapp.log': { # 也可以这样创建logger对象,logging.getLogger('myapp.log')
'handlers': ['file', 'timedRotatingFile'],
'level': 'INFO', # 这里的日志级别不能低于处理器中设置的日志级别
},
},
}
代码中使用方式:
# 导入logging库
import logging
# 获取logger的一个实例
# logger = logging.getLogger(__name__)
logger = logging.getLogger('myapp.log')
# 方法中:
logger.info('这是一个日志')
19.发送邮件
1.settings
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_SSL = True
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 465
EMAIL_HOST_USER = '[email protected]' # 帐号
EMAIL_HOST_PASSWORD = 'xxxxxxx' # 密码
DEFAULT_FROM_EMAIL = 'hanqf '
2.代码里
from django.conf import settings
# 发送邮件
from django.core.mail import send_mail
send_mail('Subject here主题', 'Here is the message.消息', settings.DEFAULT_FROM_EMAIL,
['[email protected]'], fail_silently=False)
# 一次可以发送多组邮件
from django.core.mail import send_mass_mail
message1 = ('Subject here', 'Here is the message', settings.DEFAULT_FROM_EMAIL,
['[email protected]', '[email protected]'])
message2 = ('Another Subject', 'Here is another message', settings.DEFAULT_FROM_EMAIL, ['[email protected]'])
send_mass_mail((message1, message2), fail_silently=False)
# 可以这是抄送附件等
from django.core.mail import EmailMultiAlternatives
msg = EmailMultiAlternatives('主题', '内容', settings.DEFAULT_FROM_EMAIL, ['[email protected]'],
cc=['[email protected]'])
# msg.content_subtype = "html" # 设置邮件格式,html可以发送内容为html,不推荐这么使用,可以使用下面的方式
html_content = '这是一封重要的邮件.
'
msg.attach_alternative(html_content, "text/html") # 如果接收方的邮件支持html,则显示该信息,否则显示原「内容」
# 添加附件(可选)
msg.attach_file('/Users/hanqunfeng/python_workspace/STATIC_ROOT/polls/images/background.jpg')
# 发送
msg.send()
20.main方法测试
mian方法测试一定要在如下情况下使用,这样可以保证当前模块被别处引用时不会触发如下测试代码,只有独立运行该模块时才会执行。
if __name__ == '__main__':
# 加载环境配置
import django, os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "DjangoHelloWorld.settings")
django.setup()
# 以下是测试内容
from myapp.models.user import User
user_list = User.objects.all()
xml = to_xml(user_list)
print(xml)
21.Signal,信号,有点类似MQ
1.定义信号和接收器
from django.dispatch import Signal, receiver
# my_singal = Signal()
my_singal = Signal(providing_args=["key1", "key2"]) # 定义信号接收的参数,不指定参数也可以
@receiver(my_singal)
def my_callback(sender, **kwargs): # 接收器回调函数
print(sender)
print(kwargs)
for key in kwargs:
print(key)
print(kwargs[key])
print("Request finished!")
2.发送信号,发送信号时接收器就会被执行
from signals.signals import my_singal
my_singal.send(sender=__name__, key1='qqq', key2=10, key3=100) # 实际上可以多发送一些参数
22.Django管理后台简介
首先,我们需要创建一个能够登录管理后台站点的用户。
运行如下命令:
python manage.py createsuperuser
键入你想要使用的用户名,然后按下回车键:
Username: admin
然后提示你输入想要使用的邮件地址:
Email address: [email protected]
你需要输入两次密码,第二次输入是确认密码
Password: **********
Password (again): *********
Superuser created successfully.
PS:管理员密码忘记了可以通过如下方法修改:
$ python manage.py shell
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(pk=1) # 可以通过查询获得用户对象
>>> user.set_password('xxxxxxxx')
>>> user.save()
>>> quit()
23.部署正式环境
settings.py:
DEBUG = False # 此时很多问题就会出现,需要增加很多额外的配置才能正常工作,这也是为了包含生产环境吧
ALLOWED_HOSTS = ['127.0.0.1']
# ALLOWED_HOSTS = ['*', ] # 允许所有机器访问
STATIC_URL = 'http://localhost/static/' # apache部署的静态文件服务器访问地址
STATIC_ROOT = "/Users/hanqunfeng/python_workspace/STATIC_ROOT/" #apache 服务目录
# 上传文件路径
MEDIA_URL = 'http://localhost/media/'
MEDIA_ROOT = '/Users/hanqunfeng/python_workspace/MEDIA/'
apache配置:
Alias /media/ /Users/hanqunfeng/python_workspace/MEDIA/
Alias /static/ /Users/hanqunfeng/python_workspace/STATIC_ROOT/
Require all granted
Require all granted
使用如下命令可以将本地的静态资源部署到apache服务目录:
python manage.py collectstatic
模板页面:
{% load static %}
上传文件:
model中:
photo = models.ImageField(upload_to="photo", default="default/django.jpeg") # 路径相对于MEDIA_ROOT的配置
之后要注意更新数据库。
需要安装Pillow,否则会报错
ERRORS:
polls.Question.photo: (fields.E210) Cannot use ImageField because Pillow is not installed.
HINT: Get Pillow at https://pypi.python.org/pypi/Pillow or run command "pip install Pillow".
pip install Pillow
如果要在页面中使用settings中的变量,需要在当前应用中创建一个context_processors.py 文件
from django.conf import settings # import the settings file
def settings_constant(request):
# return the value you want as a dictionnary. you may add multiple values in there.
return {'MEDIA_URL': settings.MEDIA_URL, 'DEBUG': settings.DEBUG}
并在settings文件配置如下
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'polls.context_processors.settings_constant', #应用名称.文件名称.方法名称
],
},
},
]
模板页面中:
也可以使用下面的形式获得上传文件的url,
即使用上传文件字段的url属性:{{ question.photo.url }}
{{ question.photo }} ##
{{ question.photo }}
views处理代码中:
input_img = request.FILES['photo']
question.photo = input_img
question.save()
部署到apache:
下载mod_wsgi:https://github.com/GrahamDumpleton/mod_wsgi/releases
tar xvfz mod_wsgi-X.Y.tar.gz
./configure --with-apxs=/Applications/XAMPP/bin/apxs --with-python=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
make
make install
然后在apache配置文件中加入如下配置
LoadModule wsgi_module modules/mod_wsgi.so
普通模式:
WSGIPythonHome /Users/hanqunfeng/python_workspace/DjangoHelloWorld/venv
WSGIPythonPath /Users/hanqunfeng/python_workspace/DjangoHelloWorld
或者采用守护进程模式:
WSGIDaemonProcess example.com python-home=/Users/hanqunfeng/python_workspace/DjangoHelloWorld/venv python-path=/Users/hanqunfeng/python_workspace/DjangoHelloWorld
WSGIProcessGroup example.com
配置项目访问路径
WSGIScriptAlias /mysite /Users/hanqunfeng/python_workspace/DjangoHelloWorld/DjangoHelloWorld/wsgi.py
Require all granted
访问地址:http://127.0.0.1/mysite/polls