Django-ミドルウェア-csrf拡張要求偽造インターセプトモジュールミドルウェア-Django認証 - 機能の効果を実現するためのミドルウェアプラグインで設定をジャンゴ従う-09

昨日、コメントを追加しました:CBVに取り付けられた彼らの書かれたlogin_authを飾ります

メソッド内のクラスは、最初のパラメータがクラスであるか、オブジェクト自体、一般的に結合クラスメソッドであるか、結合方法、オブジェクト、前回の書き込みデコレータが使用するパラメータを変更する必要がありますが、ここではジャンゴで私たちに書き込むことができます良いデコレータは、変更が書かれてからデコレータを必要としません。

三つの方法ガイドモジュールを忘れないでください

from django.utils.decorators import method_decorator

# @method_decorator(login_auth, name='get')  # 第一种, name 参数必须指定
class MyHome(View):
    # @method_decorator(login_auth)  # 第二种, get 和 post 都会被装饰(登录验证)(直接把 dispatch 拿过来,加个装饰器)
    def dispatch(self, request, *args, **kwargs):
        super().dispatch(request, *args, **kwargs)
     
    @method_decorator(login_auth)  # 第三种,直接装饰在单个方法上
    def get(self, request):
        return HttpResponse('get')
    
    def post(self, request):
        return HttpResponse('post')

ジャンゴミドルウェア

ジャンゴジャンゴミドルウェア、ゲートウェイに似てリクエストがDjangoのミドルウェアのバックエンド(URL)を取得するために通過する必要がある場合、ウェブサービスゲートウェイインターフェイス(wsgifモジュール)に到達するためにミドルウェアを通過する時間を取るために対応

ジャンゴミドルウェアは何をすべきかを使用することができます

  • アクセスの頻度を制限し、サイトのグローバルIDチェックを行い、権限が(抗登る)を確認... 限り、それは全体的な検証に関連するほとんどのミドルウェアで行うことができ、それはまた、ミドルウェアの思いは初めてです

Djangoのミドルウェアの設計、より完璧で、論理的な明確な、最も簡単な(それに劣るフラスコミドルウェア)

ポストを提出したすべての要求が一時的にコメントアウトCSRFミドルウェアで、この行くsettings.pyに書き込まれます、なぜ我々の前に終了し、このミドルウェアは知っているだろう

Djangoのリクエストのライフサイクル *****

ミドルウェア後(その後、views.py ...層の進行によって層)urls.pyを入力します

ポピュラーサイエンス:

  1. wsgirefは高い並列性を耐えることができない行がuwsgiモジュール(フロントプラスリバースプロキシ行うnginxの)後に交換されるように、
  • WSGIと、それぞれその意味、WSGIとuwsgi

WSGIは、プロトコル標準、wsgirefとuwsgiはWSGIプロトコル機能モジュールを実現しています

  1. 要求は、第一の中間層に行く入ると、データベース内のキャッシュデータか否かを判断します
  • いずれの場合には、(これは、資源の節約、サーバーとデータベースへの圧力を減らすことができます)、直接データを取得し、要求された返します
  • そうでなければ、我々は層によってミドルウェア層を行く、その後、コンフィギュレーション、views.pyを...ルーティング、および返されたデータがキャッシュデータベースに格納されている間ので、再度、ミドルウェア要求の最後の層に来ましたインチ (次回は、あなたは、キャッシュデータベースのデータに直接取得することができます)

知るためにライン上の概念の観点拡大に関連する特定の原則と他のポスト

デフォルトの方法は、おそらくミドルウェアで構成されており、

7デフォルトのDjangoのミドルウェアがあります。

Djangoは、ユーザー定義の独自のミドルウェアをサポートし、ユーザーに公開、また、ユーザーに5つのカスタムメソッドミドルウェアを公開することができます

# settings.py 里的七个默认中间件
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',
]

ソース・コード・ミドルウェアへの観測点

