博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Django打造大型企业官网-项目实战(四)
阅读量:5361 次
发布时间:2019-06-15

本文共 53218 字,大约阅读时间需要 177 分钟。

Django打造大型企业官网-项目实战(四)


 

一、新闻相关功能

 在项目实战三中,我们完成了新闻分类相关的一些功能,现在我们来完成新闻列表、发布新闻、编辑新闻、删除新闻的功能

1、发布新闻/编辑新闻 功能实现

 

 发布新闻、编辑新闻结合了 UEditor 富文本编辑器,关于UEditor编辑器使用,可参考随笔:

 1)HTML前端代码:发布新闻/编辑新闻 共用

 HTML代码 + UEditor 编辑器 + jQuery

{% extends 'crm/base.html' %}{
% block title %} {
% if news %} 编辑新闻 {
% else %} 发布新闻 {
% endif %}{
% endblock %}{
% block head %}{# #} {# #}{% endblock %}{
% block content-header %}

{
% if news %} 编辑新闻 {
% else %} 发布新闻 {
% endif %}

{% endblock %}{
% block content %}
{
% csrf_token %}
{% if news %}
{% else %}
{ % endif %}
{% if news %}
{% else %}
{ % endif %}
{ % if news %}
{ % else %}
{ % endif %}
{% endblock %}{
% block front-js %} {% endblock %}
edit_news.html

 

 2)views:后端代码

  发布新闻:

class AddNews(View):    """新增新闻"""    def get(self, request):        categories = NewsCategory.objects.all()        return render(request, 'crm/edit_news.html', {"categories": categories})    def post(self, request):        news_form = NewsForm(request.POST)        if news_form.is_valid():            # news_form.save()  # 也可以直接用此种方法,如果是更新则不能用            title = news_form.cleaned_data.get('title')            desc = news_form.cleaned_data.get('desc')            thumbnail = news_form.cleaned_data.get('thumbnail')            content = news_form.cleaned_data.get('content')            category = news_form.cleaned_data.get('category')            News.objects.create(title=title, desc=desc, thumbnail=thumbnail, content=content, category=category,                                author=request.user)            return JsonResponse({'status': True, 'message': '恭喜!新闻已发布成功!'})        else:            return JsonResponse({'status': False, 'message': '输入数据有误,新闻无法发布!'})
发布新闻:AddNews

 编辑新闻:

class EditNews(View):    """编辑新闻"""    def get(self, request, news_id):        news = News.objects.get(id=news_id)        categories = NewsCategory.objects.all()        return render(request, 'crm/edit_news.html', {'news': news, 'categories': categories})    def post(self, request, news_id):        news_obj = News.objects.filter(id=news_id).first()        news_form = NewsForm(request.POST, instance=news_obj)        if news_form.is_valid():            title = news_form.cleaned_data.get('title')            desc = news_form.cleaned_data.get('desc')            thumbnail = news_form.cleaned_data.get('thumbnail')            content = news_form.cleaned_data.get('content')            category = news_form.cleaned_data.get('category')            News.objects.filter(id=news_id).update(title=title, desc=desc, thumbnail=thumbnail, content=content,                                              category=category)            return JsonResponse({"status": True, "message": "恭喜!新闻更新成功并完成发布!"})        else:            return JsonResponse({"status": False, "message": "数据更改有误,新闻更新失败!"})
编辑新闻:EditNews

 

 3)forms.py:发布新闻/编辑新闻 共用

from django import formsfrom news.models import Newsclass NewsForm(forms.ModelForm):    """新闻"""    class Meta:   # models中将author设置可为null,pub_time自动添加,因此我们可以不用另加 exclude 字段限制(剔除不需要的字段)        model = News        fields = "__all__"
crm/forms.py

 

 4)urls.py:

from django.urls import path, re_pathfrom crm import viewsurlpatterns = [    path("add_news/", views.AddNews.as_view(), name='add_news'),   # crm 管理后台 新增新闻    path("add_news/upload_file", views.upload_file, name='upload_file'),   # crm 管理后台 新增新闻[上传缩略图]    re_path("edit_news/(\w+)/$", views.EditNews.as_view(), name='edit_news'),   # crm 管理后台 编辑新闻]
urls.py

 

 5)缩略图上传相关js代码已经集成到 edit_news.html 中 ,后端代码如下:

@require_POSTdef upload_file(request):    """新闻缩略图上传"""    file = request.FILES.get('file')    name = file.name    with open(os.path.join(settings.MEDIA_ROOT, 'news', 'images', name), 'wb') as fp:  # 图片保存路径        for chunk in file.chunks():            fp.write(chunk)    url = request.build_absolute_uri(settings.MEDIA_URL+"news/"+"images/"+name)   # url 返回给前端显示    return JsonResponse({"status": True, "url": url})
新增/编辑新闻 - 上传缩略图

 

二、django-rest -framework 实现:首页展示新闻文章篇数,通过点击 '加载更多',异步加载更多文章篇数

 原理:根据前端传递的页码,目前指定一页显示两篇文章,每点击一次 '加载更多' 则加载多一页的文章(即两篇),后端拿到页码,获取相应的文章数据并序列化传回给前端。传统做法:News.objects.all().value()[start:end],获取到的是字典类型(最外层还是QuerySet)对象,可序列化后传递到前端使用。但这种方式碰到带有外键字段的会有所缺陷,即外键字段只能获取到相应的外键id,如外键关联作者,获取到的只有id数值:author_id: 2 ,而外键关联的作者其他数据并不能获取到。

此时,可以使用django-rest -framework 来实现后端数据无损序列化。

 关于django-rest-framework 可参考链接:

1、虚拟环境下安装 django-rest-framework

pip install djangorestframework

 

 2、在settings.py/INSTALLED_APP 中注册:

 记得 makemigrations 、migrate 生成表数据

INSTALLED_APPS = [    '…',    'rest_framework',]

 

 3、使用django-rest-framework 下的serializers 序列化

 后端代码相关

 1)序列化 serializers.py:

# user/serializers.pyfrom rest_framework import serializersfrom users.models import UserProfileclass UserSerializer(serializers.ModelSerializer):    class Meta:        model = UserProfile        fields = ('id', 'mobile', 'username', 'email', 'employee', 'is_active')# news/serializers.pyfrom rest_framework import serializersfrom news.models import News,NewsCategory, Comment, Bannerfrom users.serializers import UserSerializerclass NewsCategorySerializer(serializers.ModelSerializer):    class Meta:        model = NewsCategory        fields = ('id', 'name')class NewsSerializer(serializers.ModelSerializer):    category = NewsCategorySerializer()    author = UserSerializer()    class Meta:        model = News        fields = ('id', 'title', 'desc', 'thumbnail', 'pub_time', 'category', 'author')
序列化:serializers.py

 2)views.py:

