读书:《精通Django 3 Web开发》

manage.py:命令行工具,内置多种方式与项目进行交互。

asgi.py:用于启动异步通信服务

settings.py:项目的配置文件

urls.py:项目的路由设置,设置网站的具体网址内容

wsgi.py:全称为Python Web Server Gateway Interface,即Python服务器网关接口

创建应用XXX , pythonmanage.py startapp XXX

migrations:用于生成数据迁移文件,通过数据迁移文件可自动在数据库里生成相应的数据表。

apps.py:当前App的配置信息

admin.py:用于设置当前App的后台管理功能。

models.py:定义数据库的映射类,每个类可以关联一张数据表

tests.py:自动化测试的模块

views.py:视图文件,处理功能的业务逻辑

启动项目 python manage.py runserver 8001

内置的应用功能

admin:内置的后台管理系统。

auth:内置的用户认证系统。

contenttypes:记录项目中所有model元数据(Django的ORM框架)。sessions:Session会话功能,用于标识当前访问网站的用户身份,记录相关用户信息。

messages:消息提示功能。

staticfiles:查找静态资源路径。

中间件(Middleware)是一个用来处理Django请求(Request)和响应(Response)的框架级别的钩子,它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。

配置属性MIDDLEWARE的数据格式为列表类型,每个中间件的设置顺序是固定的,如果随意变更中间件很容易导致程序异常

SecurityMiddleware:内置的安全机制,保护用户与网站的通信安全。

SessionMiddleware:会话Session功能。

LocaleMiddleware:国际化和本地化功能。

CommonMiddleware:处理请求信息,规范化请求内容。

CsrfViewMiddleware:开启CSRF防护功能。

AuthenticationMiddleware:开启内置的用户认证系统。

MessageMiddleware:开启内置的信息提示功能。

XFrameOptionsMiddleware:防止恶意程序单击劫持。

静态资源的配置分别由配置属性STATIC_URL、STATICFILES_DIRS和STATIC_ROOT完成

新增数据

get_or_create方法,若存在返回数据表已有的数据信息,否则数据新增

update_or_create方法,若存在,则进行更新操作,否则在数据表里新增数据

数据显示视图、数据操作视图和日期筛选视图。

数据显示视图

RedirectView用于实现HTTP重定向,默认情况下只定义GET请求的处理方法。TemplateView是视图类的基础视图,可将数据传递给HTML模板,默认情况下只定义GET请求的处理方法。ListView是在TemplateView的基础上将数据以列表显示,通常将某个数据表的数据以列表表示。DetailView是在TemplateView的基础上将数据详细显示,通常获取数据表的单条数据。

数据操作视图是对模型进行操作,如增、删、改

FormView视图类使用内置的表单功能,通过表单实现数据验证、响应输出等功能,用于显示表单数据。CreateView实现模型的数据新增功能,通过内置的表单功能实现数据新增。UpdateView实现模型的数据修改功能,通过内置的表单功能实现数据修改。DeleteView实现模型的数据删除功能,通过内置的表单功能实现数据删除。

日期筛选视图是根据模型里的某个日期字段进行数据筛选的,然后将符合结果的数据以一定的形式显示在网页上。

Django模板引擎的解析过程

使用CSRF防护功能,首先在配置文件settings.py中设置CSRF防护功能。

表单设有隐藏控件csrfmiddlewaretoken,隐藏控件是由模板语法{% csrf_token %}生成的

取消表单的CSRF防护,那么可以在模板文件上删除{% csrf_token %},并且在对应的视图函数中添加装饰器@csrf_exempt

取消整个网站的CSRF防护,那么可以在settings.py的MIDDLEWARE注释的CSRF中间件CsrfViewMiddleware

对某些请求设置CSRF防护,那么可以在模板文件上添加模板语法{%csrf_token %},然后在对应的视图函数中添加装饰器@csrf_protect实现

创建static文件夹 python manage.py collectstatic

如果将Django设为调试模式(DEBUG=True),那么项目运行时将读取项目的pstatic文件夹的静态资源。

如果将Django设为调试模式(DEBUG=True),那么项目运行时将读取项目的pstatic文件夹的静态资源。

Django数据导入导出神器django-import-export使用教程

顾名思义,django-import-export是一个用于处理导入和导出数据的库。它支持多种格式,包括xls、csv、json、yaml以及tablib支持的所有其他格式。它还可以轻松与Django管理后台集成,使用起来非常方便。

安装插件

使用PIP安装

pip install django-import-export

更新settings.py:

INSTALLED_APPS = (
    ...
    'import_export',
)

还有一个可选的配置,我通常这样添加:

IMPORT_EXPORT_USE_TRANSACTIONS = True

默认值为False。它确定库是否会在数据导入中使用数据库事务,以确保安全。

编写资源Resources

django-import-export库使用Resource的概念,它的类定义非常类似于Django处理模型表单和管理类的方式。

在文档中,作者建议将与资源相关的代码放在admin.py文件。但是,如果实现与Django admin没有关系,我通常更喜欢在app文件夹里创建一个名为resources.py。

models.py

from django.db import models
class Person(models.Model):    name = models.CharField(max_length=30)    email = models.EmailField(blank=True)    birth_date = models.DateField()    location = models.CharField(max_length=100, blank=True)

resources.py

from import_export import resourcesfrom .models import Person
class PersonResource(resources.ModelResource):    class Meta:        model = Person

这是最简单的定义。您可以将几个配置传递给元类,fieldsexclude

导出数据

导出数据到CSV

from .resources import PersonResource
person_resource = PersonResource()
dataset = person_resource.export()
dataset.csv
id,name,email,birth_date,location
1,John,john@doe.com,2016-08-11,Helsinki
2,Peter,peter@example.com,2016-08-11,Helsinki
3,Maria,maria@gmail.com,2016-08-11,Barcelona
4,Vitor,vitor@freitas.com,2016-08-11,Oulu
5,Erica,erica@gmail.com,2016-08-11,Oulu