'''
django.middleware.security.SecurityMiddleware
--> 本质是动态导入(可以看最后面的那个模仿案例) 
# from django.middleware.security import SecurityMiddleware

django.middleware.csrf.CsrfViewMiddleware
--> from django.middleware.csrf import CsrfViewMiddleware
'''

発見ジャンゴミドルウェアは 5つのユーザ定義可能なメソッドを持っています

# django.middleware.csrf.CsrfViewMiddleware  --> from django.middleware.csrf import CsrfViewMiddleware
class CsrfViewMiddleware(MiddlewareMixin):
    def _accept(self, request):
    def _reject(self, request, reason):
    def _get_token(self, request):
    def _set_token(self, request, response):
    def process_request(self, request):
    def process_view(self, request, callback, callback_args, callback_kwargs):
    def process_response(self, request, response):

# django.middleware.security.SecurityMiddleware  --> django.middleware.security.SecurityMiddleware
class SecurityMiddleware(MiddlewareMixin):
    def __init__(self, get_response=None):
    def process_request(self, request):
    def process_response(self, request, response):      

# django.contrib.sessions.middleware.SessionMiddleware
class SessionMiddleware(MiddlewareMixin):
    def __init__(self, get_response=None):
    def process_request(self, request):
    def process_response(self, request, response):
  • 我々は方法を習得する必要があります
  1. PROCESS_DIRECTORYで要求()メソッド
  2. PROCESS_DIRECTORYで応答()メソッド
  • あなたは方法を理解する必要があります
  1. PROCESS_DIRECTORYでビュー()
  2. プロセスの例外()
  3. PROCESS_DIRECTORYでtemplate_応答()

ミドルウェアの実行順序

画像は次の条件によって影響を受ける可能性がほぼ同じDjangoの要求のライフサイクル

カスタム操作は、ミドルウェア実行ミドルウェアの異なる順序の効果を探ります

テストのアイデア:

  • settings.pyに異なるミドルウェアをサインアップし、デフォルトの実行順序問い合わせ
  • 順を実行しますリターンのHttpResponseオブジェクトで異なるミドルウェアprocess一とprocess_response方法への影響は何ですか
  • トリガーのタイミングを理解するための5つの方法

カスタムミドルウェア

  1. (グローバルまたはアプリ内での)新しいフォルダを作成します。
  2. MiddlewareMiXinクラスを継承するクラスを書きます
  3. 内部の(いくつかの方法において5)必要な書き込み方法
  4. settings.pyにミドルウェアを設定するようにしてください

コード

mymiddleware / mdd.pyカスタムミドルウェア

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class MyMdd(MiddlewareMixin):
    def process_request(self, request):
        print('我是第一个中间件里面的process_request方法')

    def process_response(self, request, response):
        print('我是第一个中间件里面的process_response方法')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print(view_func)
        print(view_args)
        print(view_kwargs)
        print('我是第一个中间件里面的process_view方法')

    def process_exception(self, request, exception):
        print('我是第一个中间件里面的process_exception')

    def process_template_response(self, request, response):
        print('我是第一个中间件里面的process_template_response')
        return response


class MyMdd1(MiddlewareMixin):
    def process_request(self, request):
        print('我是第二个中间件里面的process_request方法')

    def process_response(self, request, response):
        print('我是第二个中间件里面的process_response方法')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print(view_func)
        print(view_args)
        print(view_kwargs)
        print('我是第二个中间件里面的process_view方法')

    def process_exception(self, request, exception):
        print('我是第二个中间件里面的process_exception')

    def process_template_response(self, request, response):
        print('我是第二个中间件里面的process_template_response')
        return response


class MyMdd2(MiddlewareMixin):
    def process_request(self, request):
        print('我是第三个中间件里面的process_request方法')

    def process_response(self, request, response):
        print('我是第三个中间件里面的process_response方法')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print(view_func)
        print(view_args)
        print(view_kwargs)
        print('我是第三个中间件里面的process_view方法')

    def process_exception(self, request, exception):
        print('我是第三个中间件里面的process_exception')

    def process_template_response(self, request, response):
        print('我是第三个中间件里面的process_template_response')
        return response

