Django1.11 项目应用模块

验证码插件

https://www.jianshu.com/p/c9ede8f80001

安装

pip3 install django-simple-captcha

使用

配置

settings.py 中注册这个应用

INSTALLED_APPS = [    
    'captcha',
]

生成数据表

makemigrations
migrate

路由

urls.py

# django1.x
url(r'^captcha/', include('captcha.urls')), 

# django2.x
path(r'^captcha/', include('captcha.urls')), 

Form

from django import forms
from captcha.fields import CaptchaField

class CaptchaTestForm(forms.Form):
    myfield = AnyOtherField()  # 代指其他任意的字段
    captcha = CaptchaField(error_messages={
        "invalid": "无效的验证码"
    })

上面的类名可以自定义,并且向里面添加新的字段

…or, as a ModelForm:

from django import forms
from captcha.fields import CaptchaField

class CaptchaTestModelForm(forms.ModelForm):
    captcha = CaptchaField()
    class Meta:
        model = MyModel

视图

views.py

def some_view(request):
    if request.POST:
        form = CaptchaTestForm(request.POST)
        # check the input
        if form.is_valid():
            human = True
    else:
        form = CaptchaTestForm()

    return render_to_response('template.html',locals())

模板

{{ register_form.captcha }} {{ register_form.errors.captcha }} {% csrf_token %}

渲染后的 html

captcha



用户注册功能

model

假如有下面的 model

from django.db import models
from django.contrib.auth.models import AbstractUser

class UserProfile(AbstractUser):
    pass

# 这里是继承了 Django 的 model

视图函数

配合验证码中的视图继续编写用户注册逻辑

from django.shortcuts import render
from django.contrib.auth.hashers import make_password
from users.models import UserProfile

def some_view(request):
    if request.POST:
        registerform = CaptchaTestForm(request.POST)
        # check the input
        if form.is_valid():
            user_name = register_form.cleaned_data.get(
                'email')
            password = register_form.cleaned_data.get(
                'password')
            # 给用户的密码加密
            password = make_password(password)
            UserProfile.objects.create(
                **{"username": user_name,
                   "email": user_name,
                   "password": password})
            return render(request, 'login.html')
    else:
        form = CaptchaTestForm()

    return render(request, 'register.html',locals())

登录验证

==前提条件是使用 Django 自带的 User model, 或者自定义的 model,但是这个 model 需要继承 AbstractUser==

1. 基于 FBV
# 验证用户
from django.contrib.auth import authenticate

# 验证成功后,把用户放进 session 中
from django.contrib.auth import login

# 用户退出则删除 session
from django.contrib.auth import logout

模板语言可以在前端判断用户是否通过了验证,返回 True/False

{% if request.user.is_authenticated %}

Examp

users.views.py

登录

def user_login(request):
    if request.method == "POST":
        user_name = request.POST.get('username')
        password = request.POST.get('password')

        user_obj = authenticate(username=user_name,
                                password=password)
        if user_obj:
            login(request, user_obj)
            return render(request, 'index.html')
        else:
            return render(request, 'login.html',
                          {"msg": "用户名或密码错误"})
        
    elif request.method == "GET":
        return render(request, 'login.html')

退出登录

users.views.py

def user_logout(request):
    logout(request)
    return  redirect('/')

添加自定义验证功能

users.views.py

如下示例是验证用户名或者邮箱

from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

from users.models import UserProfile


class CustomBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            # 通过用户名或邮箱来获取用户对象
            user_obj = UserProfile.objects.get(
                Q(username=username) | Q(email=username)
            )
            
            # 验证用户的密码
            if user.check_password(password):
                return user_obj
        except Exception as e:
            return None

视图函数原来的代码不变

settings.py文件中配置如下内容

AUTHENTICATION_BACKENDS = (
    'users.views.CustomBackend',  # 注意后面的逗号
)
2. 基于 CBV

登录

==首先说明一点的是,上面的自定义的验证类,在这里也是可以使用的。==

所以我们这里只关注视图的编写

方式一:

views.py

from django.contrib.auth.views import LoginView