from django.http import JsonResponsefrom xfz.settings import ONE_PAGE_NEWS_COUNT, INDEX_CATEGORY_COUNTfrom news.models import News, NewsCategoryfrom news.serializers import NewsSerializerdef news_list(request):    # 通过 p 参数,来指定要获取第几页的数据, 通过新闻分类category_id获取该分类下新闻数据    # p category_id 参数是通过查询字符串的方式传递过来的:/news/list/?p=2&category_id=1    page = int(request.GET.get('p', 1))    # 分类为0是,表示不进行任何分类,直接按照时间倒序排序,在models.py中已经默认新闻数据按倒序排序    category_id = int(request.GET.get("category_id", 0))    start = (page - 1) * ONE_PAGE_NEWS_COUNT    end = start + ONE_PAGE_NEWS_COUNT    if category_id == 0:        # 不考虑新闻分类限制        newses = News.objects.select_related('category', 'author').all()[start:end]    else:        newses = News.objects.select_related('category', 'author').filter(category__id=category_id)[start:end]    serializer = NewsSerializer(newses, many=True)    data = serializer.data  # 序列化后数据保存在serializers.data中    return JsonResponse({
"status": True, "data": data}) # 转换成json格式
View Code

 3)urls.py:

from django.urls import pathfrom . import viewsurlpatterns = [    path("news_list/", views.news_list, name='news_list'),    # 新闻列表]
View Code

 4)前端html代码:包括功能→  时间日期过滤器、点击'加载更多' 加载更多文章、点击'新闻分类'如'热点' 加载更多热点相关新闻

{% load news_filters %}    
{% block title %}小饭桌{% endblock %}
{
% if request.user.is_authenticated%}
{
% if request.user.is_staff %}
后台管理系统 {
% endif %}
退出
{
% else %}

{
% endif %}
{
% block body %}
{
% block left-content %}
    { % for news in news_list %}
  • { { news.title }}

    { { news.desc }}

    { { news.category }} { { news.pub_time|time_since }} { { news.author }}

  • { % endfor %}
{
% endblock %} {
% block right-wrapper %}
{
% endblock %}
{
% endblock %}
index.html

 

三、django-debug-toolbar 使用介绍 

 django_debug_toolbar 是django的第三方工具包,给django扩展了很多便利的调试功能。包括查看执行的sql语句、db查询次数、request、headers、调试概览等

 界面:

  

 1、虚拟环境下安装:

pip install django_debug_toolbar

 

2、settings.py中配置:

 1)INSTALLED_APPS 注册: 

  需要注意的是,debug_toolbar要放在django.contrib.staticfiles 之后,当然不要理解为紧跟着django.contrib.staticfiles后面,只要在后面即可

INSTALLED_APPS = [    # ...    'django.contrib.staticfiles',    # ...    'debug_toolbar',   # 这个]

 2)Middleware 中间件配置:

MIDDLEWARE_CLASSES = [    # ...    'debug_toolbar.middleware.DebugToolbarMiddleware',    # ...]

  在settings中的MIDDLEWARE配置’debug_toolbar.middleware.DebugToolbarMiddleware’,我们要把django-debug-toolbar这个中间件尽可能配置到最前面,但是,必须要要放在处理编码和响应内容的中间件后面,比如我们要是使用了GZipMiddleware,就要把DebugToolbarMiddleware放在GZipMiddleware后面。 

如下图,我没有使用到处理编码和响应内容的中间件后面,所以直接放在了最前面

 

 3)配置 IP 地址

  我们需要在settings.py文件中配置INTERNAL_IPS,只有访问这里面配置的ip地址时, Debug Toolbar才是展示出来。因为我们一般都是本地开发,所以,直接配置为127.0.0.1就可以了

# settings.pyINTERNAL_IPS = ("127.0.0.1",)

 

3、url路由配置:

urlpatterns = [    "…"]if settings.DEBUG:  # 只有在debug 模式下,才能使用django-debug-toolbar    import debug_toolbar    urlpatterns.append(path("__debug__/", include(debug_toolbar.urls)))

简单配置后,我们启动项目,前端界面就会显示一个 DjDt 图标,用于 django-debug-toolbar 调试:

  

点击 DjDT 图标,展开详细信息:

  

4、django-debug-toolbar 面板介绍

  • Versions :代表是哪个django版本
  • Timer : 用来计时的,判断加载当前页面总共花的时间
  • Settings : 读取django中的配置信息
  • Headers : 当前请求头和响应头信息
  • Request: 当前请求的想信息(视图函数,Cookie信息,Session信息等)
  • SQL:查看当前界面执行的SQL语句
  • StaticFiles:当前界面加载的静态文件
  • Templates:当前界面用的模板
  • Cache:缓存信息
  • Signals:信号
  • Logging:当前界面日志信息
  • Redirects:当前界面的重定向信息

 自定义django-debug-toolbar面板:

# settings.pyDEBUG_TOOLBAR_PANELS = [    # 代表是哪个django版本    'debug_toolbar.panels.versions.VersionsPanel',    # 用来计时的,判断加载当前页面总共花的时间    'debug_toolbar.panels.timer.TimerPanel',    # 读取django中的配置信息    'debug_toolbar.panels.settings.SettingsPanel',    # 看到当前请求头和响应头信息    'debug_toolbar.panels.headers.HeadersPanel',    # 当前请求的想信息(视图函数,Cookie信息,Session信息等)    'debug_toolbar.panels.request.RequestPanel',    # 查看SQL语句    'debug_toolbar.panels.sql.SQLPanel',    # 静态文件    'debug_toolbar.panels.staticfiles.StaticFilesPanel',    # 模板文件    'debug_toolbar.panels.templates.TemplatesPanel',    # 缓存    'debug_toolbar.panels.cache.CachePanel',    # 信号    'debug_toolbar.panels.signals.SignalsPanel',    # 日志    'debug_toolbar.panels.logging.LoggingPanel',    # 重定向    'debug_toolbar.panels.redirects.RedirectsPanel',

可通过在settings.py 中自定义 DEBUG_TOOLBAR_PANELS 面板选项,在前端DrDTV面板中展示我们所需要的面板信息 

 5、debug_toolbar 配置项:

 默认为如下选项,不写则按如下默认,可根据需要调整