导出数据到JSON

dataset.json
[
  {"id"1"name""John""email""john@doe.com""birth_date""2016-08-11""location""Helsinki"},
  {"id"2"name""Peter""email""peter@example.com""birth_date""2016-08-11""location""Helsinki"},
  {"id"3"name""Maria""email""maria@gmail.com""birth_date""2016-08-11""location""Barcelona"},
  {"id"4"name""Vitor""email""vitor@freitas.com""birth_date""2016-08-11""location""Oulu"},
  {"id"5"name""Erica""email""erica@gmail.com""birth_date""2016-08-11""location""Oulu"}
]

导出数据到YAML

dataset.yaml
- {birth_date'2016-08-11', email: john@doe.com, id: 1, location: Helsinki, name: John}
- {birth_date'2016-08-11', email: peter@example.com, id: 2, location: Helsinki, name: Peter}
- {birth_date'2016-08-11', email: maria@gmail.com, id: 3, location: Barcelona, name: Maria}
- {birth_date'2016-08-11', email: vitor@freitas.com, id: 4, location: Oulu, name: Vitor}
- {birth_date'2016-08-11', email: erica@gmail.com, id: 5, location: Oulu, name: Erica}

过滤数据

from .resources import PersonResource
from .models import Person
person_resource = PersonResource()
queryset = Person.objects.filter(location='Helsinki')
dataset = person_resource.export(queryset)
dataset.yaml
- {birth_date'2016-08-11', email: john@doe.com, id: 1, location: Helsinki, name: John}
- {birth_date'2016-08-11', email: peter@example.com, id: 2, location: Helsinki, name: Peter}

导出到CSV视图

from django.http import HttpResponsefrom .resources import PersonResource
def export(request):    person_resource = PersonResource()    dataset = person_resource.export()    response = HttpResponse(dataset.csv, content_type='text/csv')    response['Content-Disposition'] = 'attachment; filename="persons.csv"'    return response

导出到Excel视图

from django.http import HttpResponsefrom .resources import PersonResource
def export(request):    person_resource = PersonResource()    dataset = person_resource.export()    response = HttpResponse(dataset.xls, content_type='application/vnd.ms-excel')    response['Content-Disposition'] = 'attachment; filename="persons.xls"'    return response

导入数据

查看new_persons.csv的数据

name,email,birth_date,location,id
Jessica,jessica@jones.com,2016-08-11,New York,
Mikko,mikko@suomi.com,2016-08-11,Jyväskyla,

id必须存在,因为它是主键。但是它会生成,所以我们不需要指定值。

import.html

{% extends 'base.html' %}
{% block content %}
  <form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="file" name="myfile">
    <button type="submit">Upload</button>
  </form>
{% endblock %}

views.py

from tablib import Datasetdef simple_upload(request):    if request.method == 'POST':        person_resource = PersonResource()        dataset = Dataset()        new_persons = request.FILES['myfile']        imported_data = dataset.load(new_persons.read())        result = person_resource.import_data(dataset, dry_run=True)  # Test the data impor        if not result.has_errors():            person_resource.import_data(dataset, dry_run=False)  # Actually import now     return render(request, 'core/simple_upload.html')

Django后台管理集成

在admin.py里使用ImportExportModelAdmin,而不是ModelAdmin

from import_export.admin import ImportExportModelAdmin
from django.contrib import admin

from .models import Person

@admin.register(Person)
class PersonAdmin(ImportExportModelAdmin):
   pass

添加之后刷新页面你就会看到导入和导出按钮。

Django数据导入导出神器django-import-export使用教程

在导入现有项目时,导入功能具有良好的差异性:

Django数据导入导出神器django-import-export使用教程

这是一个强大的Django库,你可以用它做更多的事情。比如只想有导出功能,可以使用ExportMixin。

from .models import Personfrom django.contrib import adminfrom import_export.admin import ExportMixin
class PersonAdmin(ExportMixin, admin.ModelAdmin):    resource_class = PersonResource
admin.site.register(Person, BookAdmin)

详细文档地址:https://django-import-export.readthedocs.io/en/latest/

文章来源:Django中文网 作者:夜之舞。

Django+haystack+whoosh+jieba全文检索实现

全文检索主要用在大数据量时多字段模糊检索上能较大的提高检索效率。django实现全文检索功能主要靠haystack框架,而用的最多的全文检索引擎就是whooshjieba主要用于中文分词,whoosh自带的分词是英文的。要实现以上组合的全文检索,首先要安装这些模块:

pip install django-haystackpip install whooshpip install jieba

 

配置haystack框架和whoosh引擎

安装好以上模块后,接下来要到项目的settings.py中添加haystack应用,配置whoosh搜索引擎。