class MyLoginView(LoginView):
    template_name = 'login.html'  # 指定返回的模板文件

urls.py

from users import views

url(r'^login/$', views.LoginView.as_view(), name='login'),

# django2.x
path(r'^login/$', views.LoginView.as_view(), name='login'),

登录成功与否的跳转到指定的 url

settings.py

from django.core.urlresolvers import reverse_lazy

LOGIN_REDIRECT_URL = reverse_lazy('index')
# index 是一个 url 的名字

# 用户登录 GET 请求的 URL或验证失败还返回登录页面
LOGIN_URL = reverse_lazy('login')

方式二:

urls.py

from django.conf.urls import url
from django.contrib.auth.views import LoginView

urlpatterns = [
    url(r'^login/$', 
        LoginView.as_view(template_name = 'login.html'), 
        name='login'),
]

Django 的 LoginView 默认有 from对象,所以可以在前端直接使用

{% csrf_token %}
{{ form.username.label_tag }} {{ form.username }} {{ form.errors.username }}
{{ form.password.label_tag }} {{ form.password }}{{ form.errors.password }}

登录成功与否的跳转到指定的 url

settings.py

from django.core.urlresolvers import reverse_lazy

LOGIN_REDIRECT_URL = reverse_lazy('index')
# index 是一个 url 的名字

LOGIN_URL = reverse_lazy('login')

退出

views.py

from django.shortcuts import redirect
from django.views import View

class MyLogout(View):
    def get(self, request):
        logout(request)
        return redirect('/')

urls.py

from users import views
from django.conf.urls import url

urlpatterns = [
    url('^logout/$',  views.MyLogout.as_view(), name='logout'),
]

自定义登录状态验证类(CBV)

定义

utils/mixin_utils.py

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator


class LoginRequiredMixin(object):
    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args,
                                **kwargs)
    
CBV 中使用

views.py

from utils.mixin_utils import LoginRequiredMixin

class LessonsView(LoginRequiredMixin, View):
    """课程章节"""
    def get(self, request, course_id):
        return render(request, 'course-video.html')

Django 发送邮件

配置

下面一126邮箱为例

settings.py

EMAIL_HOST = "smtp.126.com"
EMAIL_PORT = 25
EMAIL_HOST_USER = '[email protected]'
EMAIL_HOST_PASSWORD = 'yourpassword' # 这个是授权密码,不是登录密码
EMAIL_USE_TLS = False
EMAIL_FROM = EMAIL_HOST_USER

发送邮件

from django.core.mail import send_mail
from MyProject import settings

send_mail(
    subject="邮件标题",
    message="邮件内容",
    from_email=settings.EMAIL_FROM,
    recipient_list=['[email protected]','[email protected]']  # 收件箱地址列表
    )

发送激活链接

场景

  1. 可以用于审批事情多链接
  2. 用于用户注册后的激活链接

逻辑

下面我用用户注册的功能来表述一下。

一般用户注册成功后,会发给用户的邮箱中一条激活用户的邮件,这个邮件中含有一个激活链接。其实就是服务器生成一条 url ,这条 url 中有服务器生成的随机字符串,用于识别每条 url 的唯一性。

逻辑如下:

用户表里会有一个字段,这个字段用于记录注册成功后用户是否激活。

在用户注册时候,服务器向用户提交的邮箱中发送用于激活的链接,同时会生成一串随机字符串,这个字符串会加入到激活链接中,同时会存放到一个专门用于存放这个字符串的表。

当用户点击这个链接时,服务器从 url 中获取到这个字符串,再从数据库中获取这个字符串,假如能获取到,就修改 用户表中的激活字段,激活用户。

具体实现代码

  1. 创建一个生成随机字符串的函数

    def generate_random_code(length=8):
        """
        生成给定长度的随机字符串
        :return:
        """
        import random
        s = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
        return ''.join(random.sample(s, length))
    
  2. 创建一个发送链接的函数

from django.core.mail import send_mail