settings.pyでの構成

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',
    'mymiddleware.mdd.MyMdd',  # 配置上
    'mymiddleware.mdd.MyMdd1',  # 配置上
    'mymiddleware.mdd.MyMdd2',  # 配置上
]

方法は知っておく必要があります

process一

要求がする場合は、設定ファイルで実行するミドルウェアで登録のsettings.pyひいてはで

  • この方法は、ミドルウェアの下、スキップされていない場合
  • この方法は、内部のHttpResponseオブジェクトを返した場合、それは、現在のミドルウェアからprocess_responseメソッドから実行リターンを上げますダウン、その後に実行されることはありません
  • 実行順序:上から
  • この方法は、ユーザー権限を確認し、ユーザID認証、アクセス頻度の制限を実装することができ...

あなたは、周波数特性に基づいてアクセスを制限するために行うことができます

process_response

応答するために時間をかけて(応答がデータの先頭に戻って参照することがあるので、パラメータ応答が返されなければならない)のミドルウェアメソッドに登録ターンsettings.py構成ファイルで実行されます

  • この方法は、ミドルウェアの下、スキップされていない場合
  • 実行順序:下からアップ
  • この方法では、キャッシングメカニズム(サーバー上の遅い圧力、データベース)を実装することができます

あなたは方法を理解する必要があります

process_view

ビュー機能の実装を成功さをルーティングする前にマッチ自動的には(上から下の実行に)トリガー

process_exception

場合ビューが機能を与えられ、自動的に(底部から上部実行に)トリガー

process_template_response

ビュー機能を含むのHttpResponseオブジェクトは、属性がレンダリング返す場合、またはオブジェクトまたはTemplateResponse時間をオブジェクトが(下から上に行われる)同等の方法をトリガするときを示すトリガ

def index(request):
    print("我是 index 视图函数")
    def render():
        return HttpRespone('用户最终能够看到的结果')  # ******
    obj = HttpResponse('index')
    obj.render = render  # 返回的 HttpResponse 对象中必须包含 render 属性,才能触发中间件里定义的 process_template_response 方法
    return obj

彼は強調しました:

ミドルウェアを書くときのパラメータは、応答を持っているとして、限り、我々はそれを返すために覚えておく必要があり、この情報は応答の前を与えることです

CSRFのミドルウェアCSRF

フィッシングサイト

原理:同一サイト、隠された箱を書く隠しとしてパーティにお金を送ります

Q:このウェブサイトのへの当社のウェブサイトどのように私たちにユーザが送信され、現在のページ要求を区別することではありません

アイデアを防ぎます

このサイトは、ユーザーのフォームフォームのページに戻ります密かにランダムな文字列を詰め

リクエストが来て、最初のランダムな文字列と同じですし、そうでない場合は、直接(403 FORBIDDEN)拒否

ソリューション

値が文字列である置く隠された入力ボックス、ページ上に置く人がサイトを知らないので、各リフレッシュは、内部値を更新します。この値は、偽造することはできません

Djangoの実装 {% csrf_token %}

ランダムな文字列は、次の特性があります。

  • 各訪問が異なる同じブラウザ
  • 別のブラウザでは、間違いなく同じではありません

CSRFから提出されたポストの要求検証データ

フォームフォーム

POSTリクエストフォームを送信するためのフォームは、あなたは何をする必要がある場合には、コードの断片を書くことで{% csrf_token %}、あなたはコメントCSRFミドルウェアを必要としません

AJAXを送信