#settings.pyINSTALLED_APPS = [    'django.contrib.admin',    'django.contrib.auth',    'django.contrib.contenttypes',    'django.contrib.sessions',    'django.contrib.messages',    'django.contrib.staticfiles',    'haystack',#全文检索框架    'book',    'user',    'recommend',    'library',    'comment',]
HAYSTACK_CONNECTIONS = {    'default': {        #使用whoosh引擎        'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',        #索引文件路径        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),    }}

接下来到项目的urls.py中添加全文检索的路由。

#项目的urls.pyurlpatterns = [    path('admin/', admin.site.urls),    path('',include('book.urls')),    path('book/', include('book.urls')),    path('user/', include('user.urls')),    path('recommend/', include('recommend.urls')),    path('library/', include('library.urls')),    path('comment/', include('comment.urls')),    path('search/', include('haystack.urls')),#全文检索路由]

然后在要做全文检索的app book下创建search_indexes.py文件,该文件是固定命名,内容如下:

#导入索引from haystack import indexes#导入模型from .models import Book_info#Book_infoIndex是固定格式命名,Book_info是你models.py中的类名class Book_infoIndex(indexes.SearchIndex, indexes.Indexable):    text = indexes.CharField(document=True, use_template=True)
    def get_model(self):        return Book_info
    def index_queryset(self, using=None):        return self.get_model().objects.all()

然后到项目下的templates文件夹中依次创建search/indexes/book/目录。book是你需要使用全文检索的app名,book前面的目录名是固定写法,不能更改。

 

接着在book目录下以”模型名_text.txt“格式创建搜索引擎的索引文件,如book_info_text.txt“,book_infobook app下的models.py中的一个模型类Book_info的小写。创建好txt文件后,在文件中输入要索引的字段。

{{ object.book_name }}{{ object.book_author }}{{ object.book_press }}

上面的内容我设置了Book_info数据库中的book_namebook_authorbook_press三个字段的全文索引。

中文分词设置

接下来设置中文分词,在系统根目录中查找haystack(windows系统查找对象是我的电脑,linux系统使用find / -name haystack),找到C:UserscgAppDataLocalProgramsPythonPython37-32Libsite-packageshaystackbackends python安装包下的这个文件夹,在该文件夹下创建ChineseAnalyzer.py,内容如下:

import jiebafrom whoosh.analysis import Tokenizer, Token
class ChineseTokenizer(Tokenizer):    def __call__(self, value, positions=False, chars=False,                 keeporiginal=False, removestops=True,                 start_pos=0, start_char=0, mode='', **kwargs):        t = Token(positions, chars, removestops=removestops, mode=mode,                  **kwargs)        seglist = jieba.cut(value, cut_all=True)        for w in seglist:            t.original = t.text = w            t.boost = 1.0            if positions:                t.pos = start_pos + value.find(w)            if chars:                t.startchar = start_char + value.find(w)                t.endchar = start_char + value.find(w) + len(w)            yield t
def ChineseAnalyzer():    return ChineseTokenizer()

复制whoosh_backend.py,将名称改为whoosh_cn_backend.py,打开该文件,引入中文分析类:

from .ChineseAnalyzer import ChineseAnalyzer

查找文件中analyzer=StemmingAnalyzer(),将其改为analyzer=ChineseAnalyzer()

完成以上的配置就可以建立索引文件了,在项目终端下输入命令重建索引:

python manage.py rebuild_index

创建好索引文件后通过python manage.py update_index来更新索引文件。

全文索引的使用

更改原来的检索模板文件:

<form action="/search/" method="GET">       <div class="input-group mb-3">              <input type="text" name="q" class="form-control" autocomplete="off" required placeholder="可检索字段-书名/作者/出版社">              <div class="input-group-append">                    <button class="btn btn-info" type="submit">搜索</button>                </div>       </div></form>

上面的action参数对应上文在项目urls.py中设置的路由,代表表单提交到全文检索路由,input输入中的name=q参数是haystack的固定写法,q代表查询的关键词。

 

用户提交检索后,系统将检索词提交给haystack,经过haystack查询后,默认结果返回到项目根目录下templates/search/search.html文件,结果中主要包含以下关键参数:

  • query:查询的关键词。
  • page:当前页的page对象,通过该对象获取查询的数据。
  • paginator:分页对象。

模板中主要代码如下:

<div class="clearfix">    <div class="alert alert-info">        检索到关于:&nbsp;<b>“{{ query }}”</b>&nbsp;的图书,当前第&nbsp;<b>{{ page.number }} </b>&nbsp;页    </div>    {# 遍历检索图书结果 #}    {% for item in page %}    <a href="{% url 'book_detail' %}?ids={{ item.object.book_id }}" style="color:black">        <div class="responsive">            <div class="img">                              <img src="{{ item.object.book_pic }}" alt="" width="300" height="200">                         <div class="desc">{{ item.object.book_name }}</div>                <div class="desc"><span>{{ item.object.book_press }}</span></div>            </div>        </div>    </a>    {% endfor %}</div>
{# 分页 #}  <div class="center">    <ul class="pagination">      {% if page.has_previous %}      <li class="page-item"><a class="page-link" href="?q={{query}}&amp;page={{ page.previous_page_number }}">上一页</a></li>      {% endif %}      {% if page.has_next %}      <li class="page-item"><a class="page-link" href="?q={{query}}&amp;page={{ page.next_page_number }}">下一页</a></li>      {% endif %}    </ul>  </div>

需要注意的是:通过page对象遍历获取的对象属性,需要在中间增加object,否则获取不到对象的属性。检索结果如下:

Django+haystack+whoosh+jieba全文检索实现

作者:libdream
链接:
https://www.jianshu.com/p/31646c304cb4
来源:
简书

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)


作者:大江狗

来源:Python Web与Django开发

大家好,我是猫哥。今天分享一篇文章,作者利用Django 3.0 +Redis 3.4 +Celery 4.4开发了个小应用。应用不复杂,但知识点很多,非常适合新手练手。项目需求如下:

  • 创建两个页面,一个用于创建页面,一个用于动态展示页面详情,并提供静态HMTL文件链接

  • 一个页面创建完成后,使用Celery异步执行生成静态HTML文件的任务

  • 使用redis作为Celery的Broker

  • 使用flower监控Celery异步任务执行情况

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

项目完成后演示见下面动画。本项目的GitHub源码地址在最后,请耐心阅读。

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

 

第零步:pip设置国内源

国内用户使用pip安装python包特别慢,这主要是应为国内连接国外网络不稳定。为加速python包的安装,首先将pip安装源设置为国内的镜像,比如阿里云提供的镜像。

linux系统修改 ~/.pip/pip.conf (没有就创建一个), 内容如下:

[global]index-url = https://mirrors.aliyun.com/pypi/simple/

windows系统直接在user目录中创建一个pip目录,如:C:Usersxxpip,新建文件pip.ini,内容如下:

[global]index-url = http://mirrors.aliyun.com/pypi/simple/

第一步:安装Django并创建项目myproject

使用pip命令安装Django.

 pip install django==3.0.4 # 安装Django,所用版本为3.0.4

使用django-admin startproject myproject创立一个名为myproject的项目

 django-admin startproject myproject

整个项目完整目录机构如下所示, 项目名为myproject, staticpage为app名。

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

第二步:安装redis和项目依赖的第三方包

项目中我们需要使用redis做Celery的中间人(Broker), 所以需要先安装redis数据库。redis网上教程很多,这里就简要带过了。

  • Windows下载地址:https://github.com/MSOpenTech/redis/releases

  • Linux下安装(Ubuntu系统):$ sudo apt-get install redis-server

本项目还需要安装如下依赖包,你可以使用pip命令逐一安装。

 pip install redis==3.4.1
 pip install celery==4.4.2
 pip install eventlet # celery 4.0+版本以后不支持在windows运行,还需额外安装eventlet库

你还可以myproject目录下新建requirements.txt加入所依赖的python包及版本,然后使用pip install -r requirements.txt命令安装所有依赖。本教程所使用的django, redis和celery均为最新版本。

 django==3.0.5
 redis==3.4.1
 celery==4.4.2  
 eventlet # for windows only

第三步:Celery基本配置

  1. 修改settings.py新增celery有关的配置。celery默认也是有自己的配置文件的,名为celeryconfig.py, 但由于管理多个配置文件很麻烦,我们把celery的配置参数也写在django的配置文件里。

 # 配置celery时区,默认时UTC。
 if USE_TZ:
     timezone = TIME_ZONE
 
 # celery配置redis作为broker。redis有16个数据库,编号0~15,这里使用第1个。
 broker_url = 'redis://127.0.0.1:6379/0'
 
 # 设置存储结果的后台
 result_backend = 'redis://127.0.0.1:6379/0'
 
 # 可接受的内容格式
 accept_content = ["json"]
 # 任务序列化数据格式
 task_serializer = "json"
 # 结果序列化数据格式
 result_serializer = "json"
 
 # 可选参数:给某个任务限流
 # task_annotations = {'tasks.my_task': {'rate_limit': '10/s'}}
 
 # 可选参数:给任务设置超时时间。超时立即中止worker
 # task_time_limit = 10 * 60
 
 # 可选参数:给任务设置软超时时间,超时抛出Exception
 # task_soft_time_limit = 10 * 60
 
 # 可选参数:如果使用django_celery_beat进行定时任务
 # beat_scheduler = "django_celery_beat.schedulers:DatabaseScheduler"
 
 # 更多选项见
 # https://docs.celeryproject.org/en/stable/userguide/configuration.html
  1. settings.py同级目录下新建celery.py,添加如下内容:

 # coding:utf-8
 from __future__ import absolute_import, unicode_literals
 import os
 from celery import Celery
 
 # 指定Django默认配置文件模块
 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
 
 # 为我们的项目myproject创建一个Celery实例。这里不指定broker容易出现错误。
 app = Celery('myproject', broker='redis://127.0.0.1:6379/0')
 
 # 这里指定从django的settings.py里读取celery配置
 app.config_from_object('django.conf:settings')
 
 # 自动从所有已注册的django app中加载任务
 app.autodiscover_tasks()
 
 # 用于测试的异步任务
 @app.task(bind=True)
 def debug_task(self):
     print('Request: {0!r}'.format(self.request))
 
 
  1. 打开settings.py同级目录下的__init__.py,添加如下内容, 确保项目启动时即加载Celery实例

 # coding:utf-8
 from __future__ import absolute_import, unicode_literals
 
 # 引入celery实例对象
 from .celery import app as celery_app
 __all__ = ('celery_app',)

网上很多django redis + celery的教程比较老了, 坑很多。比如新版原生的Celery已经支持Django了,不需要再借助什么django-celery和celery-with-redis这种第三方库了, 配置参数名也由大写变成了小写,无需再加CELERY前缀。另外当你通过app = Celery('myproject')创建Celery实例时如果不指定Broker,很容易出现[ERROR/MainProcess] consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//: [Errno 111] Connection refused这个错误。

第四步:启动redis,测试celery是否配置成功

在Django中编写和执行自己的异步任务前,一定要先测试redis和celery是否安装好并配置成功。

首先你要启动redis服务。windows进入redis所在目录,使用redis-server.exe启动redis。Linux下使用./redis-server redis.conf启动,也可修改redis.conf将daemonize设置为yes, 确保守护进程开启。

启动redis服务后,你要先运行python manage.py runserver命令启动Django服务器(无需创建任何app),然后再打开一个终端terminal窗口输入celery命令,启动worker。

 # Linux下测试
 Celery -A myproject worker -l info
 
 # Windows下测试
 Celery -A myproject worker -l info -P eventlet

如果你能看到[tasks]下所列异步任务清单如debug_task,以及最后一句celery@xxxx ready, 说明你的redis和celery都配置好了,可以开始正式工作了。

 
 -------------- celery@DESKTOP-H3IHAKQ v4.4.2 (cliffs)
 --- ***** -----
 -- ******* ---- Windows-10-10.0.18362-SP0 2020-04-24 22:02:38
 
 - *** --- * ---
 - ** ---------- [config]
 - ** ---------- .> app:         myproject:0x456d1f0
 - ** ---------- .> transport:   redis://127.0.0.1:6379/0
 - ** ---------- .> results:     redis://localhost:6379/0
 - *** --- * --- .> concurrency: 4 (eventlet)
   -- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
   --- ***** -----
    -------------- [queues]
                .> celery           exchange=celery(direct) key=celery
 
 
 [tasks]
  . myproject.celery.debug_task
 
 [2020-04-24 22:02:38,484: INFO/MainProcess] Connected to redis://127.0.0.1:6379/0
 [2020-04-24 22:02:38,500: INFO/MainProcess] mingle: searching for neighbors
 [2020-04-24 22:02:39,544: INFO/MainProcess] mingle: all alone
 [2020-04-24 22:02:39,572: INFO/MainProcess] pidbox: Connected to redis://127.0.0.1:6379/0.
 [2020-04-24 22:02:39,578: WARNING/MainProcess] c:usersmissenkapycharmprojectsdjango-static-html-generatorvenvlibsite-packagesceleryfixupsdjango.py:203: UserWarning: Using sett
 ings.DEBUG leads to a memory
            leak, never use this setting in production environments!
  leak, never use this setting in production environments!''')
 [2020-04-24 22:02:39,579: INFO/MainProcess] celery@DESKTOP-H3IHAKQ ready.

第五步:Django中创建新应用staticpage

cd进入myproject文件夹,使用python manage.py startapp staticpage创建一个名为staticpage的app。我们将创建一个简单的Page模型,并编写两个视图(对应两个URLs),一个用于添加页面,一个用于展示页面详情。staticpage目录下我们将要编辑或创建5个.py文件,分别是models.py, urls.py, views.py, forms.py和tasks.py,其中前4个都是标准的Django项目文件,内容如下所示。最后一个tasks.py用于存放我们自己编写的异步任务,稍后我会详细讲解。

 # staticpage/models.py
 from django.db import models
 import os
 from django.conf import settings
 
 class Page(models.Model):
     title = models.CharField(max_length=100, verbose_name="标题")
     body = models.TextField(verbose_name="正文")
 
     def __int__(self):
         return self.title
 
     # 静态文件URL地址,比如/media/html/page_8.html
     def get_static_page_url(self):
         return os.path.join(settings.MEDIA_URL, 'html', 'page_{}.html'.format(self.id))
 
 # staticpage/urls.py
 from django.urls import path, re_path
 from . import views
 
 
 urlpatterns = [
 
     # Create a page 创建页面
     path('', views.page_create, name='page_create'),
 
     # Page detail 展示页面详情。动态URL地址为/page/8/
     re_path(r'^page/(?P<pk>d+)/

page_create视图函数中你可以看到我们在一个page实例存到数据库后调用了generate_static_page函数在后台完成静态HTML页面的生成。如果我们不使用异步的化,我们要等静态HTML文件完全生成后才能跳转到页面详情页面, 这有可能要等好几秒。generate_static_page就是我们自定义的异步任务,代码如下所示。Celery可以自动发现每个Django app下的异步任务,不用担心。

 # staticpage/tasks.py
 
 import os, time
 from django.template.loader import render_to_string
 from django.conf import settings
 from celery import shared_task
 
 @shared_task
 def generate_static_page(page_id, page_title, page_body):
     # 模拟耗时任务,比如写入文件或发送邮件等操作。
     time.sleep(5)
 
     # 获取传递的参数
     page = {'title': page_title, 'body': page_body}
     context = {'page': page, }
 
     # 渲染模板,生成字符串
     content = render_to_string('staticpage/template.html', context)
 
     # 定义生成静态文件所属目录,位于media文件夹下名为html的子文件夹里。如目录不存在,则创建。
     directory = os.path.join(settings.MEDIA_ROOT, "html")
     if not os.path.exists(directory):
         os.makedirs(directory)
 
     # 拼接目标写入文件地址
     static_html_path = os.path.join(directory, 'page_{}.html'.format(page_id))
 
     # 将渲染过的字符串写入目标文件
     with open(static_html_path, 'w', encoding="utf-8") as f:
             f.write(content)
 

本例中我们生成的静态HTML文件位于media文件夹下的html子文件夹里,这样做有两个好处:

  • 与Django的静态文件存储规范保持一致:用户产生的静态文件都放在media文件下,网站本身所依赖的静态文件都放于static文件夹下。

  • 把所有产生的静态文件放在一个目录里与动态文件相分开,利于后续通过nginx部署。

本项目中还用到了3个模板,分别是base.html, detail.html和template.html。base.html和detail.html是没有任何样式的, 仅用于动态显示内容,template.html是用来生成静态文件的模板,是带样式的,这样你就可以很快区分动态页面和静态页面。由于我们后台生成静态文件至少需要5秒钟,我们在detail.html用了点javascript实现等5秒倒计时完成后显示生成的静态HTML文件地址。

3个模板均位于staticpage/templates/staticpage/文件夹下,代码如下所示:

 # base.html
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>添加页面</title>
 </head>
 <body>
      <h2>添加页面</h2>
      <form name="myform"  method="POST" action=".">
          {% csrf_token %}
        {{ form.as_p }}
          <button type="submit">Submit</button>
      </form>
 </body>
 </html>
 
 # detail.html
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>{{ page.title }}</title>
 </head>
 <body>
      <h2>{{ page.title }}</h2>
      <p>{{ page.body }}</p>
 
      <p>倒计时: <span id="Time">5</span></p>
      <p id="static_url" style="display:none;"> <small><a href='{{ page.get_static_page_url }}'>跳转到静态文件</a></small></p>
 
 
 <script>
  //使用匿名函数方法
  function countDown(){
  var time = document.getElementById("Time");
  var p = document.getElementById("static_url");
  //获取到id为time标签中的内容,现进行判断
  if(time.innerHTML == 0){
  //等于0时, 显示静态HTML文件URL
  p.style.display = "block";
  }else{
  time.innerHTML = time.innerHTML-1;
  }
  }
  //1000毫秒调用一次
  window.setInterval("countDown()",1000);
  </script>
 
 </body>
 </html>
 
 # template.html 生成静态文件模板
 {% load static %}
 <html lang="en">
 <head>
 <title>{% block title %}Django文档管理{% endblock %} </title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
 </head>
 
 <body>
 <nav class="navbar navbar-inverse navbar-static-top bs-docs-nav">
 
   <div class="container">
     <div class="navbar-header">
         <button type="button" class="navbar-toggle" >
           <span class="icon-bar"></span>
           <span class="icon-bar"></span>
           <span class="icon-bar"></span>                        
       </button>
         <a class="navbar-brand" href="#"><strong>Django + Celery + Redis异步生成静态文件</strong></a>
      </div>
 
       <div class="collapse navbar-collapse" id="myNavbar">
        <ul class="nav navbar-nav navbar-right">
  {% if request.user.is_authenticated %}
 
           <li class="dropdown">
               <a class="dropdown-toggle btn-green" href="#"><span class="glyphicon glyphicon-user"></span> {{ request.user.username }} <span class="caret"></span></a>
             <ul class="dropdown-menu">
               <li><a  href="#">My Account</a></li>
               <li><a  href="#">Logout</a></li>
             </ul>
           </li>  
          {% else %}  
             <li class="dropdown"><a class="dropdown-toggle btn-green" href="#"><span class="glyphicon glyphicon-user"></span> Sign Up</a></li>
 <li class="dropdown"><a class="dropdown-toggle" href="#" ><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
  {% endif %}
        </ul>
 
     </div>
 
   </div>
 </nav>    
 
  <!-- Page content of course! -->
 <main id="section1" class="container-fluid">
 
 <div class="container">
     <div class="row">
      <div class="col-sm-3 col-hide">
          <ul>
              <li> <a href="{% url 'page_create' %}">添加页面</a> </li>
          </ul>
      </div>
 
      <div class="col-sm-9">
           <h3>{{ page.title }}</h3>
          {{ page.body }}
      </div>
 </div>
 
 </div>
 </main>

 <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
 
 </body>
 </html>

第六步:在Django中注册app并添加app的URLConf

 # 修改myproject/settings.py,添加如下内容
 INSTALLED_APPS = [
     'django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'staticpage',
 ]
 
 # 设置STATIC_URL和STATIC_ROOT
 STATIC_URL = '/static/'
 STATIC_ROOT = os.path.join(BASE_DIR, 'static')
 
 # 设置MEDIA_ROOT和MEDIA_URL
 MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
 MEDIA_URL = '/media/'
 
 # 修改myproject/urls.py,添加如下内容
 from django.contrib import admin
 from django.urls import path, include
 
 from django.conf import settings
 from django.conf.urls.static import static
 
 
 urlpatterns = [
     path('admin/', admin.site.urls),
     path('', include("staticpage.urls")),
 ]
 
 # Django自带服务器默认不支持静态文件,需加入这两行。
 if settings.DEBUG:
     urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
     urlpatterns = urlpatterns + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

第七步:启动Django服务器和并安装Celery异步任务监控工具

如果一切顺利,连续使用如下命令, 即可启动Django测试服务器。打开http://127.0.0.1:8000/即可看到我们项目开头的动画啦。注意:请确保redis和celery已同时开启。

 python manage.py makemigrations
 python manage.py migrate
 python manage.py runserver

如果你要监控异步任务的运行状态(比如是否成功,是否有返回结果), 还可以安装flower这个Celery监控工具。

 pip install flower

安装好后,你有如下两种方式启动服务器。启动服务器后,打开http://localhost:5555即可查看监控情况。

 # 从terminal终端启动, proj为项目名
 $ flower -A proj --port=5555  
 # 从celery启动
 $ celery flower -A proj --address=127.0.0.1 --port=5555

 

监控异步任务还是很重要的,强烈建议安装flower。比如下图中有些任务就失败了。

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

源码地址

https://github.com/shiyunbo/django-static-page-generator-celery-redis

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

送书活动

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

 

本书共13章,主要内容涵盖Python语法及数据分析方法。章主要介绍数据分析的概念,使读者有一个大致的印象,并简单介绍本书频繁使用的Python的5个第三方库。第2章主要做一些准备工作,手把手带读者搭建Python环境,包括Python 3.7.6的安装和pip的安装。第3章介绍Python编程基础。第4章到第7章介绍使用Python进行简单数据分析的基础库,包括NumPy、Pandas和Matplotlib库,并介绍使用正则表达式处理数据的方法。第8章到3章属于进阶内容,但也是Python数据分析的基础,结合机器学习介绍一些常见的用于数据分析的机器学习算法及常用的数学模型。

赠书规则

赠书本数本次共包邮送书 

参与方式:在Python猫读者群抽奖,仅限群友参与。后台发“交流群”,获取入群方式。
开奖时间:2021年5月20日18:00

 

 

, views.page_detail, name=’page_detail’),
 
    ]
 
 # staticpage/views.py
 from django.shortcuts import render, redirect, get_object_or_404
 from django.urls import reverse
 from .forms import PageForm
 from .models import Page
 from .tasks import generate_static_page
 
 def page_create(request):
     if request.method == ‘POST’:
         form = PageForm(request.POST)
         if form.is_valid():
             page = form.save()
             generate_static_page.delay(page.id, page.title, page.body)
             return redirect(reverse(‘page_detail’, args=[str(page.pk)]))
     else:
         form = PageForm()
 
     return render(request, ‘staticpage/base.html’, {‘form’: form})
 
 
 def page_detail(request, pk):
     page = get_object_or_404(Page, id=pk)
     return render(request, ‘staticpage/detail.html’, {‘page’: page})
 
 # staticpage/forms.py
 from django import forms
 from .models import Page
 
 
 class PageForm(forms.ModelForm):
     class Meta:
         model = Page
         exclude = ()

page_create视图函数中你可以看到我们在一个page实例存到数据库后调用了generate_static_page函数在后台完成静态HTML页面的生成。如果我们不使用异步的化,我们要等静态HTML文件完全生成后才能跳转到页面详情页面, 这有可能要等好几秒。generate_static_page就是我们自定义的异步任务,代码如下所示。Celery可以自动发现每个Django app下的异步任务,不用担心。

 

本例中我们生成的静态HTML文件位于media文件夹下的html子文件夹里,这样做有两个好处:

  • 与Django的静态文件存储规范保持一致:用户产生的静态文件都放在media文件下,网站本身所依赖的静态文件都放于static文件夹下。

  • 把所有产生的静态文件放在一个目录里与动态文件相分开,利于后续通过nginx部署。

本项目中还用到了3个模板,分别是base.html, detail.html和template.html。base.html和detail.html是没有任何样式的, 仅用于动态显示内容,template.html是用来生成静态文件的模板,是带样式的,这样你就可以很快区分动态页面和静态页面。由于我们后台生成静态文件至少需要5秒钟,我们在detail.html用了点javascript实现等5秒倒计时完成后显示生成的静态HTML文件地址。

3个模板均位于staticpage/templates/staticpage/文件夹下,代码如下所示:

 

第六步:在Django中注册app并添加app的URLConf

 

第七步:启动Django服务器和并安装Celery异步任务监控工具

如果一切顺利,连续使用如下命令, 即可启动Django测试服务器。打开http://127.0.0.1:8000/即可看到我们项目开头的动画啦。注意:请确保redis和celery已同时开启。

 

如果你要监控异步任务的运行状态(比如是否成功,是否有返回结果), 还可以安装flower这个Celery监控工具。

 

安装好后,你有如下两种方式启动服务器。启动服务器后,打开http://localhost:5555即可查看监控情况。

 

 

监控异步任务还是很重要的,强烈建议安装flower。比如下图中有些任务就失败了。

Django 3.0+Redis 3.4+Celery 4.4 应用开发(附源码)

源码地址

https://github.com/shiyunbo/django-static-page-generator-celery-redis

转自:https://mp.weixin.qq.com/s/krlB0WdHpW1GSJhl2SinKw

Django 实现单点登录(SSO)

来自:CSDN,作者:亓官劼

链接:https://blog.csdn.net/qiguanjiezl/article/details/114435883

SSO简介

单点登录(Single Sign On)功能是一个非常常用的功能,尤其是我们在多个系统之间需要登录同步的时候,例如我们在登录QQ空间后,再去QQ的其他网站,都是默认登录的状态,这就是单点登录。
单点登录有很多种实现方法,这里介绍一个通过共享session的实现方法。实现共享session要做的就是要让多个不同应用共用同一个session,但是session默认的是每个应用一个独立的session和cookie的,所以这里要对session的存储进行配置。
除了默认的session存储,我也可以设置让session存储在文件、缓存或者数据库中。
如果我们让session存储在一个固定位置或者数据库中,然后我们设置各个应用cookie的domain为父域地址即可实现各个cookie的相同,从而时候各个cookie中存储的sessionID一致。

搭建测试环境

下面我们来创建两个空的Django项目来进行演示,SSO1和SSO2,这里采用pycharm直接创建两个Django项目,也可以在命令行中使用django-admin startproject sso来创建,其中sso是创建的项目名称。这里也可以使用两个完全相同的项目,在不同地址启动,但是为了演示效果,这里创建了2个。
Django 实现单点登录(SSO)
创建好两个项目后,我们要给项目写一个模拟的登录,注销的功能。
templates文件夹下创建文件login.html文件。这里直接使用之前写过的登录页面的代码,样式就不加了,在SSO1和SSO2中都加入login.html,具体代码为:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="login_content">
    <div class="page-header" id="page_header">
      <h1>登录<small>Login</small></h1>
    </div>
    <div id="login_form">
        <form method="post">
          <div class="form-group">
            <label for="exampleInputEmail1">Email address</label>
            <input type="input" class="form-control" name="usr" id="exampleInputEmail1" placeholder="username">
          </div>
          <div class="form-group">
            <label for="exampleInputPassword1">密码</label>
            <input type="password" class="form-control" name="password" id="exampleInputPassword1" placeholder="密码">
          </div>
          <div id="login_butt">
              <button type="submit" class="btn btn-default">登录</button>
              <button type="button" class="btn btn-default" onclick="">注册</button>
          </div>
        </form>
    </div>
</div>
</body>
</html>
然后在SSO1文件夹创建一个view.py文件,用来存放视图函数。(这里仅为演示SSO,就不分模块了。)
创建文件后的文件目录为:(SSO2项目一样)
.
├── SSO1
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   ├── view.py
│   └── wsgi.py
├── manage.py
├── templates
│   └── login.html
└── venv
    ├── bin
    ├── include
    ├── lib
    └── pyvenv.cfg

插入一个小BUG

macbook运行环境,pycharm创建的Django应用有时候初始化有个bug,缺少os库,会报错:
Traceback (most recent call last):
  File "/Users/qiguan/Documents/develop_files/python_files/SSO1/manage.py", line 22in <module>
    main()
  File "/Users/qiguan/Documents/develop_files/python_files/SSO1/manage.py", line 18in main
    execute_from_command_line(sys.argv)
  File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/core/management/__init__.py", line 401in execute_from_command_line
    utility.execute()
  File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/core/management/__init__.py", line 345in execute
    settings.INSTALLED_APPS
  File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/conf/__init__.py", line 82in __getattr__
    self._setup(name)
  File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/conf/__init__.py", line 69in _setup
    self._wrapped = Settings(settings_module)
  File "/Users/qiguan/Documents/develop_files/python_files/SSO1/venv/lib/python3.7/site-packages/django/conf/__init__.py", line 170in __init__
    mod = importlib.import_module(self.SETTINGS_MODULE)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/__init__.py", line 127in import_module
    return _bootstrap._gcd_import(name[level:], packagelevel)
  File "<frozen importlib._bootstrap>", line 1006in _gcd_import
  File "<frozen importlib._bootstrap>", line 983in _find_and_load
  File "<frozen importlib._bootstrap>", line 967in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728in exec_module
  File "<frozen importlib._bootstrap>", line 219in _call_with_frames_removed
  File "/Users/qiguan/Documents/develop_files/python_files/SSO1/SSO1/settings.py", line 57in <module>
    'DIRS': [os.path.join(BASE_DIR, 'templates')]
NameError: name 'os' is not defined
如果有这个报错的话,在setting.py中导入os即可:import os
然后我们在两个项目的view.py中写入登录和注销函数:
from django.http import HttpResponse
from django.shortcuts import render, redirect


def login(request):
    if request.method == 'GET':
        if 'usr' in request.session:
            # 如果session中已有信息,则显示
            usr = request.session['usr']
            password = request.session['password']
            return HttpResponse("usr:{},password:{},sessionid:{},cookie:{}".format(usr,password,request.session.session_key,request.COOKIES))
        return render(request,'login.html')
    if request.method == 'POST':
        usr = request.POST['usr']
        password = request.POST['password']
        request.session['usr'] = usr
        request.session['password'] = password
        return HttpResponse(
            "usr:{},password:{},sessionid:{},cookie:{}".format(usr, password, request.session.session_key,
                                                               request.COOKIES))


def logout(request):
    request.session.clear()
    return redirect('/login')

url.py中添加路由信息:

"""SSO1 URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from . import view
urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/',view.login),
    path('logout/',view.logout),
]

Django默认配置了csrf,需要将它注释掉,在settings.py文件中搜csrf,然后注释掉。
修改后的settings.py文件为:
"""
Django settings for SSO1 project.

Generated by 'django-admin startproject' using Django 3.1.7.

For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""

from pathlib import Path
import os

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'o=blc^vzeb1&g*b!si(wtxe44_=i5cv(3jqm2*u2u&7vgj%&=%'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'SSO1.urls'

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',
            ],
        },
    },
]

WSGI_APPLICATION = 'SSO1.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE''django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME''django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME''django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME''django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME''django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/

STATIC_URL = '/static/'

然后分别为两个项目做数据库迁移,创建一些Django项目的基础库:python3 manage.py migrate
两个项目都是同样的配置,这样我们目前两个测试的项目就搭建好了,然后我们分别启动他们在不同的端口。这里我们就直接手动启动了,分别启动在5000和6000端口。
python3 manage.py runserver 127.0.0.1:5000
python3 manage.py runserver 127.0.0.1:7000
启动两个项目:
Django 实现单点登录(SSO)
Django 实现单点登录(SSO)
现在我们分别在浏览器中打开http://127.0.0.1:5000/login/http://127.0.0.1:7000/login/,显示的页面都是登录页面,显示如下:
Django 实现单点登录(SSO)
这时我们在http://127.0.0.1:5000/login/随意输入账户密码点击登录,显示:
usr:123,password:123,sessionid:None,cookie:{'csrftoken''8YPzJbY03sHJUZH6kdFZzr9TkDtdVTKflgDDeIn0wgGC6cAeudcrkXLyIxXBEnzG'}
此时我们进入http://127.0.0.1:7000/login/,发现这个应用中,显示的还是之前的页面,登录没有同步。下面我们来实现我们的SSO,这里的实现方法非常的简单,这里提供2中实现方法:
  • 将session固定存储在同一个文件中,
  • 将session存储在Redis中

将session存储在同一个文件中实现SSO

我们在SSO2文件下创建了一个session文件夹,这个文件夹位置任意,写绝对路径即可。
然后我们在两个项目的settings.py中对cookie和session进行配置
# 设置cookie的domain为父域domain,
# 如果是使用域名,以百度为例,主域名为`www.baidu.com`,旗下各个应用为:'asd.baidu.com'
# 则这里设置为:`.baidu.com`
SESSION_COOKIE_DOMAIN = '127.0.0.1'

# 设置session存储在文件中
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
# 设置存储位置,这里设为绝对路径
SESSION_FILE_PATH = '/Users/qiguan/Documents/develop_files/python_files/SSO2/session'

注意一下,这里配置的都是一样的,但是如果两个项目名称不一样的话,是不能直接将完整的settings.py直接复制到另一个的,因为里面有一些项目的配置,例如ROOT_URLCONF = 'SSO1.urls'WSGI_APPLICATION = 'SSO1.wsgi.application'这些前面的都是项目名,需要主要区分。
此时我们在打开
http://127.0.0.1:5000/login/,输入账号密码,此页面显示:
usr:123,password:123,sessionid:2bs2nx2iq879epxu7au7o1zq63o095v7,cookie:{'sessionid''2bs2nx2iq879epxu7au7o1zq63o095v7''csrftoken''8YPzJbY03sHJUZH6kdFZzr9TkDtdVTKflgDDeIn0wgGC6cAeudcrkXLyIxXBEnzG'}
此时我们在打开http://127.0.0.1:7000/login/,我们直接访问,而不用登录,发现显示同样的内容,即我们使用的是同样的内容,实现了SSO。

使用Redis实现SSO

使用文件系统上实现共享session在小并发系统上不会出现问题,但是并发量大的话,会出现一些问题,所以我们这里再介绍一下使用Redis的实现。
需要自行安装Redis,并且在两个项目使用的Python中安装Django-redis:
pip3 install django-redis
在做好这些之后,修改settings.py文件,将使用文件存储session的配置注释掉,修改为:

# # 设置session存储在文件中
# SESSION_ENGINE = 'django.contrib.sessions.backends.file'
# # 设置存储位置,这里设为绝对路径
# SESSION_FILE_PATH = '/Users/qiguan/Documents/develop_files/python_files/SSO2/session'

# 使用Redis存储session
CACHES = {
    "default": {
        "BACKEND""django_redis.cache.RedisCache",
        "LOCATION""redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS""django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100}
            # "PASSWORD": "123",
        }
    }
}

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
SESSION_COOKIE_AGE = 60 * 5

此时我们再来测试一下两个应用,这时我们先访问一下logout,将session清空,然后访问:http://127.0.0.1:5000/login/,输入账户密码后显示:
usr:123,password:123,sessionid:None,cookie:{'csrftoken''8YPzJbY03sHJUZH6kdFZzr9TkDtdVTKflgDDeIn0wgGC6cAeudcrkXLyIxXBEnzG'}

此时我们访问http://127.0.0.1:7000/login/(不登录),显示同样的usr和password信息。

此时我们的SSO也可以正常实现。

好了,本文就先到这里,大家如有需要,可以根据具体的业务进行实现,这里就不赘述了。等以后有空再写一些Django相关的开发博客。