【Django快速开发实战】(30~51)使用Django创建一个基础应用:职位管理系统

本文详细介绍了Django项目中的一些关键实践,包括创建数据库管理后台、使用中间件记录请求日志和性能、支持多语言、错误日志上报Sentry、集成Celery实现异步任务以及安全防护措施。通过这些步骤,项目具备了监控、国际化、异步处理和安全功能。
摘要由CSDN通过智能技术生成

30 | 遗留系统集成:为已有系统数据库生成管理后台

流程说明:为已有数据库生成管理后台

在这里插入图片描述

创建项目和应用

  • 创建新项目
django-admin startproject empmanager
  • 创建新应用
django-admin startapp candidates

编辑settings.py的数据库配置

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

生成model类

  • 检查数据库
python manage.py inspectdb
  • 将数据库拷贝到models里面
python manage.py inspectdb>candidates/models.py

调整model和注册到admin

  • 调整model
    删掉auth相关的model。
    外键应用的models要新增related_name。
from django.db import models
from django.contrib.auth.models import User


class Candidate(models.Model):
    userid = models.IntegerField(unique=True, blank=True, null=True)
    username = models.CharField(max_length=135)
    city = models.CharField(max_length=135)
    phone = models.CharField(max_length=135)
    email = models.CharField(max_length=135)
    apply_position = models.CharField(max_length=135)
    born_address = models.CharField(max_length=135)
    gender = models.CharField(max_length=135)
    candidate_remark = models.CharField(max_length=135)
    bachelor_school = models.CharField(max_length=135)
    master_school = models.CharField(max_length=135)
    doctor_school = models.CharField(max_length=135)
    major = models.CharField(max_length=135)
    degree = models.CharField(max_length=135)
    test_score_of_general_ability = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    paper_score = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    first_score = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    first_learning_ability = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    first_professional_competency = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    first_advantage = models.TextField()
    first_disadvantage = models.TextField()
    first_result = models.CharField(max_length=256)
    first_recommend_position = models.CharField(max_length=256)
    first_remark = models.CharField(max_length=135)
    second_score = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    second_learning_ability = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    second_professional_competency = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    second_pursue_of_excellence = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    second_communication_ability = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    second_pressure_score = models.DecimalField(max_digits=10, decimal_places=5, blank=True, null=True)  # max_digits and decimal_places have been guessed, as this database handles decimal fields as float
    second_advantage = models.TextField()
    second_disadvantage = models.TextField()
    second_result = models.CharField(max_length=256)
    second_recommend_position = models.CharField(max_length=256)
    second_remark = models.CharField(max_length=135)
    hr_score = models.CharField(max_length=10)
    hr_responsibility = models.CharField(max_length=10)
    hr_communication_ability = models.CharField(max_length=10)
    hr_logic_ability = models.CharField(max_length=10)
    hr_potential = models.CharField(max_length=10)
    hr_stability = models.CharField(max_length=10)
    hr_advantage = models.TextField()
    hr_disadvantage = models.TextField()
    hr_result = models.CharField(max_length=256)
    hr_remark = models.CharField(max_length=256)
    creator = models.CharField(max_length=256)
    created_date = models.DateTimeField()
    modified_date = models.DateTimeField(blank=True, null=True)
    last_editor = models.CharField(max_length=256)
    first_interviewer_user = models.ForeignKey(User, models.DO_NOTHING, related_name='first_interviewer_user',blank=True, null=True)
    hr_interviewer_user = models.ForeignKey(User, models.DO_NOTHING, related_name='hr_interviewer_user',blank=True, null=True)
    second_interviewer_user = models.ForeignKey(User, models.DO_NOTHING, related_name='second_interviewer_user',blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'candidate'




class JobsJob(models.Model):
    job_type = models.SmallIntegerField()
    job_name = models.CharField(max_length=250)
    job_city = models.SmallIntegerField()
    job_responsibility = models.TextField()
    job_requirement = models.TextField()
    created_date = models.DateTimeField()
    modified_date = models.DateTimeField()
    creator = models.ForeignKey(User, models.DO_NOTHING, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'jobs_job'


class JobsResume(models.Model):
    username = models.CharField(max_length=135)
    city = models.CharField(max_length=135)
    phone = models.CharField(max_length=135)
    email = models.CharField(max_length=135)
    apply_position = models.CharField(max_length=135)
    born_address = models.CharField(max_length=135)
    gender = models.CharField(max_length=135)
    picture = models.CharField(max_length=100)
    attachment = models.CharField(max_length=100)
    bachelor_school = models.CharField(max_length=135)
    master_school = models.CharField(max_length=135)
    doctor_school = models.CharField(max_length=135)
    major = models.CharField(max_length=135)
    degree = models.CharField(max_length=135)
    created_date = models.DateTimeField()
    modified_date = models.DateTimeField()
    candidate_introduction = models.TextField()
    work_experience = models.TextField()
    project_experience = models.TextField()
    applicant = models.ForeignKey(User, models.DO_NOTHING, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'jobs_resume'
  • 注册到admin
from django.contrib import admin

# Register your models here.
from .models import JobsJob,JobsResume,Candidate

admin.site.register(JobsJob)
admin.site.register(JobsResume)
admin.site.register(Candidate)

admin管理后台页面

在这里插入图片描述
在这里插入图片描述

31 | Django的中间件(Middleware)

中间件说明

在这里插入图片描述

32 | 创建请求日志、性能日志记录中间件

创建中间件的流程说明

在这里插入图片描述

定义实现中间件

  • interview/performance.py
import time
import logging


logger = logging.getLogger(__name__)


def performance_logger_middleware(get_response):
    def middleware(request):
        start_time = time.time()
        response = get_response(request)
        duration = time.time() - start_time
        response["X-Page-Duration-ms"] = int(duration * 1000)
        logger.info("%s %s %s", duration, request.path, request.GET.dict() )
        return response

    return middleware

注册middleware到settings中

  • settings\base.py
MIDDLEWARE = [
    'interview.performance.performance_logger_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',
]

配置日志文件路径

  • settings\base.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'simple': { # exact format is not important, this is the minimum information
            'format': '%(asctime)s %(name)-12s %(lineno)d %(levelname)-8s %(message)s',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'simple',
        },

        'mail_admins': { # Add Handler for mail_admins for `warning` and above
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
        },
        'file': {
            #'level': 'INFO',
            'class': 'logging.FileHandler',
            'formatter': 'simple',
            'filename': os.path.join(LOG_DIR, 'recruitment.admin.log'),
        },

        'performance': {
            #'level': 'INFO',
            'class': 'logging.FileHandler',
            'formatter': 'simple',
            'filename': os.path.join(LOG_DIR, 'recruitment.performance.log'),
        },
    },

    'root': {
        'handlers': ['console', 'file'],
        'level': 'INFO',
    },

    'loggers': {
        "django_python3_ldap": {
            "handlers": ["console", "file"],
            "level": "DEBUG",
        },

        "interview.performance": {
            "handlers": ["console", "performance"],
            "level": "INFO",
            "propagate": False,
        },
        'django': {
            "handlers": ["console","file"],
            "level": "INFO",
            'propagate': True,
        },
    },
}

效果呈现

  • 文件
    在这里插入图片描述

  • 终端
    在这里插入图片描述

33 | 在Django中支持多语言

流程说明:使用多语言

在这里插入图片描述
在这里插入图片描述

jobs/models.py使用多语言

from django.utils.translation import gettext_lazy as _

from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
from django.utils.translation import gettext_lazy as _
# Create your models here.

# 候选人学历
DEGREE_TYPE = ((u'本科', u'本科'), (u'硕士', u'硕士'), (u'博士', u'博士'))

JobTypes = [
    (0,"技术类"),
    (1,"产品类"),
    (2,"运营类"),
    (3,"设计类"),
    (4,"市场营销类")
]

Cities = [
    (0,"北京"),
    (1,"上海"),
    (2,"深圳"),
    (3,"杭州"),
    (4,"广州")
]


class Job(models.Model):
    # Translators: 职位实体的翻译
    job_type = models.SmallIntegerField(blank=False, choices=JobTypes, verbose_name=_("职位类别"))
    job_name = models.CharField(max_length=250, blank=False, verbose_name=_("职位名称"))
    job_city = models.SmallIntegerField(choices=Cities, blank=False, verbose_name=_("工作地点"))
    job_responsibility = models.TextField(max_length=1024, verbose_name=_("职位职责"))
    job_requirement = models.TextField(max_length=1024, blank=False, verbose_name=_("职位要求"))
    creator = models.ForeignKey(User, verbose_name=_("创建人"), null=True, on_delete=models.SET_NULL)
    created_date = models.DateTimeField(verbose_name=_("创建日期"), auto_now_add=True)
    modified_date = models.DateTimeField(verbose_name=_("修改日期"), auto_now=True)

    class Meta:
        verbose_name = _('职位')
        verbose_name_plural = _('职位列表')

    def __str__(self):
        return self.job_name




class Resume(models.Model):
    # Translators: 简历实体的翻译
    username = models.CharField(max_length=135, verbose_name=_('姓名'))
    applicant = models.ForeignKey(User, verbose_name=_("申请人"), null=True, on_delete=models.SET_NULL)
    city = models.CharField(max_length=135, verbose_name=_('城市'))
    phone = models.CharField(max_length=135,  verbose_name=_('手机号码'))
    email = models.EmailField(max_length=135, blank=True, verbose_name=_('邮箱'))
    apply_position = models.CharField(max_length=135, blank=True, verbose_name=_('应聘职位'))
    born_address = models.CharField(max_length=135, blank=True, verbose_name=_('生源地'))
    gender = models.CharField(max_length=135, blank=True, verbose_name=_('性别'))
    picture = models.ImageField(upload_to='images/', blank=True, verbose_name=_('个人照片')) 
    attachment = models.FileField(upload_to='file/', blank=True, verbose_name=_('简历附件'))

    # 学校与学历信息
    bachelor_school = models.CharField(max_length=135, blank=True, verbose_name=_('本科学校'))
    master_school = models.CharField(max_length=135, blank=True, verbose_name=_('研究生学校'))
    doctor_school = models.CharField(max_length=135, blank=True, verbose_name=u'博士生学校')
    major = models.CharField(max_length=135, blank=True, verbose_name=_('专业'))
    degree = models.CharField(max_length=135, choices=DEGREE_TYPE, blank=True, verbose_name=_('学历'))
    created_date = models.DateTimeField(verbose_name="创建日期", default=datetime.now)
    modified_date = models.DateTimeField(verbose_name="修改日期", auto_now=True)

    # 候选人自我介绍,工作经历,项目经历
    candidate_introduction = models.TextField(max_length=1024, blank=True, verbose_name=u'自我介绍')
    work_experience = models.TextField(max_length=1024, blank=True, verbose_name=u'工作经历')
    project_experience = models.TextField(max_length=1024, blank=True, verbose_name=u'项目经历')

    class Meta:
        verbose_name = _('简历')
        verbose_name_plural = _('简历列表')
    
    def __str__(self):
        return self.username

jobs/template/models.py使用多语言

使用{% translate “匠果科技开放职位” %}和{% blocktranslate with user_name=user.username %}

<!--jobs/templates/base.html-->

<!--bootstrap start-->
{# Load the tag library #}
{% load bootstrap4 %}

{% load i18n %}

{# Load CSS and JavaScript #}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}

{# Display django.contrib.messages as Bootstrap alerts #}
{% bootstrap_messages %}
<!--bootstrap end-->


<h1 style="margin:auto;width:50%">{% translate "匠果科技开放职位" %}</h1>

<p></p>

{% block header %}
<a href="/" style="text-decoration: none; color:#007bff">{% translate "Homepage" %}</a>
<a href="/joblist" style="text-decoration: none; color:#007bff">{% translate job_list" %}</a>
{% if user.is_authenticated %}
    <a href="/accounts/logout" style="text-decoration: none; color:#007bff">{% translate Logout" %}</a>
{% else %}
    <a href="/accounts/login" style="text-decoration: none; color:#007bff">{% translate Login" %}</a>
{% endif %}
{% if user.is_authenticated %}
    <p>{% blocktranslate with user_name=user.username %} 终于等到你 {{ user_name }}, 期待加入我们,用技术去探索一个新世界{% endblocktranslate %}</p>
{% else %}
    <br>{% translate "欢迎你,期待加入我们,登陆后可以提交简历."%}<br>
{% endif %}
{% endblock %}

{% block content %}
{% endblock %}

生成文本格式的多语言资源文件(.po)

mkdir locale
django-admin makemessages -l zh_HANS -l en

在这里插入图片描述

  • locale\en\LC_MESSAGES\django.po
    msgstr为翻译结果,需要我们自己在翻译成英文(我天真以为可以自动翻译)。
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-12-02 06:55+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. Translators: 职位实体的翻译
#: .\jobs\models.py:29
msgid "职位类别"
msgstr "Position category"

#: .\jobs\models.py:30
msgid "职位名称"
msgstr "Job title"

#: .\jobs\models.py:31
msgid "工作地点"
msgstr "working place"

#: .\jobs\models.py:32
msgid "职位职责"
msgstr "responsibilities"

#: .\jobs\models.py:33
msgid "职位要求"
msgstr "Job Requirements"

#: .\jobs\models.py:34
msgid "创建人"
msgstr "Creator"

#: .\jobs\models.py:35
msgid "Creation date"
msgstr ""

#: .\jobs\models.py:36
msgid "modification date"
msgstr ""

#: .\jobs\models.py:39
msgid "职位"
msgstr "position"

#: .\jobs\models.py:40
msgid "职位列表"
msgstr "Position list"

#. Translators: 简历实体的翻译
#: .\jobs\models.py:50
msgid "姓名"
msgstr "full name"

#: .\jobs\models.py:51
msgid "申请人"
msgstr "applicant"

#: .\jobs\models.py:52
msgid "城市"
msgstr "city"

#: .\jobs\models.py:53
msgid "手机号码"
msgstr "phone number"

#: .\jobs\models.py:54
msgid "邮箱"
msgstr "mailbox"

#: .\jobs\models.py:55
msgid "应聘职位"
msgstr "Position Applied"

#: .\jobs\models.py:56
msgid "生源地"
msgstr "Place of origin"

#: .\jobs\models.py:57
msgid "性别"
msgstr "Gender"

#: .\jobs\models.py:58
msgid "个人照片"
msgstr "Personal photos"

#: .\jobs\models.py:59
msgid "简历附件"
msgstr "Resume attachment"

#: .\jobs\models.py:62
msgid "本科学校"
msgstr "Undergraduate school"

#: .\jobs\models.py:63
msgid "研究生学校"
msgstr "post-graduate schools"

#: .\jobs\models.py:65
msgid "专业"
msgstr "major"

#: .\jobs\models.py:66
msgid "学历"
msgstr "education"

#: .\jobs\models.py:76
msgid "简历"
msgstr "resume"

#: .\jobs\models.py:77
msgid "简历列表"
msgstr "Resume list"

#: .\jobs\templates\base.html:18
msgid "匠果科技开放职位"
msgstr "Open position of Jianguo Technology"

#: .\jobs\templates\base.html:23
msgid "Homepage"
msgstr ""

#: .\jobs\templates\base.html:24
msgid "job_list"
msgstr ""

#: .\jobs\templates\base.html:26
msgid "Logout"
msgstr ""

#: .\jobs\templates\base.html:28
msgid "Login"
msgstr ""

#: .\jobs\templates\base.html:31
#, python-format
msgid " 终于等到你 %(user_name)s, 期待加入我们,用技术去探索一个新世界"
msgstr "Finally, you  %(user_name)s are looking forward to joining us and exploring a new world with technology"

#: .\jobs\templates\base.html:33
msgid "欢迎你,期待加入我们,登陆后可以提交简历."
msgstr "Welcome and look forward to joining us. You can submit your resume after logging in."

#: .\jobs\templates\base.html:51
msgid "Switch"
msgstr ""

#: .\recruitment\urls.py:30
msgid "招聘管理系统"
msgstr "Recruitment management system"

#: .\settings\base.py:115
msgid "Chinese"
msgstr ""

#: .\settings\base.py:116
msgid "English"
msgstr ""


编译生成可以高效使用的二进制文件(.mo)

 django-admin compilemessages

在这里插入图片描述

settings添加多语言配置

  • settings/base.py
MIDDLEWARE = [
...
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',### 多语言中间件
    'django.middleware.common.CommonMiddleware',
...
]


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

LANGUAGES = [
    ('zh-hans', _('Chinese')),
    ('en', _('English')),
]

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = True

LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)

选择语言的功能

  • jobs/template/base.html
<div style="flex: 1; align-content:right;">
<form action="{% url 'set_language' %}" method="post" style="margin-block-end: 0em;">{% csrf_token %}
    <input name="next" type="hidden" value="{{ redirect_to }}">
    <select name="language">
        {% get_current_language as LANGUAGE_CODE %}
        {% get_available_languages as LANGUAGES %}
        {% get_language_info_list for LANGUAGES as languages %}
        {% for language in languages %}
            <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
                {{ language.name_local }} ({{ language.code }})
            </option>
        {% endfor %}
    </select>

	<input type="submit" value={% translate "Switch" %} style="font-size:12;height:20px">
</form>
</div>
</div>
<hr style="margin-top: 0px; ">

recruitment/urls.py

    path('i18n/', include('django.conf.urls.i18n')),

多语言效果

在这里插入图片描述

坑:CommandError: Can’t find msguniq. Make sure you have GNU gettext tools 0.15 or newer installed.

参考文章https://blog.csdn.net/weixin_30836759/article/details/99920251

34 | 错误和异常日志上报:Sentry集成(待研究)

教材

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考文档

https://blog.csdn.net/yangjianrong1985/article/details/106913358

下载链接

https://gitee.com/fqzhang/onpremise

环境需要linux或WSL,否则无法执行

在这里插入图片描述

35 | 错误和异常日志上报:捕获异常上报到Sentry并发送钉钉群通知(待研究)

在这里插入图片描述

performance.py


class PerformanceAndExceptionLoggerMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        start_time = time.time()
        response = self.get_response(request)
        duration = time.time() - start_time
        response["X-Page-Duration-ms"] = int(duration * 1000)
        logger.info("duration:%s url:%s parameters:%s", duration, request.path, request.GET.dict() )

        # Code to be executed for each request/response after
        # the view is called.

        return response

    def process_exception(self, request, exception):
        if exception:
                
            message = "url:{url} ** msg:{error} ````{tb}````".format(
                url = request.build_absolute_uri(),
                error = repr(exception),
                tb = traceback.format_exc()
            )
            
            logger.warning(message)
            
            # send dingtalk message
            dingtalk.send(message)

            # capture exception to sentry:
            capture_exception(exception)
                
        return HttpResponse("Error processing the request, please contact the system administrator.", status=500)

36 | Django安全防护:防止XSS跨站脚本攻击

在这里插入图片描述

简单来说,使用render就可以避免XSS跨站JS脚本攻击。

37 | Django安全防护:CSRF跨站请求伪造和SQL注入攻击

在这里插入图片描述
简单来说,template使用CSRF令牌避免CSRF跨站请求伪造,数据查询不要在使用原始SQL查询时还使用参数就可以避免SQL注入攻击(使用ORM查询数据最安全)。

38 | Django Rest Framework开放API

https://www.django-rest-framework.org/

Installation安装

pip install djangorestframework
pip install markdown       # Markdown support for the browsable API.
pip install django-filter  # Filtering support

settings/base.py:Add ‘rest_framework’ to your INSTALLED_APPS setting.

INSTALLED_APPS = [
    ...
    'rest_framework',
]

settings/base.py:增加REST_FRAMEWORK

REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ]
}

your root urls.py file

from django.contrib.auth.models import User
from rest_framework import routers, serializers, viewsets
from jobs.models import Job


#### rest_framework
# Serializers define the API representation.
class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ['url', 'username', 'email', 'is_staff']

# ViewSets define the view behavior.
class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

class JobSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Job
        fields = '__all__'

class JobViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows groups to be viewed or edited.
    """
    queryset = Job.objects.all()
    serializer_class = JobSerializer

# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'jobs', JobViewSet)
urlpatterns = [
    ...
    # django rest api & api auth (login/logout)
    path('api/', include(router.urls)),
    path('api-auth/', include('rest_framework.urls')),
]

启动应用

在这里插入图片描述

39 | 在Django中使用缓存&Redis的使用

https://django-redis-chs.readthedocs.io/zh_CN/latest/

下载和安装redis

链接
定位到redis解压缩后的文件夹中, 打开命令提示符对话框输入"cmd"。然后在命令窗口输入“redis-server.exe redis.windows.conf”出现如下界面即成功启动。
在这里插入图片描述

启动redis服务

双击redis-server.exe可启动redis服务

安装pip install -i https://pypi.tuna.tsinghua.edu.cn/simple django_redis

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple django_redis

启用中间件

  • settings\base.py
MIDDLEWARE = [
    'interview.performance.performance_logger_middleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',#redis中间件
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',#redis中间件
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

建立CACHES

  • settings\base.py
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            #"PASSWORD":"mysecret",
            "SOCKET_CONNECT_TIMEOUT": 5,  # in seconds
            "SOCKET_TIMEOUT": 5,  # r/w timeout in seconds
        }
    }
}

缓存效果

在这里插入图片描述

目前bug

1.默认缓存10分钟,我还不知道在哪改。
2.目前换用户登录后,后一个用户可以访问到前一个用户的访问信息。需要redis清空缓存,请见下方做法。
https://blog.csdn.net/lthahaha/article/details/118926226
在这里插入图片描述

40 | Django与Celery 集成:Celery的使用

celery文档
中文非官方文档

Installation & Bundles

在这里插入图片描述

task.py脚本 & run_task.py脚本

run_task.py脚本是用来运行task.py脚本。

  • recruitment\celery\task.py
#!coding=utf-8

from celery import Celery

# 第一个参数 是当前脚本的名称,第二个参数 是 broker 服务地址
app = Celery('tasks', backend='redis://127.0.0.1', broker='redis://127.0.0.1')


@app.task
def add(x, y):
    return x + y
  • recruitment\celery\run_task.py
#coding=utf-8

from tasks import add

result = add.delay(4, 4)
print('Is task ready: %s' % result.ready())

run_result = result.get(timeout=1)
print('task result: %s' % run_result)

CMD启动Celery服务

  • 方法一:直接启动。win10上运行celery4.x就会出现bug,Celery ValueError: not enough values to unpack (expected 3, got 0)。
C:\Users\Season\Desktop\4.python网页前后端\Django基础教程\workspace4-recruiting\recruitment【40】Django与Celery 集成:Celery的使用\recruitment\celery>celery -A tasks worker --loglevel=INFO 
C:\Users\Season\Desktop\4.python网页前后端\Django基础教程\workspace4-recruiting\recruitment【40】Django与Celery 集成:Celery的使用\recruitment\celery>python run_task.py

在这里插入图片描述

  • 方法二:pip install eventlet,加入参数-P eventlet再启动。debug成功。
C:\Users\Season\Desktop\4.python网页前后端\Django基础教程\workspace4-recruiting\recruitment【40】Django与Celery 集成:Celery的使用\recruitment\celery>celery -A tasks worker --loglevel=INFO -P eventlet
C:\Users\Season\Desktop\4.python网页前后端\Django基础教程\workspace4-recruiting\recruitment【40】Django与Celery 集成:Celery的使用\recruitment\celery>python run_task.py

在这里插入图片描述

Celery ValueError: not enough values to unpack (expected 3, got 0)的解决方案

https://blog.csdn.net/qq_30242609/article/details/79047660

启动flower

参考链接
Celery提供了一个工具flower,将各个任务的执行情况、各个worker的健康状态进行监控并以可视化的方式展现。

1.安装flower:

pip install flower

2.启动flower
例如启动项目工程下面celery_tasks目录的main.py 异步任务启动函数

flower -A celery_tasks.main --port=5555

我的启动如下

celery -A tasks flower --broker=redis://localhost:6379/0

在这里插入图片描述

3.进入http://localhost:5555即可查看。
在这里插入图片描述
4.可以查看发送的任务详情信息
在这里插入图片描述

41 | Django与Celery集成:异步任务

https://docs.celeryproject.org/en/master/django/first-steps-with-django.html#using-celery-with-django

配置celery:recruitment\celery.py

from __future__ import absolute_import, unicode_literals

import os

from celery import Celery

# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.base')

app = Celery('recruitment')

# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
#   should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')

# Load task modules from all registered Django app configs.
app.autodiscover_tasks()


@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

初始化:recruitment_init_.py

from __future__ import absolute_import, unicode_literals

# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

__all__ = ('celery_app',)

celery配置放在settings/local.py

CELERY_BROKER_URL = 'redis://redis:6379/0'
CELERY_RESULT_BACKEND = 'redis://redis:6379/1'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TASK_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERYD_MAX_TASKS_PER_CHILD = 10
CELERYD_LOG_FILE = os.path.join(BASE_DIR, "logs", "celery_work.log")
CELERYBEAT_LOG_FILE = os.path.join(BASE_DIR, "logs", "celery_beat.log")

发布消息的应用改为异步

  • interview\tasks.py
from __future__ import absolute_import, unicode_literals

from celery import shared_task 
from .dingtalk import send

@shared_task
def send_dingtalk_message(message):
    send(message)
  • interview\admin.py
from .tasks import send_dingtalk_message

# 通知一面面试官面试
def notify_interviewer(modeladmin, request, queryset):
    candidates = ""
    interviewers = ""
    for obj in queryset:
        candidates = obj.username + ";" + candidates
        interviewers = obj.first_interviewer_user.username + ";" + interviewers
    # 这里的消息发送到钉钉, 或者通过 Celery 异步发送到钉钉
    #dingtalk.send ("候选人 %s 进入面试环节,亲爱的面试官,请准备好面试: %s" % (candidates, interviewers) )
    send_dingtalk_message.delay("候选人 %s 进入面试环节,亲爱的面试官,请准备好面试: %s" % (candidates, interviewers) )
    messages.add_message(request, messages.INFO, '已经成功发送面试通知')

启动服务

  • redis缓存服务(双击redis-server.exe)

  • celery异步服务(有bug未启动完成)

celery -A proj worker -l INFO
  • flower监控服务
  • django服务(常规操作)

总结Django与Celery集成:异步任务

在这里插入图片描述

42 | Django与Celery集成:定时任务

我的项目https://blog.csdn.net/m0_46629123/article/details/126495062

安装beat

pip install django-celery-beat

加入APP列表

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

在这里插入图片描述

数据库迁移

python manage.py makemigrations
python manage.py migrate
在这里插入图片描述

intervals填写周期

在这里插入图片描述

crontab填写周期

在这里插入图片描述

periodic tasks填写定期任务

在这里插入图片描述

手动启动beat:

celery -A GAGA beat --scheduler django_celery_beat.schedulers:DatabaseScheduler

在这里插入图片描述

我的成功经验:

https://blog.csdn.net/m0_46629123/article/details/126495062

43 | 文件和图片上传功能

讲义

在这里插入图片描述
在这里插入图片描述

settings.py设定media路径

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

根目录urls.py设定访问路径

在这里插入图片描述
我的例子

    ## 媒体文件夹路由:读取"/static/"的数据
    re_path('media/(?P<path>.*)', serve, {'document_root':settings.MEDIA_ROOT},name='media'),

model.py设定上传upload

  • 吕老师的例子
    picture = models.ImageField(upload_to='images/', blank=True, verbose_name=_('个人照片')) 
    attachment = models.FileField(upload_to='file/', blank=True, verbose_name=_('简历附件'))
  • 我的例子
class reward(models.Model):
    # 奖品
    id = models.AutoField(primary_key=True)
    reward_name = models.CharField(max_length=135, blank=True, null=True, verbose_name=u'奖品名称')
    reward_image = models.ImageField(max_length=135, blank=True, null=True, verbose_name=u'奖品照片',upload_to='static\images')
    reward_score = models.IntegerField(blank=True, null=True,verbose_name=u'奖品积分')
    reward_inventory_total = models.IntegerField(blank=True, null=True, verbose_name=u'奖品入库总量')
    reward_inventory_used = models.IntegerField(blank=True, null=True, verbose_name=u'奖品使用量')
    reward_inventory_left = models.IntegerField(blank=True, null=True, verbose_name=u'奖品剩余量')
    class Meta:
        db_table = u'reward'
        verbose_name = u'奖品'
        verbose_name_plural = u'奖品'

    def __str__(self):
        return self.reward_name


    ## 列表页显示图片
    def image_img(self):
        if not self.reward_image:
            return '无'
        return format_html(
            """<div><img src='{}' style='width:50px;height:50px;' ></div>""",
            self.reward_image.url)

    image_img.short_description = '图片'

admin.py显示上传的图片

    ## 列表页显示图片
    def image_img(self):
        if not self.reward_image:
            return '无'
        return format_html(
            """<div><img src='{}' style='width:50px;height:50px;' ></div>""",
            self.reward_image.url)

    image_img.short_description = '图片'

OSS存储(略)

在这里插入图片描述

44 | 实践中的问题:多数据库路由

教程

在这里插入图片描述

settings.py配置多个数据库

在这里插入图片描述

inspect db命令

# 生成全部表
python manage.py inspectdb --database==running
# 生成指定表
python manage.py inspectdb --database==running area city >models.py

running应用(可选)

INSTALLED_APPS = [
...
    'running',
...
]

注册到admin

from django.contrib import admin
# Register your models here.
from .models import JobsJob,JobsResume,Candidate

admin.site.register(JobsJob)
admin.site.register(JobsResume)
admin.site.register(Candidate)

添加路由

  • settings.py
DATABASE_ROUTERS = ['settings.router.DatabaseRouter']
  • settings/router.py来判断使用default数据库还是running数据库。
# settings/router.py
# database router to multiple database by app label
class DatabaseRouter:
    route_app_labels = {'running'}

    def db_for_read(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return 'running'
        return 'default'

    def db_for_write(self, model, **hints):
        if model._meta.app_label in self.route_app_labels:
            return 'running'
        return 'default'

    def allow_relation(self, obj1, obj2, **hints):
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        遗留数据库中的表不允许迁移
        """
        if app_label in self.route_app_labels:
            return False
        return True

45 | Django之美:3 行代码支持大数据量的关联外键

场景

在这里插入图片描述

自动模糊查询

在这里插入图片描述- models.py

class City(models.Model):
    cityid = models.AutoField(primary_key=True)
    countryid = models.ForeignKey(Country, db_column='countryid', null=True, on_delete=models.SET_NULL)
    areaid = models.PositiveIntegerField(blank=True, null=True)
    provinceid = models.ForeignKey(Province, db_column='provinceid', null=True, on_delete=models.SET_NULL)

    chn_name = models.CharField(max_length=64)
    eng_name = models.CharField(max_length=64, blank=True, null=True)
    sort = models.PositiveIntegerField()

    class Meta:
        managed = False
        db_table = 'city'

    def __str__(self):
        return self.chn_name if self.chn_name else self.eng_name if self.eng_name else ""
  • admin.py
@admin.register(City)
class CityAdmin(ReadOnlyAdmin):
    autocomplete_fields = ['provinceid','countryid',]

多级关联查询django-smart-selects

在这里插入图片描述

46 | Django之美:20行代码实现只读站点ReadOnlyAdmin

场景

在这里插入图片描述

代码

class ReadOnlyAdmin(admin.ModelAdmin):
    readonly_fields = []

    def get_list_display(self, request):
        return [field.name for field in self.model._meta.concrete_fields]

    def get_readonly_fields(self, request, obj=None):
        return list(self.readonly_fields) + \
               [field.name for field in obj._meta.fields] + \
               [field.name for field in obj._meta.many_to_many]

    def has_add_permission(self, request):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

    def has_change_permission(self, request, obj=None):
        return False


@admin.register(Country)
class CountryAdmin(ReadOnlyAdmin):
    search_fields = ('chn_name', 'eng_name',)

效果:字段只读,方法禁用

在这里插入图片描述

47| Django之美:10行代码自动注册所有Model到Admin管理后台(可选)

场景

在这里插入图片描述

  • app/admins.py
    在这里插入图片描述
  • settings.py中将running调到APP最后
```python
INSTALLED_APPS = [
...
    'running',]

更简单的方法

在这里插入图片描述

用sandman2来更更简单地自动定义模型(不能与uwsgi生产环境同时使用)

在这里插入图片描述
在这里插入图片描述

48 | Django之美:Signals信号及其使用场景(待研究)

什么是Signals信号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如何使用Signals信号

在这里插入图片描述
在这里插入图片描述

自定义信号

在这里插入图片描述

49 | Django之美:优雅的架构设计 - CSR架构总结之Celery

Celery 架构

在这里插入图片描述

在这里插入图片描述

Celery 中的核心概念

在这里插入图片描述
在这里插入图片描述

Celery 的跨平台 - 不同语言的客户端/服务器端实现

在这里插入图片描述

Celery 的高可用架构

在这里插入图片描述

50 | Django之美:优雅的架构设计 - CSR架构总结之Sentry&Django Framework

Sentry 架构之美

在这里插入图片描述
在这里插入图片描述

Sentry 中的概念

在这里插入图片描述

Sentry 整体架构

在这里插入图片描述

snuba的作用

在这里插入图片描述

DRF 架构之美

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

51 | Django之美:Django的常用插件

在这里插入图片描述

Simple UI

在这里插入图片描述

INSTALLED_APPS = [
    'simpleui',
.....
]

Django Debug Toolbar

https://django-debug-toolbar.readthedocs.io/en/latest/installation.html
在这里插入图片描述
settings.py设定

"""
Django settings for training_system project.

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

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

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

from pathlib import Path
import os

# Build paths inside the project like this: BASE_DIR / 'subdir'.
#BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = Path(__file__).resolve().parent.parent
#BASE_DIR = Path(__file__).resolve()
print('BASE_DIR',BASE_DIR)
#print('BASE_DIR2',os.path.dirname(os.path.dirname(os.path.abspath(__file__))))


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

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-$z^45$dg5xl!3oo@ma54#n&9x-3p3_*6g^719c80xy*0dww7u='

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

#DEBUG = False
DEBUG = True

if DEBUG == True:
    INTERNAL_IPS = ['127.0.0.1', ]
    DEBUG_TOOLBAR_CONFIG = {
        'JQUERY_URL': 'https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js',
        'SHOW_COLLAPSED': True,
        'SHOW_TOOLBAR_CALLBACK': lambda x: True,
    }


ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'simpleui',
    #'grappelli',
    'bootstrap4',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'training',
    'debug_toolbar',
]

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    #'django.middleware.cache.UpdateCacheMiddleware',#redis中间件
    'django.middleware.common.CommonMiddleware',
    #'django.middleware.cache.FetchFromCacheMiddleware',#redis中间件
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'training_system.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 = 'training_system.wsgi.application'


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

print(Path(__file__).resolve().parent)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        #'NAME': BASE_DIR / 'db.sqlite3',
        'NAME': Path(__file__).resolve().parent /'SQL' /'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.2/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.2/topics/i18n/

# 修改中文,Django内部设置zh_Hans方法指向中文 
LANGUAGE_CODE = 'zh-hans' 

# 修改中国时区
TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

#USE_L10N = True

USE_L10N = False
 
DATETIME_FORMAT = 'Y-m-d H:i:s'
 
DATE_FORMAT = 'Y-m-d'

#USE_TZ = True
USE_TZ = False

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

# 通过url直接访问我在项目中的静态文件
STATIC_URL = '/static/'
# 部署静态文件时(pyhtonmanage.pycollectstatic)所有的静态文静聚合的目录
STATIC_ROOT = os.path.join(BASE_DIR, "/static/")
# STATICFILES_DIRS告诉django,首先到STATICFILES_DIRS里面寻找静态文件,其次再到各个app的static文件夹里面找
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)



# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'



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


LOGIN_URL = '/user_login/'





# CACHES = {
#     "default": {
#         "BACKEND": "django_redis.cache.RedisCache",
#         "LOCATION": "redis://127.0.0.1:6379/1",
#         "OPTIONS": {
#             "CLIENT_CLASS": "django_redis.client.DefaultClient",
#             "PASSWORD":"mysecret",
#             "SOCKET_CONNECT_TIMEOUT": 5,  # in seconds
#             "SOCKET_TIMEOUT": 5,  # r/w timeout in seconds
#         }
#     }
# }

Haystack

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值