三つの方法(第三の後端が前方から分離することができます)

  1. 最初のページの書き込みでは{% csrf_token %}、データ内に入力キー情報を取得するには、検索したタグを使用します

  2. 直接書き込みAjaxのデータ値{{ csrf_token }}data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},

  3. 公式ドキュメントを参照してくださいすることをお勧めします、ページに使用されているカスタムのjsファイルは、自動的に取得し、CSRFチェックを通過するためのjsスクリプトを読み込みます *****

    • あなたは、JSファイルのjsに次のコードを配置することができます
    // js 代码(一般放在 static 文件夹下)
    function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie !== '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
    
    var csrftoken = getCookie('csrftoken');
    
    
    function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    
    $.ajaxSetup({
        beforeSend: function (xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });
    • 使用後配置する<script src="{% static 'setjs.js' %}"></script>行にインポートを
    • これは、HTMLで記述する必要がなくなり{% csrf_token %}にAJAXや書き込み{{ csrf_token }}

関連CSRFデコレータ

他のミドルウェアは、以下の方法が確認または取り消さチェックするために従うことができます

二つの問題

グローバルウェブサイトはあなたが(CSRFミドルウェアをコメントアウトされていない)CSRF時間をチェックする必要がある場合には、いくつかは、どのようにこのプロセスをチェックする必要はありませんがありますか? @csrf_exempt

あなたは(一度にCSRFミドルウェアをコメント)グローバルウェブサイトCSRFチェックしていないとき、あなたはどのように対処する検証する必要があるいくつかはあるのですか?@csrf_protect

単機能CSRFチェックをキャンセルしたときにコメントを外しCSRFミドルウェア:csrf_exempt

FBV

from django.views.decorators.csrf import csrf_exempt

# 全局开启时,局部禁用
@csrf_exempt
def index(request):
  pass

CBV

単一の方法は2つの方法ではないがあります。グローバルのためであります

# CBV比较特殊,不能单独加在某个方法上
# 只能加在类上或dispatch方法上
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt


# @method_decorator(csrf_exempt,name='dispatch')  # 第一种
class Csrf_Token(View):
  @method_decorator(csrf_exempt)  # 第二种
  def dispatch(self,request,*args,**kwargs):
    res = super().dispatch(request,*args,**kwargs)
    return res
  # @method_decorator(csrf_exempt)  # 这里这么写不行!!!
  def get(self,request):
    pass
  def post(self,request):
    pass

単機能のチェックになっCSRFは、CSRFミドルウェアコメント:csrf_protect

FBV

from django.views.decorators.csrf import csrf_protect


@csrf_protect
def lll(request):
    return HttpResponse('lll')

CBVは変更に変更する必要があります

世界のために、それは単一のためのいずれかとすることができる3つの方法がありますが、

from django.views.decorators.csrf import csrf_protect

from django.views import View
from django.utils.decorators import method_decorator


# 第一种方式
# @method_decorator(csrf_protect,name='post')  # 有效的
class MyView(View):
    @method_decorator(csrf_protect)  # 第三种方式
    def dispatch(self, request, *args, **kwargs):
        res = super().dispatch(request, *args, **kwargs)
        return res

    def get(self, request):
        return HttpResponse('get')

    # 第二种方式
    # @method_decorator(csrf_protect)  # 有效的
    def post(self, request):
        return HttpResponse('post')

概要: CSRFデコレータのみcsrf_exemptはCBVを飾るために、他のデコレータは、次の3つの方法を持つことができる特殊なケースであり、

認証モジュール

先端科学小さなポイント:

  • 結果は最高の種類を確認するために、「文字列」をプリントアウトの方法で置き換え、オブジェクトが書き換えられる__str__()方法
  • ジャンゴ管理スーパーユーザのみが入ることができます
  • メソッド認証モジュールを使用する方法は、それがモジュール認証を持っていることが最善であります
  • パスワードの変更は、それ以外の場合は無効、保存するために.SAVE()を呼び出す必要があります

ログイン機能を実行するために、組み込みのテーブルを使用して認証ジャンゴ

関与認証に関連する方法

python3 manage.py createsuperuser  # 命令行下创建超级用户(可以拥有登录 django admin 后台管理的权限)

# 查询用户是否存在
user_obj = auth.authenticate(username=username, password=password)  # 数据库中的密码是密文的(该方法不能只传用户名一个参数),返回值要么是对象,要么是 None

# 记录用户状态
auth.login(request, user_obj)  # 登录,会自动存 session
# 优点:只要执行了这一句话,你就可以在后端任意位置通过 request.user 拿到当前登录的用户对象(未登录会报错,AnonymousUser 匿名用户)

# 获取用户对象
request.user  # 用户登录了直接获取用户对象,用户没登录获取到 AnonymousUser 匿名用户

# 判断当前用户是否登录,未登录(AnonymousUser)会返回 False,其他情况下返回 True
request.user.is_authenticated

# 验证用户密码是否正确
is_right = request.user.check_password(old_password)  # 将获取的用户密码,自动加密,然后去数据库中对比(返回布尔值)

# 修改密码
request.user.set_password(new_password)  # 修改密码
request.user.save()  # 需要保存才能生效

# 注销用户
auth.logout(request)  # 等价于 request.session.flush() (删除了 session
表中记录,浏览器 cookie)