def send_to_register_email(email, to_emails, 
                           send_type='register'):
    """
    用于给指定的邮箱地址发送链接
    :param email: 发件人邮箱地址
    :param to_emails: 收件箱地址,必须是列表或者元祖
    :param send_type: 类型 注册或者找回密码
    :return: 
    """
    if send_type == 'register':
        email_title = "鲨鱼在线学习平台激活链接"
        email_body = "请点击此链接地址激活账号:"
        url = "http://www.sharkyun.com/active/{}/"
        email_body = email_body + url.format(
            generate_random_code(16))  # 使用了上面的函数
        
    elif send_type == 'r':
        email_title = "鲨鱼在线学习平台找回密码"
        email_body = "请点击此链接地址:"
        url = "http://www.sharkyun.com/reback/{}/"
        email_body = email_body + url.format(
            generate_random_code(16))

    else:
        email_title, email_body = '', ''

    send_status = send_mail(
        subject=email_title,
        message=email_body,
        from_email=email,
        recipient_list=to_emails
    )
    if send_status == 1:
        print('发送成功')

Django 处理上传下载静态文件

假如用户上传一个图片文件作为自己的头像或者作为一个 logo, Django 该如何存放在本地。

之后用户访问页面时,Django 又该如何从服务器中取出该图片响应给前端呢?

Model 设计

models.py

class MyStatic(models.Model):
        name = models.CharField('姓名', max_length=64)
        image = models.ImageField(
            '头像', 
            upload_to='avatar/%Y/%m/%d/', 
            max_length=100)

配置

首先在 settings.py 中配置

  • 上传文件的根路径(针对上传)
    • 以后所有的图片都以此路径为自己的根路径,自己只需要写上相对路径即可。
  • 还需要配置路由相关信息(针对 GET 请求时的下载,主要是用于页面的展示)

首先在下项目的根目录下创建 media 文件夹

Django1.11 项目应用模块_第1张图片
image.png

增加配置

settings.py

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

django.template.context_processors.media

如果启用这个处理器,每个MEDIA_URL 将包含一个RequestContext 变量,表示MEDIA_URL 设置的值。

settings.py

TEMPLATES = [
        {'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.media',
            ]
        }}
]

上传

在项目的 urls.py
urls.py

from django.conf.urls import url
from learning_static import views as static_views

from LearningDjango1.settings import MEDIA_ROOT
from django.views.static import serve

urlpatterns = [
    # 处理上传
    url(r'^update/$', static_views.update),

    # 处理页面下载展示预览
    url(r'^media/(?P.*)', serve, {"document_root": MEDIA_ROOT})
]

views.py

from learning_static.models import MyStatic
def update(request):
        if request.method == "POST":
                name = request.POST.get('name')
                file_name = request.FILES.get('file')
                print(name, type(file_name))
                obj = MyStatic()
                obj.name = name
                obj.image = file_name
                obj.save()
                return HttpResponse("上传成功")
        else:
             return render(request, 'media.html')

media.html

名称 {% csrf_token %}

访问(下载)

urls.py

from django.conf.urls import url
from learning_static import views as static_views

from LearningDjango1.settings import MEDIA_ROOT
from django.views.static import serve

urlpatterns = [
    url(r"^show_image/$", static_views.show_image),

    # 处理已上传文件的访问路由
    url(r'^media/(?P.*)', serve, {"document_root": MEDIA_ROOT})
]

views.py

def show_image(request):
        file_name = request.GET.get('name')
        file_obj = MyStatic.objects.filter(name=file_name).first()
        return render(request,
                      'show_media.html',
                      {"image": file_obj})

show_media.html

头像

分页

这里采用的是第三方的模块

地址: https://github.com/jamespacileo/django-pure-pagination

安装

pip3 install  django-pure-pagination

注册应用

settings.py

INSTALLED_APPS = (
    ...
    'pure_pagination',
)

配置

setting.py

PAGINATION_SETTINGS = {
    'PAGE_RANGE_DISPLAYED': 10, 
    'MARGIN_PAGES_DISPLAYED': 2,

    'SHOW_FIRST_PAGE_WHEN_INVALID': True,
}
分页

PAGE_RANGE_DISPLAYED 每次当前页的两边显示多少个页码

