Django2学习笔记

摘要

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": "123@123.com"}}]

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": "qunfeng_han@126.com"}}]

模板中:

<script src="{% static 'polls/js/jquery-1.11.0.min.js' %}"></script> #注意这里必须有闭合标签</script>,否则显示会有问题

<div id="userdiv"></div>
<div id="userlistdiv"></div>

<script>

    $.getJSON("{% url 'myapp:user_query_json_get' 1 %}", function(ret) {
        $.each(ret, function (key, value) {
            // key 为字典的 key,value 为对应的值
            $("#userdiv").append(value.pk+"#"+value.fields.name+"#"+value.fields.birth_day+"#"+value.fields.phone+"#"+value.fields.email+"<br>")

        });
    });

    $.getJSON("{% url 'myapp:user_query_json' %}", function(ret) {
        $.each(ret, function (key, value) {
            // key 为字典的 key,value 为对应的值
            $("#userlistdiv").append(value.pk+"#"+value.fields.name+"#"+value.fields.birth_day+"#"+value.fields.phone+"#"+value.fields.email+"<br>")

        });
    })

</script>

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)

输出结果

<?xml version="1.0" encoding="utf-8"?>
<django-objects version="1.0">
    <object model="myapp.user" pk="4">
        <field name="name" type="CharField">哈哈</field>
        <field name="birth_day" type="DateField">2018-04-09</field>
        <field name="phone" type="CharField">13800138000</field>
        <field name="email" type="CharField">138@qq.com</field>
    </object>
    <object model="myapp.user" pk="2">
        <field name="name" type="CharField">张三</field>
        <field name="birth_day" type="DateField">
            <None></None>
        </field>
        <field name="phone" type="CharField">
            <None></None>
        </field>
        <field name="email" type="CharField">zhansan@163.com</field>
    </object>
</django-objects>

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 +"<br>")

        });
    },
    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') }}<br>
{{ _('my test local') }}<br>

这里注意,如果要使用『trans』标签,必须在页面中加载{% load i18n %}

{% load i18n %}
{% trans "my test local" %}<br>

{#将翻译结果保存到变量中#}
{% trans "my test local" as mylocal %}
{{ mylocal }}<br>

{#设置局部显示的语言,下面的内容将显示对应的英文内容,但只在区块内有效#}
{% language 'en' %}
    {% get_current_language as LANGUAGE_CODE %}
    Current language: {{ LANGUAGE_CODE }} <br>  #区块内显示en
    {{ _('Django site admin') }}<br>
{% endlanguage %}

{% get_current_language as LANGUAGE_CODE %}
    Current language: {{ LANGUAGE_CODE }} <br>  #区块外显示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)在模板页面中需要切换语言的地方加入如下代码:

<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
    <input name="next" type="hidden" value="{{ redirect_to }}" />
    <select name="language">
        {% get_current_language as LANGUAGE_CODE %}
        {% get_available_languages as LANGUAGES %}
        {% get_language_info_list for LANGUAGES as languages %}
        {% for language in languages %}
            <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
                {{ language.name_local }} ({{ language.code }})
            </option>
        {% endfor %}
    </select>
    <input type="submit" value="Go" />
</form>

说明:
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方法不起作用

<script type="text/javascript" src="{% url 'javascript-catalog' %}"></script>
<script src="{% static 'myapp/js/test.js' %}"></script>

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时同样可以完成翻译
模板中:

<script>
    alert(gettext('hello js'))
    alert(gettext('o my god'))
</script>

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 = 'xxx@163.com'  # 帐号
EMAIL_HOST_PASSWORD = 'xxxxxxx'  # 密码
DEFAULT_FROM_EMAIL = 'hanqf <xxx@163.com>'

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,
          ['aaaaa@126.com'], fail_silently=False)

# 一次可以发送多组邮件
from django.core.mail import send_mass_mail

message1 = ('Subject here', 'Here is the message', settings.DEFAULT_FROM_EMAIL,
            ['aaaaa@126.com', 'aaaaa@163.com'])
message2 = ('Another Subject', 'Here is another message', settings.DEFAULT_FROM_EMAIL, ['aaaaa@126.com'])
send_mass_mail((message1, message2), fail_silently=False)

# 可以这是抄送附件等
from django.core.mail import EmailMultiAlternatives
msg = EmailMultiAlternatives('主题', '内容', settings.DEFAULT_FROM_EMAIL, ['aaaaa@126.com'],
                             cc=['aaaaa@163.com'])
# msg.content_subtype = "html" # 设置邮件格式,html可以发送内容为html,不推荐这么使用,可以使用下面的方式
html_content = '<p>这是一封<strong>重要的</strong>邮件.</p>'
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: admin@example.com

你需要输入两次密码,第二次输入是确认密码

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/

<Directory /Users/hanqunfeng/python_workspace/STATIC_ROOT>
Require all granted
</Directory>

<Directory /Users/hanqunfeng/python_workspace/MEDIA/>
Require all granted
</Directory>

使用如下命令可以将本地的静态资源部署到apache服务目录:

python manage.py collectstatic

模板页面:

{% load static %}

<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />

上传文件:
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', #应用名称.文件名称.方法名称
            ],
        },
    },
]

模板页面中:

<form enctype="multipart/form-data">
<img src="{{MEDIA_URL}}abc/a.png">
<input type="file" name="photo" id="id_photo" />
</form>

也可以使用下面的形式获得上传文件的url,
即使用上传文件字段的url属性:{{ question.photo.url }}
<a href="{{MEDIA_URL}}{{ question.photo }}">{{ question.photo }}</a> ##
<a href="{{ question.photo.url }}">{{ question.photo }}</a>

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

<Directory /Users/hanqunfeng/python_workspace/DjangoHelloWorld/DjangoHelloWorld>
<Files wsgi.py>
Require all granted
</Files>
</Directory>

访问地址:http://127.0.0.1/mysite/polls

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

推荐阅读更多精彩内容