# settings.pyCONFIG_DEFAULTS = {    # Toolbar options    'DISABLE_PANELS': {
'debug_toolbar.panels.redirects.RedirectsPanel'}, 'INSERT_BEFORE': '', 'JQUERY_URL': '//cdn.bootcss.com/jquery/2.1.4/jquery.min.js', 'RENDER_PANELS': None, 'RESULTS_CACHE_SIZE': 10, 'ROOT_TAG_EXTRA_ATTRS': '', 'SHOW_COLLAPSED': False, 'SHOW_TOOLBAR_CALLBACK': 'debug_toolbar.middleware.show_toolbar', # Panel options 'EXTRA_SIGNALS': [], 'ENABLE_STACKTRACES': True, 'HIDE_IN_STACKTRACES': ( 'socketserver' if six.PY3 else 'SocketServer', 'threading', 'wsgiref', 'debug_toolbar', 'django', ), 'PROFILER_MAX_DEPTH': 10, 'SHOW_TEMPLATE_CONTEXT': True, 'SKIP_TEMPLATE_PREFIXES': ( 'django/forms/widgets/', 'admin/widgets/', ), 'SQL_WARNING_THRESHOLD': 500, # milliseconds
django-debug-toolbar:CONFIG_DEFAULTS

 需要注意的一点是:CONFIG_DEFAULTS 中 'JQUERY_URL': '//cdn.bootcss.com/jquery/2.1.4/jquery.min.js' 指向的是Google 下的 js文件,属国外网(服务器),在国内引用容易造成网络慢或404页面的错误,因此,在使用django-debug-toolbar时,建议将 'JQUERY_URL' 设置指向国内相关js,如:'JQUERY_URL': '//cdn.bootcss.com/jquery/2.1.4/jquery.min.js' , 如果在项目中有引用 jquery.min.js 文件,也可以直接配置为空,本项目中便是这样配置的。

# django-debug-toolbar 配置DEBUG_TOOLBAR_CONFIG = {    'JQUERY_URL': ''}

 


 

四、实现搜索功能

1、普通方式实现搜索功能

 适用于数据不是特别多的场景

后端代码实现:views.py

from django.shortcuts import renderfrom django.db.models import Qfrom courses.models import Coursedef search(request):    """搜索"""    search_field = request.GET.get("q", "")    if search_field:        newses = News.objects.select_related("category", "author").filter(Q(title__icontains=search_field)|Q(content__icontains=search_field))        return render(request, 'search/search.html', locals())    else:        hot_newses = News.objects.select_related("category", "author")[0:4]   #热门推荐        return render(request, 'search/search.html', {
"hot_newses": hot_newses})

 urls.py:

urlpatterns = [    path("search/", search, name='search'),]

前端代码实现:search.html

{% extends 'front_base.html' %}{% block title %}搜索{% endblock %}{% block front-css %}
{% endblock %}{% block left-content %}
{% if newses %}

搜索结果

    {% for news in newses %}
  • { { news.title }}

    { { news.desc }}

    { { news.category.name }} { { news.pub_time|timesince }} { { news.author.username }}

  • {% endfor %}
{% elif hot_newses %}

热门推荐

    {% for hot_news in hot_newses %}
  • { { hot_news.title }}

    { { hot_news.desc }}

    { { hot_news.category.name }} { { hot_news.pub_time|timesince }} { { hot_news.author.username }}

  • {% endfor %}
{% else %}

搜索结果

抱歉ヽ(ー_ー)ノ 没有搜到与 "{

{ search_field }}" 相关的新闻信息~

{% endif %}
{% endblock %}{% block right-wrapper %}
{% endblock %}
search.html

界面:

 


 

2、使用 django-haystack实现全文搜索

 1)django-haystack插件:

  django-haystack 插件,是专门给 django 提供搜索功能的。django-haystack 提供了一个搜索的接口,底层可以根据自己的需求更好搜索引擎。它其实有点类似于 django 中的ORM 插件,提供了一个操作数据库的接口,但是底层具体使用哪个数据库是可以自己设置的。安装方式也非常简单,通过 pip install django-haystack 即可安装

 2)搜索引擎:

  django-haystack 支持的搜索引擎有 Solr 、Elasticsearch 、Whoosh  、Xapian 等,whoosh 是基于纯python 的搜索引擎,检索速度快,集成方便,因此这里我们就选择 Whoosh 来作为 haystack 的搜索引擎。 安装方式同样是通过 pip 安装的:pip install whoosh

 注意:django-haystack 适用于 数据比较多的场景(上千万、上亿数据),如果数据量较少,使用普通的搜索方式就可以了

 3)集成步骤:

  3.1)在项目中安装 django-haystack 、Whoosh

pip install django-haystackpip install whoosh

   3.2)在 INSTALLED_APPS 中注册 django-haystack:

INSTALLED_APPS = [    'haystack',   # django-haystack]

  3.3)在settings.py 中配置相应的搜索引擎:

# 全局搜索django-haystack 配置HAYSTACK_CONNECTIONS = {    'default': {        # 设置 haystack 的搜索引擎        'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',        # 设置索引文件的位置,同时在项目目录中新建 whoosh_index 文件夹        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),    }}

  3.4)在 相应的 app目录下新建一个 search_indexes.py 文件,然后创建索引类。比如要给 news app 创建索引,在news 目录下新建 search_indexes.py 文件 ,里面代码编写:

# news.py/search_indexes.pyfrom haystack import indexesfrom news.models import Newsclass NewsIndex(indexes.SearchIndex, indexes.Indexable):    text = indexes.CharField(document=True, use_template=True)    # 为了方便,必须取名为 text    def get_model(self):        return News    def index_queryset(self, using=None):        return self.get_model().objects.all()

   3.5)索引创建好了,需要指定字段为索引的字段,如本例中使用 news models下的news表 中的 title 、content 作为索引字段 , 步骤:

    在 templates 目录下新建 search 文件夹 → 进入search 目录  ,新建子文件夹:indexes → 进入 indexes 目录,再新建 news子文件夹(对应 news app) → 进入 news 目录下,新建txt文件:news_text.txt (命名规则:appname_索引name)→ 最后,在 news_text.txt 文件中指定索引字段:

# templates/search/indexes/news/news_text.txt{
{ object.title }} # title 为索引字段{
{ object.content }} # content 为索引字段

 

   3.6)urls.py 映射:

