Django+Vueは生鮮電子商取引プラットフォームの9.パーソナルセンター機能開発


文書ディレクトリ
  • 一、DRFのAPIドキュメント自動生成と機能開発
  • 二、ユーザー個人情報修正機能実現
  • 1.ユーザ情報
  • は、権限およびシーケンス化動的設定により取得する.
  • 2.Vueインタフェースは、ユーザ情報表示
  • を実現する.
  • 3.ユーザ資料修正実現
  • 三、ユーザーコレクション機能完成
  • 四、ユーザー伝言機能実現
  • 五、ユーザー出荷先住所機能開発
  • 一人で成功したい、運命を変えたい、夢を持つことが大切だ.......私はすべての人が心の中に夢があるべきだと思って、祖国の大志を抱いて、自分の夢を見つけて、認めたらやって、風に従って揺るがないと思っています.同时に、私达は自分で梦想があるだけではなくて、あなたはまた自分の梦想で他の人に感染して影响するべきで、成功者はきっと自分の梦想で他の人の梦想に火をつけて、いつも梦想を播種する人です.李彦宏
    GithubとGiteeコードの同期更新:https://github.com/PythonWebProject/Django_Fresh_Ecommerce; https://gitee.com/Python_Web_Project/Django_Fresh_Ecommerce.
    一、DRFのAPIドキュメントの自動生成と機能開発
    多くのインタフェースが定義され、各インタフェースの機能と関連する使用説明をより明確に理解するために、APIドキュメントの生成が実現されました.
    DRFはurlsで以前にAPIドキュメントの生成を自動的に実現する.py文書ルーティングはurl(r'docs/', include_docs_urls(title=' ')),と定義されており、アクセステストは以下のとおりです.
    文書は自動的に生成され,テストが可能であり,JSなどのコードを生成してフロントエンドに直接提供してテストを行うことができることがわかる.AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’が間違っている場合はsettings.pyに構成を追加するには:
    # DRF  
    REST_FRAMEWORK = {
        ...
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
        ...
    }
    

    ここで、APIドキュメントの説明は、ビューを作成するときに定義されます.たとえば、ユーザーコレクションビューを定義するには、次のようにします.
    class UserFavViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
        '''
        list:
                  
        create:
                  
        retrieve:
                  
        destroy:
                  
        '''
        permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
        serializer_class = UserFavSerializer
        authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication]
        lookup_field = 'goods_id'
    
        def get_queryset(self):
            return UserFav.objects.filter(user=self.request.user, is_delete=False)
    

    注釈には、各タイプのリクエストについて、固定されたフォーマットで説明が定義されていることがわかります.また、モデル、シーケンス化、フィルタを定義するときにフィールドにhelp_textのプロパティを指定すると、ドキュメントにDescriptionが表示されます.
    ドキュメントは手動で更新する必要がなく、バックエンドコード論理実装後に自動的に更新され、Shell、JavaScript、Pythonなど多くのテストコード方式が提供されます.また、認証が必要なインタフェースについては、テストの前に検証する必要があります.
    データが存在する場合にのみ、データが返されるのは、関連する権限のみです.
    DRFフレームワークは、OpenAPIモードの生成に内蔵されたサポートを提供し、APIドキュメントの構築を許可するツールとともに使用できます.優れたサードパーティ製のドキュメントパッケージもたくさんあります.
    二、ユーザー個人情報修正機能の実現
    1.権限とシーケンス化の動的設定によるユーザー情報の取得
    パーソナルセンターでは名前、生年月日、性別、電子メールアドレスなどを変更することができ、変更前にユーザ情報を表示する必要があるため、ユーザ情報を取得するインタフェースを定義し、権限検証を行う必要がある、apps/users/views.pyは以下のように改善されています.
    class UserViewSet(CreateModelMixin, RetrieveModelMixin, viewsets.GenericViewSet):
        '''
          
        create:
                
        '''
    
        serializer_class = UserRegSerializer
        queryset = User.objects.filter(is_delete=False)
        authentication_classes = [SessionAuthentication, JSONWebTokenAuthentication]
    
        def get_permissions(self):
            '''      '''
            if self.action == 'retrieve':
                return [IsAuthenticated]
            elif self.action == 'create':
                return []
            return []
    
        def get_serializer_class(self):
            '''       '''
            if self.action == 'retrieve':
                return UserDetailSerializer
            elif self.action == 'create':
                return UserRegSerializer
            return UserDetailSerializer
    
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            user = self.perform_create(serializer)
            re_dict = serializer.data
            payload = jwt_payload_handler(user)
            re_dict['token'] = jwt_encode_handler(payload)
            re_dict['name'] = user.name if user.name else user.username
            headers = self.get_success_headers(re_dict)
            return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)
    
        def perform_create(self, serializer):
            return serializer.save()
    
        def get_object(self):
            return self.request.user
    

    apps/users/serializers.py定義のシーケンス化は次のとおりです.
    class UserDetailSerializer(serializers.ModelSerializer):
        '''       '''
        class Meta:
            model = User
            fields = ['name', 'gender', 'birthday', 'email', 'mobile']
    

    ここでの権限検証は一般的な権限検証とは異なり、ユーザーが登録する際、ユーザーはまだアカウントを持っていないため、create(request, *args, **kwargs)perform_create(serializer)の方法を実行するため、これらの方法はすべてのユーザーに開放され、年間検証を行わないべきである.一方、ユーザ登録後、ユーザ情報を変更するには、まずユーザ情報を取得する必要があり、このときget_object()メソッドが実行されるため、このメソッドに対する権限検証が必要である.従って、従来の権限検証方法、すなわちpermission_classes = [IsAuthenticated]を採用するのではなく、動的に権限を設定する必要がある.すなわち、get_permissions()方法を書き換える必要がある.それ以外に、以前に定義されたユーザシーケンス化はユーザ登録機能に対して行われていたため、name、gender、birthday、emailなどのユーザ情報を取得する必要があり、以前のユーザシーケンス化されたusername、code、mobile、passwordなどのフィールドとは異なるため、シーケンス化クラスUserDetailSerializerを再定義し、ユーザ登録および取得情報に対して、UserViewSetはまた、get_serializer_class()メソッドを書き換える動的設定シーケンス化も必要である.
    以下のように説明します.
    明らかに、ユーザーが正常にログインした後にのみ、情報にアクセスできます.
    2.Vueインタフェースはユーザー情報表示を実現する
    フロントエンドでは、コンポーネントはsrc/views/member/userinfoである.vueは、次のようになります.
    created () {
        this.getUserInfo();
    },
    
    getUserInfo () { //      
        getUserDetail().then((response)=> {
            this.userInfo = response.data;
        }).catch(function (error) {
            console.log(error);
        });
    },    
    
    getUserInfoインタフェースがapiで呼び出された.jsでは、次のように変更されます.
    //      
    export const getUserDetail = () => { return axios.get(`${local_host}/users/1/`) }
    
    

    次のように表示されます.
    情報を同期して取得できることは明らかです.
    3.ユーザー資料修正の実現
    ユーザー情報の変更を行うには、以下のように、UpdateModelMixinからUserViewSetを継承するだけです.
    class UserViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, viewsets.GenericViewSet):
        '''
          
        create:
                
        update:
                
        partial_update:
                
        retrieve:
                
        '''
    
        serializer_class = UserRegSerializer
        queryset = User.objects.filter(is_delete=False)
        authentication_classes = [SessionAuthentication, JSONWebTokenAuthentication]
    

    userinfo.vueは次のとおりです.
    <button class="btn_blue_1" style="border:none;" @click="confirmModify">    button>
    
    confirmModify () { //     
        updateUserInfo(this.userInfo).then((response)=> {
            alert('    ');
        }).catch(function (error) {
            console.log(error);
        });
    }
    

    updateUserInfoインタフェース、apiが呼び出されました.jsでは、次のように変更されます.
    //      
    export const updateUserInfo = params => { return axios.patch(`${local_host}/users/1/`, params) }
    

    まず、現在のユーザーデータを次のように問い合せます.
    +----+--------------------------------------------------------------------------------+----------------------------+--------------+----------+------------+-----------+----------+-----------+----------------------------+--------+------------+--------+-------------+-------------+-----------+
    | id | password                                                                       | last_login                 | is_superuser | username | first_name | last_name | is_staff | is_active | date_joined                | name   | birthday   | gender | mobile      | email       | is_delete |
    +----+--------------------------------------------------------------------------------+----------------------------+--------------+----------+------------+-----------+----------+-----------+----------------------------+--------+------------+--------+-------------+-------------+-----------+
    |  1 | pbkdf2_sha256$180000$wpfCm77Dcpee$rHfFjBNZ2SzLLHdd0ZtbiIRqNB86VvgwTJv6ZCXTbfk= | 2020-07-30 16:12:00.000000 |            1 | admin    | Corley     | XXX       |        1 |         1 | 2020-07-20 10:12:00.000000 | Corley | 2020-07-15 | female | 13311111111 | 123@123.com |         0 |
    +----+--------------------------------------------------------------------------------+----------------------------+--------------+----------+------------+-----------+----------+-----------+----------------------------+--------+------------+--------+-------------+-------------+-----------+
    1 row in set (0.01 sec)
    
    

    次のようにデータを変更します.
    明らかに、ページをリフレッシュし、データが変更され、データ・ライブラリが次のようにクエリーされます.
    +----+--------------------------------------------------------------------------------+----------------------------+--------------+----------+------------+-----------+----------+-----------+----------------------------+---------+------------+--------+-------------+-------------+-----------+
    | id | password                                                                       | last_login                 | is_superuser | username | first_name | last_name | is_staff | is_active | date_joined                | name    | birthday   | gender | mobile      | email       | is_delete |
    +----+--------------------------------------------------------------------------------+----------------------------+--------------+----------+------------+-----------+----------+-----------+----------------------------+---------+------------+--------+-------------+-------------+-----------+
    |  1 | pbkdf2_sha256$180000$wpfCm77Dcpee$rHfFjBNZ2SzLLHdd0ZtbiIRqNB86VvgwTJv6ZCXTbfk= | 2020-07-30 16:12:00.000000 |            1 | admin    | Corley     | XXX       |        1 |         1 | 2020-07-20 10:12:00.000000 | Corley2 | 2020-07-30 | male   | 13311111111 | 124@123.com |         0 |
    +----+--------------------------------------------------------------------------------+----------------------------+--------------+----------+------------+-----------+----------+-----------+----------------------------+---------+------------+--------+-------------+-------------+-----------+
    1 row in set (0.00 sec)
    
    

    データが修正されたことも証明された.
    説明:前にフロントエンドでテストした時、ユーザーがログインしてから5分ぐらいでログイン状態が失効したことを発見しました.再ログインしてから正常にアクセスする必要があります.私はとても憂鬱です.JWTの有効期限が7日に設定されているのに、どうして効果がありませんか.今日は設定をよく見てみました.確かに私が間違っているようです.私が設定したのはJWT_REFRESH_EXPIRATION_DELTAで7日です.つまり、この時間帯でJWTを更新してログイン状態を維持することができます.私が考えている期限切れではなく、JWT_EXPIRATION_DELTAを設定する必要があります.これこそ本当のJWTの期限切れです.JWTが期限切れになった後、設定したリフレッシュの期限切れの時間内にあれば、JWTをリフレッシュしてログイン状態を維持することができます.具体的には、ブログを参照してください.https://www.jianshu.com/p/a60efb8bac35ああ、私のJWTは次のように構成されています.
    # JWT  
    JWT_AUTH = {
        #     
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
        #       
        'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=30),
        #      
        'JWT_AUTH_HEADER_PREFIX': 'JWT',
    }
    

    三、ユーザーコレクション機能の完成
    以前、商品詳細ページでは、ユーザーがコレクションを追加および削除する機能の一部を実現していました.ここでは、ユーザーセンターでユーザーコレクション機能を完備しています.
    コレクションされた商品の詳細をユーザーセンターに表示するにはapps/user_operation/serializers.pyで定義されたネストシーケンスは、次のようになります.
    class UserFavDetailSerializer(serializers.ModelSerializer):
        goods = GoodsSerializer()
    
        class Meta:
            model = UserFav
            fields = ['id', 'goods']
    

    ビューは以下のように改善されています.
    class UserFavViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
        '''
        list:
                  
        create:
                  
        retrieve:
                  
        destroy:
                  
        '''
        permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
        serializer_class = UserFavSerializer
        authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication]
        lookup_field = 'goods_id'
    
        def get_queryset(self):
            return UserFav.objects.filter(user=self.request.user, is_delete=False)
    
        def get_serializer_class(self):
            '''       '''
            if self.action == 'list':
                return UserFavDetailSerializer
            elif self.action == 'create':
                return UserFavSerializer
            return UserFavSerializer
    

    次のようにテストします.
    フロントエンドsrc/views/member/collection.vueは次のとおりです.
    <tr v-for="(item,index) in collections">
        <td bgcolor="#ffffff">
            <router-link :to="'/app/home/productDetail/'+item.goods.id" class="f6" target="_blank">{{item.goods.name}}router-link>
        td>
        <td bgcolor="#ffffff">   <span class="goods-price">¥{{item.goods.shop_price}} span>
        td>
        <td align="center" bgcolor="#ffffff">
            <a class="f6" @click="deletePro(index, item.goods.id)">  a>
        td>
    tr>
    
    created () {
        this.getCollection();
    },
    
    getCollection () { //      
        getAllFavs().then((response)=> {
            this.collections = response.data;
        }).catch(function (error) {
            console.log(error);
        });
    },
    
    deletePro (index, id) { //      
        alert('                 ?');
        delFav(id).then((response)=> {
            this.collections.splice(index,1);
            alert('     ');
        }).catch(function (error) {
            console.log(error);
        });
    }
    

    初期化時にgetCollection()メソッドを呼び出してコレクションを取得し、getAllFavsインタフェースを呼び出し、データを得た後にforループで表示することがわかる.削除時にdeletePro(index, item.goods.id)メソッドを呼び出し、delFavインタフェース、apiを呼び出す.jsは以下のように変更されました.
    //  
    export const addFav = params => { return axios.post(`${local_host}/userfavs/`, params) }
    
    //    
    export const delFav = goodsId => { return axios.delete(`${local_host}/userfavs/`+goodsId+'/') }
    
    export const getAllFavs = () => { return axios.get(`${local_host}/userfavs/`) }
    
    //      
    export const getFav = goodsId => { return axios.get(`${local_host}/userfavs/`+goodsId+'/') }
    

    以下のように説明します.
    再クエリー・データベースは次のとおりです.
    +----+----------------------------+-----------+----------+---------+
    | id | add_time                   | is_delete | goods_id | user_id |
    +----+----------------------------+-----------+----------+---------+
    |  1 | 2020-07-29 17:02:39.893993 |         0 |       25 |       1 |
    |  2 | 2020-07-29 17:02:49.268221 |         0 |       15 |       1 |
    |  3 | 2020-07-29 17:02:57.410071 |         0 |        5 |       1 |
    |  6 | 2020-07-29 18:23:00.000000 |         0 |        3 |       1 |
    +----+----------------------------+-----------+----------+---------+
    4 rows in set (0.01 sec)
    
    

    明らかに、コレクションデータは同期されています.
    四、ユーザー伝言機能の実現
    ユーザメッセージには、追加、取得、削除などの機能が含まれます.
    追加機能を実装し、apps/user_をシーケンス化operation/serializers.pyは次のとおりです.
    class LeavingMessageSerializer(serializers.ModelSerializer):
        user = serializers.HiddenField(default=serializers.CurrentUserDefault())
    
        class Meta:
            model = UserLeavingMessage
            fields = ['id', 'user', 'message_type', 'subject', 'message', 'file']
    

    ビューの定義は次のとおりです.
    class LeavingMessageViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
        '''
        list:
                
        create:
                
        delete:
                
        '''
    
        permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
        authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication]
        serializer_class = LeavingMessageSerializer
    
        def get_queryset(self):
            return UserLeavingMessage.objects.filter(user=self.request.user, is_delete=False)
    

    ルーティングの設定は次のとおりです.
    #       
    router.register(r'messages', LeavingMessageViewSet, basename='messages')
    

    次のようにテストします.
    明らかに、メッセージの追加に成功し、データベースを表示します.
    +----+--------------+--------------+--------------------------------------+------------------+----------------------------+-----------+---------+
    | id | message_type | subject      | message                              | file             | add_time                   | is_delete | user_id |
    +----+--------------+--------------+--------------------------------------+------------------+----------------------------+-----------+---------+
    |  1 |            4 |          |                          |     .jpg     | 2020-07-30 17:51:15.337881 |         0 |       1 |
    +----+--------------+--------------+--------------------------------------+------------------+----------------------------+-----------+---------+
    1 row in set (0.00 sec)
    
    

    メッセージはもう一つadd_timeフィールドですが、手動で時間を入力せずに自動的に生成するため、add_の上書きを定義する必要があります.timeフィールド、およびadd_を表すread_onlyプロパティを設定する必要がありますtimeフィールドは、次のように返されずにコミットされます.
    class LeavingMessageSerializer(serializers.ModelSerializer):
        user = serializers.HiddenField(default=serializers.CurrentUserDefault())
        add_time = serializers.DateTimeField(read_only=True)
    
        class Meta:
            model = UserLeavingMessage
            fields = ['id', 'user', 'message_type', 'subject', 'message', 'file', 'add_time']
    
    read_onlyプロパティはAPI出力に含まれますが、作成または更新操作中に入力に含めるべきではありません.Trueに設定して、シーケンス化表示形式の場合に使用され、逆シーケンス化中にインスタンスの作成または更新時に使用されないようにします.
    このとき、次のようにテストします.
    add_が表示されますtimeフィールドですが、コミット時間は必要ありません.
    フロントエンドsrc/views/member/message.vueは次のとおりです.
    <li v-for="(item,index) in messageAll">
        <div>
            <span v-if="item.message_type===1">span>
            <span v-if="item.message_type===2">span>
            <span v-if="item.message_type===3">span>
            <span v-if="item.message_type===4">span>
            <span v-if="item.message_type===5">span>
            <span>{{item.subject}}span>
            <span>({{item.add_time}})span>
        div>
        <div>
            {{item.message}}
        div>
        <div>
            <a @click="deleteMessage(index, item.id)">  a>
            <a :href="(item.file)">       a>
    
        div>
    
    li>
    
    <form action="" method="post" enctype="multipart/form-data" name="formMsg">
        <table width="100%" border="0" cellpadding="3">
            <tbody><tr>
                <td align="right">td>
                <td>
                    <input type="radio" id="one" value="1" v-model="message_type">
                    <label for="one">  label>
                    <input type="radio" id="two" value="2" v-model="message_type">
                    <label for="two">  label>
                    <input type="radio" id="three" value="3" v-model="message_type">
                    <label for="three">  label>
                    <input type="radio" id="four" value="4" v-model="message_type">
                    <label for="four">  label>
                    <input type="radio" id="five" value="5" v-model="message_type">
                    <label for="five">  label>
    
                    
                td>
            tr>
            <tr>
                <td align="right">td>
                <td><input name="msg_title" type="text" size="30" class="inputBg" v-model="subject">td>
            tr>
            <tr>
                <td align="right" valign="top">td>
                <td><textarea name="msg_content" cols="50" rows="4" wrap="virtual" class="B_blue" v-model="message">textarea>td>
            tr>
            <tr>
                <td align="right">td>
                <td><input type="file" name="message_img" size="45" class="inputBg" @change="preview">td>
            tr>
            <tr>
                <td> td>
                <td><input type="hidden" name="act" value="act_add_message">
                    
                    <a class="btn_blue_1" @click="submitMessage">  a>
                td>
            tr>
            <tr>
                <td> td>
                <td>
                    <font color="red">font><br><br>gif、jpg、png、word、excel、txt、zip、ppt、pdf                      td>
            tr>
            tbody>table>
    form>
    
    created () {
        this.getMessage();
    },
    
    submitMessage () { //    
        const formData = new FormData();
        formData.append('file',this.file);
        formData.append('subject',this.subject);
        formData.append('message',this.message);
        formData.append('message_type',this.message_type);
        addMessage(formData).then((response)=> {
            this.getMessage();
    
        }).catch(function (error) {
            console.log(error);
        });
    },
    getMessage () { //    
        getMessages().then((response)=> {
            console.log(response.data);
            this.messageAll = response.data;
        }).catch(function (error) {
            console.log(error);
        });
    },
    deleteMessage (index, id) { //     
        delMessages(id).then((response)=> {
            alert("    ")
            this.messageAll.splice(index,1);
        }).catch(function (error) {
            console.log(error);
        });
    },
    

    初期化時にgetMessage()メソッドが呼び出され、getMessageインタフェースが呼び出され、データの後頭蓋骨forサイクルが示される.新しいメッセージが追加されると、submitMessage()メソッドが呼び出され、addMessageインタフェースが送信される.メッセージ削除時に呼び出すdeleteMessage(index, id)メソッド、呼び出すdelMessagesインタフェース実装、api.jsは以下のように変更されました.
    //    
    export const getMessages = () => {return axios.get(`${local_host}/messages/`)}
    
    //    
    export const addMessage = params => {return axios.post(`${local_host}/messages/`, params, {headers:{ 'Content-Type': 'multipart/form-data' }})}
    
    //    
    export const delMessages = messageId => {return axios.delete(`${local_host}/messages/`+messageId+'/')}
    

    新しいメッセージをアップロードするには、APIから見ることができます.新しいメッセージを提出するとき、HeaderにContent-Typeをmultipart/form-data追加し、アップロードファイルをサポートします.同時にDRFはMultipartParser類を提供し、HTMLフォームの内容の多くを解析し、ファイルのアップロードをサポートします.
    次のようにテストします.
    明らかに、操作は成功した.
    五、ユーザー出荷先住所機能開発
    user_Operationでは出荷先アドレス機能を開発します.
    まずmodels.pyでUserAddressモデルを変更するには、次のようにします.
    class UserAddress(models.Model):
        '''      '''
        user = models.ForeignKey(User, verbose_name='  ', null=True, on_delete=models.SET_NULL)
        province = models.CharField(max_length=50, default='', verbose_name='  ')
        city = models.CharField(max_length=50, default='', verbose_name='  ')
        district = models.CharField(max_length=80, default='', verbose_name='  ')
        address = models.CharField(max_length=100, default='', verbose_name='    ')
        signer_name = models.CharField(max_length=20, default='', verbose_name='   ')
        signer_mobile = models.CharField(max_length=11, default='', verbose_name='    ')
    
        add_time = models.DateTimeField(default=datetime.now, verbose_name=u'    ')
        is_delete = models.BooleanField(default=False, verbose_name='    ')
    
        class Meta:
            verbose_name = '    '
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.address
    

    変更後にデータベースをマッピングし、次のようにシーケンス化を定義します.
    class AddressSerializer(serializers.ModelSerializer):
        user = serializers.HiddenField(default=serializers.CurrentUserDefault())
        add_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M')
    
        class Meta:
            model = UserAddress
            fields = ['id', 'user', 'province', 'city', 'district', 'address', 'signer_name', 'add_time', 'signer_mobile']
    

    ビューを再定義するには、次のようにします.
    class AddressViewSet(viewsets.ModelViewSet):
        '''
              
        list:
                  
        create:
                  
        update:
                  
        delete:
                  
        '''
    
        permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
        authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication]
        serializer_class = AddressSerializer
    
        def get_queryset(self):
            return UserAddress.objects.filter(user=self.request.user, is_delete=False)
    
    AddressViewSetviewsets.ModelViewSetから継承され、mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSetから以前に継承されたコード量が大幅に減少したが、同様の効果が得られることがわかる.
    ルーティングの設定は次のとおりです.
    #         
    router.register(r'address', AddressViewSet, basename='address')
    

    次のようにテストします.
    データの作成に成功しました.
    フロントエンドsrc/views/member/receive.vueは次のとおりです.
    <table width="100%" border="0" cellpadding="5" cellspacing="1" bgcolor="#dddddd" v-for="(item, index) in receiveInfoArr">
        <tbody>
            <tr>
                <td align="right" bgcolor="#ffffff">td>
                <td colspan="3" align="left" bgcolor="#ffffff">
                    <div class="addr" @click="bubble(index)">
                        <v-distpicker :province="item.province" :city="item.city" :area="item.district" @province="updateProvince"  @city="updateCity" @area="updateArea">v-distpicker>
                    div>
                td>
            tr>
            <tr>
                <td align="right" bgcolor="#ffffff">td>
                <td align="left" bgcolor="#ffffff"><input name="consignee" type="text" class="inputBg" id="consignee_0" value="ssss" v-model="item.signer_name">
                    (  )span>
                    td>
    
            tr>
            <tr>
                <td align="right" bgcolor="#ffffff">td>
                <td align="left" bgcolor="#ffffff"><input name="address" type="text" class="inputBg" id="address_0" v-model="item.address">
                    (  )span>td>
            tr>
            <tr>
    
                <td align="right" bgcolor="#ffffff">td>
                <td align="left" bgcolor="#ffffff"><input name="mobile" type="text" class="inputBg" id="mobile_0" v-model="item.signer_mobile">(  )span>td>
            tr>
            <tr>
                <td align="right" bgcolor="#ffffff"> td>
                <td colspan="3" align="center" bgcolor="#ffffff">
                
    
                <button class="bnt_blue_2" @click="confirmUpdate(item.id, index)">    button>
                <button class="bnt_blue_2" @click="deleteInfo(item.id)">  button>
                
                td>
            tr>
        tbody>
    table>
    
    <table width="100%" border="0" cellpadding="5" cellspacing="1" bgcolor="#dddddd">
        <tbody>
            <tr>
                <td align="right" bgcolor="#ffffff">td>
                <td colspan="3" align="left" bgcolor="#ffffff">
                    <div class="addr">
                        
                        <v-distpicker :province="newInfo.province" :city="newInfo.city" :area="newInfo.district" @province="getProvince" @city="getCity" @area="getArea">v-distpicker>
                    div>
                td>
            tr>
            <tr>
                <td align="right" bgcolor="#ffffff">td>
                <td align="left" bgcolor="#ffffff"><input name="consignee" type="text" class="inputBg" id="consignee_0" value="ssss" v-model="newInfo.signer_name">
                    (  )span> td>
    
            tr>
            <tr>
                <td align="right" bgcolor="#ffffff">td>
                <td align="left" bgcolor="#ffffff"><input name="address" type="text" class="inputBg" id="address_0" v-model="newInfo.address">
                    (  )span>td>
            tr>
            <tr>
    
                <td align="right" bgcolor="#ffffff">td>
                <td align="left" bgcolor="#ffffff"><input name="mobile" type="text" class="inputBg" id="mobile_0" v-model="newInfo.signer_mobile">(  )span>td>
            tr>
            <tr>
                <td align="right" bgcolor="#ffffff"> td>
                <td colspan="3" align="center" bgcolor="#ffffff">
                
                <button class="bnt_blue_2" @click="addReceive">      button>
    
                
                td>
            tr>
        tbody>
    table>
    
    created () {
        this.getReceiveInfo();
    },
    
    updateProvince (data) {
        this.receiveInfoArr[this.currentIndex].province = data.value;
    },
    updateCity (data) {
        this.receiveInfoArr[this.currentIndex].city = data.value;
    },
    updateArea (data) {
        this.receiveInfoArr[this.currentIndex].district = data.value;
    },
    
    
    getProvince (data) {
        this.newInfo.province = data.value;
    },
    getCity (data) {
        this.newInfo.city = data.value;
    },
    getArea (data) {
        this.newInfo.district = data.value;
    },
    getReceiveInfo() { //       
        getAddress().then((response)=> {
            console.log(response.data);
            this.receiveInfoArr = response.data;
    
        }).catch(function (error) {
            console.log(error);
        });
    
    },
    
    addReceive () { //      
        addAddress(this.newInfo).then((response)=> {
            alert('    ');
            //     
            this.getReceiveInfo();
            this.newInfo = Object.assign({}, this.newInfoEmpty);
    
        }).catch(function (error) {
            console.log(error);
        });
    },
    confirmUpdate (id, index) { //       
        updateAddress(id, this.receiveInfoArr[index]).then((response)=> {
            alert('    ');
            this.getReceiveInfo();
        }).catch(function (error) {
            console.log(error);
        });
    
    },
    deleteInfo (id, index) { //        
        delAddress(id).then((response)=> {
            alert('    ');
            this.getReceiveInfo();
        }).catch(function (error) {
            console.log(error);
        });
    }
    

    初期化時にgetReceiveInfo()メソッドを呼び出して出荷先アドレス情報を取得し、getAddressインタフェースを呼び出し、データを取得した後、forループを介してフロントエンドに表示することがわかる.各フィールドを変更して、対応するupdateメソッドをそれぞれ呼び出し、confirmUpdate (id, index)メソッドを呼び出してデータを更新し、最後にupdateAddressインタフェースを呼び出して更新を実現する.出荷先アドレス呼び出しaddReceive()メソッドを追加し、updateAddressインタフェースを呼び出して新規データを実現する.削除データは、deleteInfo(id, index)メソッドを呼び出し、delAddressインタフェースを介して削除される.
    api.jsの対応するインタフェースは以下のように変更されました.
    //      
    export const addAddress = params => {return axios.post(`${local_host}/address/`, params)}
    
    //      
    export const delAddress = addressId => {return axios.delete(`${local_host}/address/`+addressId+'/')}
    
    //      
    export const updateAddress = (addressId, params) => {return axios.patch(`${local_host}/address/`+addressId+'/', params)}
    
    //      
    export const getAddress = () => {return axios.get(`${local_host}/address/`)}
    

    プレゼンテーションは以下の通りです.明らかに、正常に商品の住所を追加、削除、変更、検査することができます.