# 登录验证装饰器
from django.contrib.auth.decorators import login_required

# @login_required  # 自动校验当前用户是否登录,如果没有登录,(未传参数的情况下)默认跳转到 django 自带的登录页面(还是 404 ?)
# ------ 局部配置
@login_required(login_url='/login/')
def set_password(request):
    pass

# ------ 全局配置(不用在里面写配置了)
# 在 settings.py 中写
LOGIN_URL = '/login/'


# 注册用户
from django.contrib.auth.models import User  # 这就是那张 auth 表
# 创建普通用户
User.objects.create_user(username=username, password=password)
# 创建超级用户
User.objects.create_superuser(username=username, password=password, email='[email protected]')  # 创建超级用户必须传邮箱
# 不能用 User.objects.create(username=username, password=password)  (这样密码没有加密)

コアコード

app01 / views.py

from django.shortcuts import render, HttpResponse
from django.contrib import auth


def xxx(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 取数据库查询当前用户数据
        # models.User.objects.filter(username=username,password=password).first()
        user_obj = auth.authenticate(username=username, password=password)  # 必须要用 因为数据库中的密码字段是密文的 而你获取的用户输入的是明文
        print(user_obj)
        # print(user_obj)
        # print(user_obj.username)
        # print(user_obj.password)
        # 保存用户状态
        # request.session['user'] = user_obj
        auth.login(request, user_obj)  # 将用户状态记录到session中
        """只要执行了这一句话  你就可以在后端任意位置通过request.user获取到当前用户对象"""
    return render(request, 'xxx.html')


def yyy(request):
    print(request.user)  # 如果没有执行auth.login那么拿到的是匿名用户
    print(request.user.is_authenticated)  # 判断用户是否登录  如果是你们用户会返回False
    # print(request.user.username)
    # print(request.user.password)
    return HttpResponse('yyy')


from django.contrib.auth.decorators import login_required


# 修改用户密码
@login_required  # 自动校验当前用户是否登录  如果没有登录 默认跳转到 一个莫名其妙的登陆页面
def set_password(request):
    if request.method == 'POST':
        old_password = request.POST.get('old_password')
        new_password = request.POST.get('new_password')
        # 先判断原密码是否正确
        is_right = request.user.check_password(old_password)  # 将获取的用户密码 自动加密 然后去数据库中对比当前用户的密码是否一致
        if is_right:
            print(is_right)
            # 修改密码
            request.user.set_password(new_password)
            request.user.save()  # 修改密码的时候 一定要save保存 否则无法生效
    return render(request, 'set_password.html')


@login_required
def logout(request):
    # request.session.flush()
    auth.logout(request)
    return HttpResponse("logout")


from django.contrib.auth.models import User


def register(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user_obj = User.objects.filter(username=username)
        if not user_obj:
            # User.objects.create(username =username,password=password)  # 创建用户名的时候 千万不要再使用create 了
            # User.objects.create_user(username =username,password=password)  # 创建普通用户
            User.objects.create_superuser(username=username, password=password, email='[email protected]')  # 创建超级用户
    return render(request, 'register.html')

カスタム拡張autorフィールド

前提:

settings.py追加設定を追加

# ... 其他配置
# 告诉 django 不再使用 auth 默认的表  而是使用你自定义的表
AUTH_USER_MODEL = 'app01.Userinfo'  # '应用名.模型表类名'
# ... 其他配置

二つの方法

app01 / models.py

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


# Create your models here.
# 第一种 使用一对一关系  不考虑


# 第二种方式   使用类的继承
class Userinfo(AbstractUser):
    # 千万不要跟原来表(AbstractUser)中的字段有冲突
    phone = models.BigIntegerField()
    avatar = models.CharField(max_length=32)
    # 别忘了去 settings.py 里配置

次の手順

データベース移行コマンドを実行しますpython3 manage.py makemigrations、python3 manage.py migrate()

この後、すべての認証モジュールの機能は、すべてのあなたは、むしろAUTH_USERを使用するよりも、作成したテーブルに基づいて(これらはもはや自動的にテーブルを作成していません)

Djangoのミドルウェア構成は、機能プラグインの効果をエミュレートすることができ

Djangoのミドルウェアを実行コメントしていない、機能のクラスは、クラスのように書くことができる。実際に

我々は、(デザイン思考がそうである学ぶために、後でrestframework)ミドルウェアの例に従う通知機能を作る、あなたはマイクロチャネル通知、SMS通知を送信することができ、右通知

コードの実装

主な構成セクション(学ぶために多くの設計)

start.pyエントリ・ファイル

import notify

notify.send_all('国庆放假了 记住放八天哦')

notify/__init__.py キーコード(導入動的知識のimportlibの組み合わせ、反射、等)

import settings
import importlib


def send_all(content):
    for path_str in settings.NOTIFY_LIST:  # 1.拿出一个个的字符串   'notify.email.Email'
        module_path, class_name = path_str.rsplit('.', maxsplit=1)  # 2.从右边开始 按照点切一个 ['notify.email', 'Email']
        module = importlib.import_module(module_path)  # from notity import msg/email/wechat
        cls = getattr(module, class_name)  # 利用反射 一切皆对象的思想 从文件中获取属性或者方法 cls = 一个个的类名
        obj = cls()  # 类实例化生成对象
        obj.send(content)  # 对象调方法

settings.pyの構成(機能がオンまたはオフにここですることができます)

NOTIFY_LIST = [
    'notify.email.Email',
    'notify.msg.Msg',
    # 'notify.wechat.WeChat',  # 注释掉了,这个功能就不执行了
    'notify.qq.QQ',
]

機能拡張

その後、各関数は、ファイル(個々のファイルに分割、settings.pyプラグイン可能な効果と遊ぶ)、直接これらのコードを実装するためにファイルを追加し、この機能を追加するためにすることができ

通知/ email.py

class Email(object):
    def __init__(self):
        pass  # 发送邮件需要的代码配置

    def send(self, content):
        print('邮件通知:%s' % content)

通知/ msg.py

class Msg(object):
    def __init__(self):
        pass  # 发送短信需要的代码配置

    def send(self, content):
        print('短信通知:%s' % content)

通知/ qq.py

class QQ(object):
    def __init__(self):
        pass  # 发送qq需要的代码准备

    def send(self, content):
        print('qq通知:%s' % content)

通知/ wechat.py

class WeChat(object):
    def __init__(self):
        pass  # 发送微信需要的代码配置

    def send(self, content):
        print('微信通知:%s' % content)
補足:pycharmのヒント

ジャンプするコードの現在位置を知りたい、あなたは図のアイコンをクリックすることができ、迅速なポジショニングは、あなたがディレクトリ構造を知らせることができます(おそらく右を参照します)

おすすめ

転載: www.cnblogs.com/suwanbin/p/11591887.html
おすすめ