urlpatterns = [    path("search/", include("haystack.urls")),]

  3.7)templates/search 文件夹下创建 search.html 模板文件, haystack 会自动的在 templates 文件夹下寻找这个模板文件然后渲染,并且会给这个模板文件传入 page、paginator 、query 等参数。其中 page 和 paginator 分别是django内置的Page类和Paginator类的对象,query 是查询的关键字, 我们可以通过 page.object_list 获取到查找出来的数据。

   从 page.object_list 获取所有 newses 数据 ,但此时的 newses 数据类型并不是QuerySet ,需要 for 循环操作取出 每一个 news 相关数据,再通过 .object 语法获取到我们要的 news 数据 。代码实现如下:

    {
    % for result in page.object_list %} {
    % with result.object as news %} # news = result.object
  • {

    { news.title }}

    {

    { news.desc }}

    {

    { news.category.name }} {
    { news.pub_time|timesince }}
    {
    { news.author.username }}

  • {
    % endwith %} {
    % endfor %}
View Code

 

 

  3.8)上述工作都处理好了,在启动项目 运用搜索功能前,我们需要先进行创建索引指令操作:

# 在 pycharm 下的 manage.py 下执行创建索引指令:rebuild_index# 直接创建索引指令:python manage.py rebuild_index

 

 

 这样,便创建完索引,可以使用 django-haystack搜索功能了


 

   4)使用 jieba 分词替换 Whoosh 默认的分词

   Whoosh 默认是采用正则表达式进行分词的,这对于英文来说是足够了,但是对于中文却支持不好。因此我们需要替换该默认分词为 jieba 分词,jieba分词是中文分词中最好用的免费的分词库,但对英文分词比较不是很友好,要使用 jieba 分词库,需要通过 pip install jieba 进行安装

   安装完成后,我们采取 Whoosh与 jieba 分词相结合的方式来实现适用于英文、中文的搜索。在 news app 目录下创建一个名为 whoosh_cn_backend.py 的文件 ,把下述代码复制粘贴到里面