FBV 视图

views.py


def index(request):

    try:
        page = request.GET.get('page', 1)
    except PageNotAnInteger:
        page = 1

    objects = MyModel.object.all()

    # Provide Paginator with the request object for complete querystring generation

    p = Paginator(objects, 5, request=request)
    # 这里的 5 是每页要显示的几条数据
    people = p.page(page)
    
    return render(request, 'index.html', 
                  {"people": people})

基于FBV的前端显示

index.html

{% load i18n %}

{# 循环数据开始 #}
    {% for object in people.object_list %}
        {{ object }}
    {% endfor %}

{# 循环数据结束 #}




CBV 视图

Generic Class-Based Views

  • views.py
from django.views.generic import ListView

from pure_pagination.mixins import PaginationMixin

from my_app.models import MyModel


class MyModelListView(PaginationMixin, ListView):
    # 告诉 ListView 使用分页,每页显示 10 条数据
    # 待验证
    paginate_by = 10

    # 将其替换为您的模型或使用 queryset 对象
    object = MyModel

基于CBV 的前端显示

这个和基于FBV 的一样

ModelForm

没有外键

models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

class UserProfile(AbstractUser):
    nick_name = models.CharField('昵称', max_length=25, default='')
    birday = models.DateField('生日', null=True, blank=True)
    mobile = models.CharField('手机', max_length=11)

userforms.py

import re
from django import forms
from users.models import UserProfile
class UserForm(forms.ModelForm):
    class Meta:
        model = UserProfile
        fields = ['nick_name', 'birday', 'mobile']
    
    # 针对指定字段添加自定义验证
    def clean_mobile(self):
        """
        验证手机号是否合法
        :return: 合法的数据或者错误信息
        """
        mobile = self.cleaned_data['mobile']
        PRGEX_MOBILE = r'^1[358]\d{9}|^147\d{8}|^176\d{8}$'
        regex = re.compile(PRGEX_MOBILE)
        if regex.match(mobile):
            return mobile
        else:
            raise forms.ValidationError('无效的手机号',
                                        code='mobile_invalid')

添加数据功能

views.py

from django.views import View
class UserView(View):
    def get(self, request):
        user_form = UserForm()
        return render(request,'users.html',
                      {'userForm': 'user_form'})
    def post(self, request):
        user_form = UserForm(request.POST)
        if user_form.is_valid():
            user_form.save()

编辑数据功能

views.py

from django.views import View

class EditUserView(View):
    def get(self, request):
        user_id = request.GET.get("user_id")
        user_obj = UserProfile.objects.filter(id=int(user_id))
        if user_obj:
            user_form = UserForm(instance=user_obj)
            return render(request, 'edit_user.html',
                          {"userForm": user_form,
                           "user_id": user_id})
    def post(self, request):
        user_id = request.GET.get("user_id")
        user_obj = UserProfile.objects.filter(id=int(user_id))
        if user_obj:
            user_form = UserForm(data=request.POST, instance=user_obj)
            user_form.save()
            return HttpResponse("更新成功")

有外键

可以在自定义验证的方法里返回外键对应的一个对象

配置全局404、500页面

要想让 Django 正常显示出我们配置好的 404等页面, 需要配置 settings.py 中的 DEBUG = False

DEBUG = False

但是,一旦这样设置后,Django 将不再代管我们的 css 等静态文件,所以原来settings.py 中的 STATICFILES_DIRS 将失效。

我们需要自己配置处理这个静态文件的路由。

settings.py

STATIC_ROOT = os.path.join(BASE_DIR, "static")

在项目的urls.py文件中配置路由

urls.py

from django.views.static import serve
from SKOnline.settings import STATIC_ROOT
urlpatterns = [
    url(r'^static/(?P.*)',
        serve, {"document_root": STATIC_ROOT}),
]  

Django 中数据互导

把SQLite数据导入到MySQL中

之前我们默认使用的是SQLite数据库,我们开发完成之后,里面有许多数据。如果我们想转换成Mysql数据库,那我们先得把旧数据从SQLite导出,然后再导入到新的Mysql数据库里去。

1、SQLite导出数据

导出之前,我们先确保settins.py数据库配置选项那里,还是使用的是SQLite配置,如果已经修改了,请先修改回来:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

然后在CMD命令行里输入:

$ python manage.py dumpdata > data.json

这样就将数据导出到Django项目根目录下的data.json文件。

2、MySQL导入数据
同样,先将Django的数据库配置改为MySQL的:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '你的数据库名',
        'USER': '你的MySQL用户名',
        'PASSWORD': '你的密码',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

然后在CMD命令行里输入:

$ python manage.py loaddata data.json

注意:

确保Mysql用户表里为空的,如果之前有迁移过数据到Mysql,有可能会出错。注意出错的时候所报的错误信息。如果提示有重复主键,那需要先删掉数据。这些数据是在给MySQL数据库应用迁移文件的时候产生的,一般是content_type相关的表。

进入到MySQL,执行如下的SQL语句:

mysql> use 你的数据库名;
mysql> delete from auth_permission;
mysql> delete from django_content_type;

删除数据之后,再执行一次导入命令即可。基本上,导入不了数据都是MySQL存在数据导致的。

加载时区表

另外,有可能所安装的MySQL没有加载时区表。这个可能会导致filter对日期的查询有问题。Django官方文档也指出这个问题。MySQL官网也有对应处理方法:加载时区表。

Linux/Mac解决方法都很简单。windows系统要先下载一个sql文件:timezone_2018e_posix_sql.zip。

下载完成之后,解压得到一个sql文件,再执行cmd命令导入该文件即可:

$ mysql -u root -p mysql < timezone_posix.sql

MySQL数据到PostgreSQL

操作很简单:

$ ./manage.py dumpdata >> backup.json

到PostgreSQL对应的配置中:

$ ./manage.py loaddata backup.json

添加日志功能

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s %(levelname)-8s %(message)s'
        },
        'detail': {
            'format': '%(asctime)s %(levelname)-8s %(pathname)s[line:%(lineno)d] %(message)s'
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'standard',
        },
        'file': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/django.log',
            'maxBytes': 1024 * 1024 * 5,  # 5 MB
            'backupCount': 100,
            'formatter': 'detail',
        },
        'app1_file': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/app1.log',
            'maxBytes': 1024 * 1024 * 5,  # 5 MB
            'backupCount': 100,
            'formatter': 'detail',
        },
        'app2_file': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/app2.log',
            'maxBytes': 1024 * 1024 * 5,  # 5 MB
            'backupCount': 100,
            'formatter': 'detail',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'level': 'INFO',
            'propagate': True,
        },
        # 自定义模块日志
        'users': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': True,
        },
        'common': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': True,
        },
        'myapp': {
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': True,
        },
        'app1': {
            'handlers': ['console', 'app1_file'],
            'level': 'INFO',
            'propagate': True,
        },
        'pushdata': {
            'handlers': ['console', 'app2_file'],
            'level': 'INFO',
            'propagate': True,
        },

    },
}

此配置分成三个部分:

  • formatters: 指定输出的格式,被handler使用。
  • handlers: 指定输出到控制台还是文件中,以及输出的方式。被logger引用。
  • loggers: 指定django中的每个模块使用哪个handlers。以及日志输出的级别。

注意:日志的输出级别是由loggers中的每个模块中level选项定义。如果没有配置,那么默认为warning级别。

然后在每个模块的views.py中,通过下面代码使用

import logging
# 得到 django 的日志对象,一般用于全局的日志记录
logger = logging.getLogger('django')

# 得到 app1 的日志对象,仅用于对 app1 进行日志记录
app1_logger = logging.getLogger('app1')

具体使用

logger.debug("hello django")
logger.info("hello django")
logger.error("hello django")
app1_logger.debug("hello django")
app1_logger.info("hello django")
app1_logger.error("hello django")

在 Django 外部使用 其配置信息

就是可以在不运行 Django 的情况下,在其他的文件中使用 Django 的配置信息。

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "YuLong.settings")
import django
django.setup()

你可能感兴趣的:(Django1.11 项目应用模块)