# whoosh_cn_backend.py# encoding: utf-8from __future__ import absolute_import, division, print_function, unicode_literalsimport jsonimport osimport reimport shutilimport threadingimport warningsfrom django.conf import settingsfrom django.core.exceptions import ImproperlyConfiguredfrom django.utils import sixfrom django.utils.datetime_safe import datetimefrom django.utils.encoding import force_textfrom haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, EmptyResults, log_queryfrom haystack.constants import DJANGO_CT, DJANGO_ID, IDfrom haystack.exceptions import MissingDependency, SearchBackendError, SkipDocumentfrom haystack.inputs import Clean, Exact, PythonData, Rawfrom haystack.models import SearchResultfrom haystack.utils import log as loggingfrom haystack.utils import get_identifier, get_model_ctfrom haystack.utils.app_loading import haystack_get_modeltry:    import whooshexcept ImportError:    raise MissingDependency("The 'whoosh' backend requires the installation of 'Whoosh'. Please refer to the documentation.")# Handle minimum requirement.if not hasattr(whoosh, '__version__') or whoosh.__version__ < (2, 5, 0):    raise MissingDependency("The 'whoosh' backend requires version 2.5.0 or greater.")# Bubble up the correct error.from whoosh import indexfrom whoosh.analysis import StemmingAnalyzerfrom whoosh.fields import ID as WHOOSH_IDfrom whoosh.fields import BOOLEAN, DATETIME, IDLIST, KEYWORD, NGRAM, NGRAMWORDS, NUMERIC, Schema, TEXTfrom whoosh.filedb.filestore import FileStorage, RamStoragefrom whoosh.highlight import highlight as whoosh_highlightfrom whoosh.highlight import ContextFragmenter, HtmlFormatterfrom whoosh.qparser import QueryParserfrom whoosh.searching import ResultsPagefrom whoosh.writing import AsyncWriterDATETIME_REGEX = re.compile('^(?P
\d{4})-(?P
\d{2})-(?P
\d{2})T(?P
\d{2}):(?P
\d{2}):(?P
\d{2})(\.\d{3,6}Z?)?$')LOCALS = threading.local()LOCALS.RAM_STORE = Noneimport jiebafrom whoosh.analysis import Tokenizer, Tokenclass 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 tdef ChineseAnalyzer(): return ChineseTokenizer()class WhooshHtmlFormatter(HtmlFormatter): """ This is a HtmlFormatter simpler than the whoosh.HtmlFormatter. We use it to have consistent results across backends. Specifically, Solr, Xapian and Elasticsearch are using this formatting. """ template = '<%(tag)s>%(t)s
'class WhooshSearchBackend(BaseSearchBackend): # Word reserved by Whoosh for special use. RESERVED_WORDS = ( 'AND', 'NOT', 'OR', 'TO', ) # Characters reserved by Whoosh for special use. # The '\\' must come first, so as not to overwrite the other slash replacements. RESERVED_CHARACTERS = ( '\\', '+', '-', '&&', '||', '!', '(', ')', '{ ', '}', '[', ']', '^', '"', '~', '*', '?', ':', '.', ) def __init__(self, connection_alias, **connection_options): super(WhooshSearchBackend, self).__init__(connection_alias, **connection_options) self.setup_complete = False self.use_file_storage = True self.post_limit = getattr(connection_options, 'POST_LIMIT', 128 * 1024 * 1024) self.path = connection_options.get('PATH') if connection_options.get('STORAGE', 'file') != 'file': self.use_file_storage = False if self.use_file_storage and not self.path: raise ImproperlyConfigured("You must specify a 'PATH' in your settings for connection '%s'." % connection_alias) self.log = logging.getLogger('haystack') def setup(self): """ Defers loading until needed. """ from haystack import connections new_index = False # Make sure the index is there. if self.use_file_storage and not os.path.exists(self.path): os.makedirs(self.path) new_index = True if self.use_file_storage and not os.access(self.path, os.W_OK): raise IOError("The path to your Whoosh index '%s' is not writable for the current user/group." % self.path) if self.use_file_storage: self.storage = FileStorage(self.path) else: global LOCALS if getattr(LOCALS, 'RAM_STORE', None) is None: LOCALS.RAM_STORE = RamStorage() self.storage = LOCALS.RAM_STORE self.content_field_name, self.schema = self.build_schema(connections[self.connection_alias].get_unified_index().all_searchfields()) self.parser = QueryParser(self.content_field_name, schema=self.schema) if new_index is True: self.index = self.storage.create_index(self.schema) else: try: self.index = self.storage.open_index(schema=self.schema) except index.EmptyIndexError: self.index = self.storage.create_index(self.schema) self.setup_complete = True def build_schema(self, fields): schema_fields = { ID: WHOOSH_ID(stored=True, unique=True), DJANGO_CT: WHOOSH_ID(stored=True), DJANGO_ID: WHOOSH_ID(stored=True), } # Grab the number of keys that are hard-coded into Haystack. # We'll use this to (possibly) fail slightly more gracefully later. initial_key_count = len(schema_fields) content_field_name = '' for field_name, field_class in fields.items(): if field_class.is_multivalued: if field_class.indexed is False: schema_fields[field_class.index_fieldname] = IDLIST(stored=True, field_boost=field_class.boost) else: schema_fields[field_class.index_fieldname] = KEYWORD(stored=True, commas=True, scorable=True, field_boost=field_class.boost) elif field_class.field_type in ['date', 'datetime']: schema_fields[field_class.index_fieldname] = DATETIME(stored=field_class.stored, sortable=True) elif field_class.field_type == 'integer': schema_fields[field_class.index_fieldname] = NUMERIC(stored=field_class.stored, numtype=int, field_boost=field_class.boost) elif field_class.field_type == 'float': schema_fields[field_class.index_fieldname] = NUMERIC(stored=field_class.stored, numtype=float, field_boost=field_class.boost) elif field_class.field_type == 'boolean': # Field boost isn't supported on BOOLEAN as of 1.8.2. schema_fields[field_class.index_fieldname] = BOOLEAN(stored=field_class.stored) elif field_class.field_type == 'ngram': schema_fields[field_class.index_fieldname] = NGRAM(minsize=3, maxsize=15, stored=field_class.stored, field_boost=field_class.boost) elif field_class.field_type == 'edge_ngram': schema_fields[field_class.index_fieldname] = NGRAMWORDS(minsize=2, maxsize=15, at='start', stored=field_class.stored, field_boost=field_class.boost) else: schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=ChineseAnalyzer(), field_boost=field_class.boost, sortable=True) if field_class.document is True: content_field_name = field_class.index_fieldname schema_fields[field_class.index_fieldname].spelling = True # Fail more gracefully than relying on the backend to die if no fields # are found. if len(schema_fields) <= initial_key_count: raise SearchBackendError("No fields were found in any search_indexes. Please correct this before attempting to search.") return (content_field_name, Schema(**schema_fields)) def update(self, index, iterable, commit=True): if not self.setup_complete: self.setup() self.index = self.index.refresh() writer = AsyncWriter(self.index) for obj in iterable: try: doc = index.full_prepare(obj) except SkipDocument: self.log.debug(u"Indexing for object `%s` skipped", obj) else: # Really make sure it's unicode, because Whoosh won't have it any # other way. for key in doc: doc[key] = self._from_python(doc[key]) # Document boosts aren't supported in Whoosh 2.5.0+. if 'boost' in doc: del doc['boost'] try: writer.update_document(**doc) except Exception as e: if not self.silently_fail: raise # We'll log the object identifier but won't include the actual object # to avoid the possibility of that generating encoding errors while # processing the log message: self.log.error(u"%s while preparing object for update" % e.__class__.__name__, exc_info=True, extra={ "data": { "index": index, "object": get_identifier(obj)}}) if len(iterable) > 0: # For now, commit no matter what, as we run into locking issues otherwise. writer.commit() def remove(self, obj_or_string, commit=True): if not self.setup_complete: self.setup() self.index = self.index.refresh() whoosh_id = get_identifier(obj_or_string) try: self.index.delete_by_query(q=self.parser.parse(u'%s:"%s"' % (ID, whoosh_id))) except Exception as e: if not self.silently_fail: raise self.log.error("Failed to remove document '%s' from Whoosh: %s", whoosh_id, e, exc_info=True) def clear(self, models=None, commit=True): if not self.setup_complete: self.setup() self.index = self.index.refresh() if models is not None: assert isinstance(models, (list, tuple)) try: if models is None: self.delete_index() else: models_to_delete = [] for model in models: models_to_delete.append(u"%s:%s" % (DJANGO_CT, get_model_ct(model))) self.index.delete_by_query(q=self.parser.parse(u" OR ".join(models_to_delete))) except Exception as e: if not self.silently_fail: raise if models is not None: self.log.error("Failed to clear Whoosh index of models '%s': %s", ','.join(models_to_delete), e, exc_info=True) else: self.log.error("Failed to clear Whoosh index: %s", e, exc_info=True) def delete_index(self): # Per the Whoosh mailing list, if wiping out everything from the index, # it's much more efficient to simply delete the index files. if self.use_file_storage and os.path.exists(self.path): shutil.rmtree(self.path) elif not self.use_file_storage: self.storage.clean() # Recreate everything. self.setup() def optimize(self): if not self.setup_complete: self.setup() self.index = self.index.refresh() self.index.optimize() def calculate_page(self, start_offset=0, end_offset=None): # Prevent against Whoosh throwing an error. Requires an end_offset # greater than 0. if end_offset is not None and end_offset <= 0: end_offset = 1 # Determine the page. page_num = 0 if end_offset is None: end_offset = 1000000 if start_offset is None: start_offset = 0 page_length = end_offset - start_offset if page_length and page_length > 0: page_num = int(start_offset / page_length) # Increment because Whoosh uses 1-based page numbers. page_num += 1 return page_num, page_length @log_query def search(self, query_string, sort_by=None, start_offset=0, end_offset=None, fields='', highlight=False, facets=None, date_facets=None, query_facets=None, narrow_queries=None, spelling_query=None, within=None, dwithin=None, distance_point=None, models=None, limit_to_registered_models=None, result_class=None, **kwargs): if not self.setup_complete: self.setup() # A zero length query should return no results. if len(query_string) == 0: return { 'results': [], 'hits': 0, } query_string = force_text(query_string) # A one-character query (non-wildcard) gets nabbed by a stopwords # filter and should yield zero results. if len(query_string) <= 1 and query_string != u'*': return { 'results': [], 'hits': 0, } reverse = False if sort_by is not None: # Determine if we need to reverse the results and if Whoosh can # handle what it's being asked to sort by. Reversing is an # all-or-nothing action, unfortunately. sort_by_list = [] reverse_counter = 0 for order_by in sort_by: if order_by.startswith('-'): reverse_counter += 1 if reverse_counter and reverse_counter != len(sort_by): raise SearchBackendError("Whoosh requires all order_by fields" " to use the same sort direction") for order_by in sort_by: if order_by.startswith('-'): sort_by_list.append(order_by[1:]) if len(sort_by_list) == 1: reverse = True else: sort_by_list.append(order_by) if len(sort_by_list) == 1: reverse = False sort_by = sort_by_list if facets is not None: warnings.warn("Whoosh does not handle faceting.", Warning, stacklevel=2) if date_facets is not None: warnings.warn("Whoosh does not handle date faceting.", Warning, stacklevel=2) if query_facets is not None: warnings.warn("Whoosh does not handle query faceting.", Warning, stacklevel=2) narrowed_results = None self.index = self.index.refresh() if limit_to_registered_models is None: limit_to_registered_models = getattr(settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True) if models and len(models): model_choices = sorted(get_model_ct(model) for model in models) elif limit_to_registered_models: # Using narrow queries, limit the results to only models handled # with the current routers. model_choices = self.build_models_list() else: model_choices = [] if len(model_choices) > 0: if narrow_queries is None: narrow_queries = set() narrow_queries.add(' OR '.join(['%s:%s' % (DJANGO_CT, rm) for rm in model_choices])) narrow_searcher = None if narrow_queries is not None: # Potentially expensive? I don't see another way to do it in Whoosh... narrow_searcher = self.index.searcher() for nq in narrow_queries: recent_narrowed_results = narrow_searcher.search(self.parser.parse(force_text(nq)), limit=None) if len(recent_narrowed_results) <= 0: return { 'results': [], 'hits': 0, } if narrowed_results: narrowed_results.filter(recent_narrowed_results) else: narrowed_results = recent_narrowed_results self.index = self.index.refresh() if self.index.doc_count(): searcher = self.index.searcher() parsed_query = self.parser.parse(query_string) # In the event of an invalid/stopworded query, recover gracefully. if parsed_query is None: return { 'results': [], 'hits': 0, } page_num, page_length = self.calculate_page(start_offset, end_offset) search_kwargs = { 'pagelen': page_length, 'sortedby': sort_by, 'reverse': reverse, } # Handle the case where the results have been narrowed. if narrowed_results is not None: search_kwargs['filter'] = narrowed_results try: raw_page = searcher.search_page( parsed_query, page_num, **search_kwargs ) except ValueError: if not self.silently_fail: raise return { 'results': [], 'hits': 0, 'spelling_suggestion': None, } # Because as of Whoosh 2.5.1, it will return the wrong page of # results if you request something too high. :( if raw_page.pagenum < page_num: return { 'results': [], 'hits': 0, 'spelling_suggestion': None, } results = self._process_results(raw_page, highlight=highlight, query_string=query_string, spelling_query=spelling_query, result_class=result_class) searcher.close() if hasattr(narrow_searcher, 'close'): narrow_searcher.close() return results else: if self.include_spelling: if spelling_query: spelling_suggestion = self.create_spelling_suggestion(spelling_query) else: spelling_suggestion = self.create_spelling_suggestion(query_string) else: spelling_suggestion = None return { 'results': [], 'hits': 0, 'spelling_suggestion': spelling_suggestion, } def more_like_this(self, model_instance, additional_query_string=None, start_offset=0, end_offset=None, models=None, limit_to_registered_models=None, result_class=None, **kwargs): if not self.setup_complete: self.setup() field_name = self.content_field_name narrow_queries = set() narrowed_results = None self.index = self.index.refresh() if limit_to_registered_models is None: limit_to_registered_models = getattr(settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True) if models and len(models): model_choices = sorted(get_model_ct(model) for model in models) elif limit_to_registered_models: # Using narrow queries, limit the results to only models handled # with the current routers. model_choices = self.build_models_list() else: model_choices = [] if len(model_choices) > 0: if narrow_queries is None: narrow_queries = set() narrow_queries.add(' OR '.join(['%s:%s' % (DJANGO_CT, rm) for rm in model_choices])) if additional_query_string and additional_query_string != '*': narrow_queries.add(additional_query_string) narrow_searcher = None if narrow_queries is not None: # Potentially expensive? I don't see another way to do it in Whoosh... narrow_searcher = self.index.searcher() for nq in narrow_queries: recent_narrowed_results = narrow_searcher.search(self.parser.parse(force_text(nq)), limit=None) if len(recent_narrowed_results) <= 0: return { 'results': [], 'hits': 0, } if narrowed_results: narrowed_results.filter(recent_narrowed_results) else: narrowed_results = recent_narrowed_results page_num, page_length = self.calculate_page(start_offset, end_offset) self.index = self.index.refresh() raw_results = EmptyResults() searcher = None if self.index.doc_count(): query = "%s:%s" % (ID, get_identifier(model_instance)) searcher = self.index.searcher() parsed_query = self.parser.parse(query) results = searcher.search(parsed_query) if len(results): raw_results = results[0].more_like_this(field_name, top=end_offset) # Handle the case where the results have been narrowed. if narrowed_results is not None and hasattr(raw_results, 'filter'): raw_results.filter(narrowed_results) try: raw_page = ResultsPage(raw_results, page_num, page_length) except ValueError: if not self.silently_fail: raise return { 'results': [], 'hits': 0, 'spelling_suggestion': None, } # Because as of Whoosh 2.5.1, it will return the wrong page of # results if you request something too high. :( if raw_page.pagenum < page_num: return { 'results': [], 'hits': 0, 'spelling_suggestion': None, } results = self._process_results(raw_page, result_class=result_class) if searcher: searcher.close() if hasattr(narrow_searcher, 'close'): narrow_searcher.close() return results def _process_results(self, raw_page, highlight=False, query_string='', spelling_query=None, result_class=None): from haystack import connections results = [] # It's important to grab the hits first before slicing. Otherwise, this # can cause pagination failures. hits = len(raw_page) if result_class is None: result_class = SearchResult facets = {} spelling_suggestion = None unified_index = connections[self.connection_alias].get_unified_index() indexed_models = unified_index.get_indexed_models() for doc_offset, raw_result in enumerate(raw_page): score = raw_page.score(doc_offset) or 0 app_label, model_name = raw_result[DJANGO_CT].split('.') additional_fields = {} model = haystack_get_model(app_label, model_name) if model and model in indexed_models: for key, value in raw_result.items(): index = unified_index.get_index(model) string_key = str(key) if string_key in index.fields and hasattr(index.fields[string_key], 'convert'): # Special-cased due to the nature of KEYWORD fields. if index.fields[string_key].is_multivalued: if value is None or len(value) is 0: additional_fields[string_key] = [] else: additional_fields[string_key] = value.split(',') else: additional_fields[string_key] = index.fields[string_key].convert(value) else: additional_fields[string_key] = self._to_python(value) del(additional_fields[DJANGO_CT]) del(additional_fields[DJANGO_ID]) if highlight: sa = StemmingAnalyzer() formatter = WhooshHtmlFormatter('em') terms = [token.text for token in sa(query_string)] whoosh_result = whoosh_highlight( additional_fields.get(self.content_field_name), terms, sa, ContextFragmenter(), formatter ) additional_fields['highlighted'] = { self.content_field_name: [whoosh_result], } result = result_class(app_label, model_name, raw_result[DJANGO_ID], score, **additional_fields) results.append(result) else: hits -= 1 if self.include_spelling: if spelling_query: spelling_suggestion = self.create_spelling_suggestion(spelling_query) else: spelling_suggestion = self.create_spelling_suggestion(query_string) return { 'results': results, 'hits': hits, 'facets': facets, 'spelling_suggestion': spelling_suggestion, } def create_spelling_suggestion(self, query_string): spelling_suggestion = None reader = self.index.reader() corrector = reader.corrector(self.content_field_name) cleaned_query = force_text(query_string) if not query_string: return spelling_suggestion # Clean the string. for rev_word in self.RESERVED_WORDS: cleaned_query = cleaned_query.replace(rev_word, '') for rev_char in self.RESERVED_CHARACTERS: cleaned_query = cleaned_query.replace(rev_char, '') # Break it down. query_words = cleaned_query.split() suggested_words = [] for word in query_words: suggestions = corrector.suggest(word, limit=1) if len(suggestions) > 0: suggested_words.append(suggestions[0]) spelling_suggestion = ' '.join(suggested_words) return spelling_suggestion def _from_python(self, value): """ Converts Python values to a string for Whoosh. Code courtesy of pysolr. """ if hasattr(value, 'strftime'): if not hasattr(value, 'hour'): value = datetime(value.year, value.month, value.day, 0, 0, 0) elif isinstance(value, bool): if value: value = 'true' else: value = 'false' elif isinstance(value, (list, tuple)): value = u','.join([force_text(v) for v in value]) elif isinstance(value, (six.integer_types, float)): # Leave it alone. pass else: value = force_text(value) return value def _to_python(self, value): """ Converts values from Whoosh to native Python values. A port of the same method in pysolr, as they deal with data the same way. """ if value == 'true': return True elif value == 'false': return False if value and isinstance(value, six.string_types): possible_datetime = DATETIME_REGEX.search(value) if possible_datetime: date_values = possible_datetime.groupdict() for dk, dv in date_values.items(): date_values[dk] = int(dv) return datetime(date_values['year'], date_values['month'], date_values['day'], date_values['hour'], date_values['minute'], date_values['second']) try: # Attempt to use json to load the values. converted_value = json.loads(value) # Try to handle most built-in types. if isinstance(converted_value, (list, tuple, set, dict, six.integer_types, float, complex)): return converted_value except: # If it fails (SyntaxError or its ilk) or we don't trust it, # continue on. pass return valueclass WhooshSearchQuery(BaseSearchQuery): def _convert_datetime(self, date): if hasattr(date, 'hour'): return force_text(date.strftime('%Y%m%d%H%M%S')) else: return force_text(date.strftime('%Y%m%d000000')) def clean(self, query_fragment): """ Provides a mechanism for sanitizing user input before presenting the value to the backend. Whoosh 1.X differs here in that you can no longer use a backslash to escape reserved characters. Instead, the whole word should be quoted. """ words = query_fragment.split() cleaned_words = [] for word in words: if word in self.backend.RESERVED_WORDS: word = word.replace(word, word.lower()) for char in self.backend.RESERVED_CHARACTERS: if char in word: word = "'%s'" % word break cleaned_words.append(word) return ' '.join(cleaned_words) def build_query_fragment(self, field, filter_type, value): from haystack import connections query_frag = '' is_datetime = False if not hasattr(value, 'input_type_name'): # Handle when we've got a ``ValuesListQuerySet``... if hasattr(value, 'values_list'): value = list(value) if hasattr(value, 'strftime'): is_datetime = True if isinstance(value, six.string_types) and value != ' ': # It's not an ``InputType``. Assume ``Clean``. value = Clean(value) else: value = PythonData(value) # Prepare the query using the InputType. prepared_value = value.prepare(self) if not isinstance(prepared_value, (set, list, tuple)): # Then convert whatever we get back to what pysolr wants if needed. prepared_value = self.backend._from_python(prepared_value) # 'content' is a special reserved word, much like 'pk' in # Django's ORM layer. It indicates 'no special field'. if field == 'content': index_fieldname = '' else: index_fieldname = u'%s:' % connections[self._using].get_unified_index().get_index_fieldname(field) filter_types = { 'content': '%s', 'contains': '*%s*', 'endswith': "*%s", 'startswith': "%s*", 'exact': '%s', 'gt': "{%s to}", 'gte': "[%s to]", 'lt': "{to %s}", 'lte': "[to %s]", 'fuzzy': u'%s~', } if value.post_process is False: query_frag = prepared_value else: if filter_type in ['content', 'contains', 'startswith', 'endswith', 'fuzzy']: if value.input_type_name == 'exact': query_frag = prepared_value else: # Iterate over terms & incorportate the converted form of each into the query. terms = [] if isinstance(prepared_value, six.string_types): possible_values = prepared_value.split(' ') else: if is_datetime is True: prepared_value = self._convert_datetime(prepared_value) possible_values = [prepared_value] for possible_value in possible_values: terms.append(filter_types[filter_type] % self.backend._from_python(possible_value)) if len(terms) == 1: query_frag = terms[0] else: query_frag = u"(%s)" % " AND ".join(terms) elif filter_type == 'in': in_options = [] for possible_value in prepared_value: is_datetime = False if hasattr(possible_value, 'strftime'): is_datetime = True pv = self.backend._from_python(possible_value) if is_datetime is True: pv = self._convert_datetime(pv) if isinstance(pv, six.string_types) and not is_datetime: in_options.append('"%s"' % pv) else: in_options.append('%s' % pv) query_frag = "(%s)" % " OR ".join(in_options) elif filter_type == 'range': start = self.backend._from_python(prepared_value[0]) end = self.backend._from_python(prepared_value[1]) if hasattr(prepared_value[0], 'strftime'): start = self._convert_datetime(start) if hasattr(prepared_value[1], 'strftime'): end = self._convert_datetime(end) query_frag = u"[%s to %s]" % (start, end) elif filter_type == 'exact': if value.input_type_name == 'exact': query_frag = prepared_value else: prepared_value = Exact(prepared_value).prepare(self) query_frag = filter_types[filter_type] % prepared_value else: if is_datetime is True: prepared_value = self._convert_datetime(prepared_value) query_frag = filter_types[filter_type] % prepared_value if len(query_frag) and not isinstance(value, Raw): if not query_frag.startswith('(') and not query_frag.endswith(')'): query_frag = "(%s)" % query_frag return u"%s%s" % (index_fieldname, query_frag)class WhooshEngine(BaseEngine): backend = WhooshSearchBackend query = WhooshSearchQuery
whoosh_cn_backend.py

   然后,需要在settings.py 下 django-haystack 配置中修改: HAYSTACK_CONNECTIONS

    原:

# 全局搜索django-haystack 配置HAYSTACK_CONNECTIONS = {    'default': {        # 设置 haystack 的搜索引擎        'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',        # 设置索引文件的位置        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),    }}

    新:

# 全局搜索django-haystack 配置HAYSTACK_CONNECTIONS = {    'default': {        # 设置 haystack 的搜索引擎        'ENGINE': 'news.whoosh_cn_backend.WhooshEngine',   # 改        # 设置索引文件的位置        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),    }}

  5)建立索引的方式

  方式1:在项目的根目录下,使用命令:python manage.py rebuild_index 来创建索引

  方式2:在settings中做如下配置 ,这样每次进行数据增删改查后系统都能自动创建索引,不需要每次都手动创建:

# settings.py# 配置后, 增删改查操作后都能自动创建索引HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

 


 

五、权限管理

1、自定义 django 命令  

 在需要创建权限管理相关的 APP 下新建 python 包 ,包名称必须命名为:management  → 接着在 management 包目录下,再新建 python 包 ,命名同样唯一:commands  → 进入 commands 包中,新建 python文件,文件名即为命令名,如初始化分组操作,命名 python 文件名为:initgroup.py ,目录如下所示:

 

在 initgroup.py 中编写代码,实现执行指令:python manage.py initgroup 时,控制台打印 hello world :

from django.core.management.base import BaseCommandclass Command(BaseCommand):    """执行python manage.py initgroup命令时,会执行此类"""    def handle(self, *args, **options):        # 执行initgroup命令时,打印 hello world        self.stdout.write(self.style.SUCCESS("hello world"))


 

2、实现网站分组管理及权限配置

 分组示例:编辑组/财务组/管理员/超级管理员

  代码实现:

from django.core.management.base import BaseCommandfrom django.contrib.auth.models import Group, Permission, ContentTypefrom news.models import News, NewsCategory, Comment, Bannerfrom courses.models import Course, CourseCategory, Teacher, CourseOrderclass Command(BaseCommand):    def handle(self, *args, **options):        # 1. 编辑组:管理新闻/管理课程/管理评论/管理轮播图        edit_content_types = {   # 获取相关models下指定表的 contenttype            ContentType.objects.get_for_model(News),            ContentType.objects.get_for_model(NewsCategory),            ContentType.objects.get_for_model(Banner),            ContentType.objects.get_for_model(Comment),            ContentType.objects.get_for_model(Course),            ContentType.objects.get_for_model(CourseCategory),            ContentType.objects.get_for_model(Teacher),        }        edit_permissions = Permission.objects.filter(content_type__in=edit_content_types)   # 找到相关表的所有权限        editGroup = Group.objects.create(name="编辑组")    # 创建组        editGroup.permissions.set(edit_permissions)     # 将表权限分配给 编辑组        editGroup.save()        self.stdout.write(self.style.SUCCESS("编辑分组完成!"))        # 2. 财务组:管理课程订单        finance_content_types = {  # 获取相关models下指定表的 contenttype            ContentType.objects.get_for_model(CourseOrder),        }        finance_permissions = Permission.objects.filter(content_type__in=finance_content_types)  # 找到相关表的所有权限        financeGroup = Group.objects.create(name="财务组")  # 创建组        financeGroup.permissions.set(finance_permissions)  # 将表权限分配给 编辑组        financeGroup.save()        self.stdout.write(self.style.SUCCESS("财务分组完成!"))        # 3. 管理员:包含编辑组 + 财务组 权限        admin_permissions = edit_permissions.union(finance_permissions)        adminGroup = Group.objects.create(name="管理员")  # 创建组        adminGroup.permissions.set(admin_permissions)  # 将表权限分配给 编辑组        adminGroup.save()        self.stdout.write(self.style.SUCCESS("管理员分组完成!"))        # 4.超级管理员        # is_superuser

 执行命令:

 

 数据库中生成分组数据:

 


 分组完成,实现给用户配置分组权限:

  

后端代码实现:

@require_POSTdef edit_staff(request):    """配置员工权限"""    mobile = request.POST.get("mobile", None)    user = UserProfile.objects.filter(mobile=mobile).first()    if user.is_staff:        group_ids =[]        group_info = request.POST.get("groups", None)        if group_info:   # 从前端获取的权限分组是这样的:1/2/3 ,数字代表的是分组group id            group_list = group_info.split("/")            for group_id in group_list:                if group_id != "":                    group_ids.append(group_id)        if group_ids:            groups = Group.objects.filter(id__in=group_ids)            user.groups.clear()   # 清除旧分组            user.groups.add(*groups)   # 添加新分组            user.save()        else:            user.groups.clear()            user.save()        return JsonResponse({
"status": True, "message": "员工权限配置完成!"}) else: return JsonResponse({
"status": True, "message": "该用户不是员工,无法配置权限!"})

 

 

 

 

 

 

 

 

 

 

 


 

转载于:https://www.cnblogs.com/Eric15/articles/11024591.html

你可能感兴趣的文章
数据库第1,2,3范式学习
查看>>
《Linux内核设计与实现》第四章学习笔记
查看>>
Docker 安装MySQL5.7(三)
查看>>
CF1067C Knights 构造
查看>>
CSS: caption-side 属性
查看>>
CSS3中box-sizing的理解
查看>>
Web.Config文件配置之配置Session变量的生命周期
查看>>
mysql导入source注意点
查看>>
linux下编译安装nginx
查看>>
DLL 导出函数
查看>>
windows超过最大连接数解决命令
查看>>
12个大调都是什么
查看>>
angular、jquery、vue 的区别与联系
查看>>
Intellij idea创建javaWeb以及Servlet简单实现
查看>>
代理网站
查看>>
Open multiple excel files in WebBrowser, only the last one gets activated
查看>>
FFmpeg进行视频帧提取&音频重采样-Process.waitFor()引发的阻塞超时
查看>>
最近邻与K近邻算法思想
查看>>
【VS开发】ATL辅助COM组件开发
查看>>
FlatBuffers In Android
查看>>