인프런 커뮤니티 질문&답변

RNpfmfnpr님의 프로필 이미지
RNpfmfnpr

작성한 질문수

Vue.js - Django 연동 웹 프로그래밍 (실전편)

페이지 이동 - Detail 화면으로

row 행 클릭 오류

작성

·

361

·

수정됨

0

선생님 안녕하세요.

태그 클릭 시 관련 게시글만 보여주는 파트까지는 잘 실행되었습니다.

그 후, 게시글 클릭 시 detail 로 넘어가는 부분에서 오류가 발생했습니다. 혹시 오타가 있는가 해서 복사-붙여넣기를 해봤지만 계속 오류가 발생합니다.Page not found (404)

Request Method:GETRequest URL:http://localhost:8000/blog/post/undefined/

Using the URLconf defined in mysite.urls, Django tried these URL patterns, in this order:

  1. admin/

  2. [name='home']

  3. blog/ post/list/ [name='post_list']

  4. blog/ post/<int:pk>/ [name='post_detail']

  5. api/

The current path, blog/post/undefined/, didn’t match any of these.

You’re seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.

답변 2

0

김석훈님의 프로필 이미지
김석훈
지식공유자

독자님. vuetify v3 사용하는 거 맞죠?

지금 올려주신 소스는 v2 버전입니다. 섹션 10. 소스 다운로드에 대한 설명이 있으니,

그 설명에서 VueDjangoVite 프로젝트 소스를 활용하세요.

해보시고 다시 질문 바랍니다.

RNpfmfnpr님의 프로필 이미지
RNpfmfnpr
질문자

와 드디어 됐어요.

정말 감사합니다.

0

김석훈님의 프로필 이미지
김석훈
지식공유자

안녕하세요. 독자님.

Vuetify v3 을 사용중이시죠? v-data-table 이 동작해서 포스트 목록은 잘 나오고 있는 상태이지요?

그렇다면 제공된 소스에서 ~/components/PostList.vue 의 serverPage() 메소드가 정확하게 코딩되어 있고 잘 동작하는지 확인 부탁합니다. 즉 테이블에서 행을 클릭하면 해당 게시글의 detail 화면으로 넘어가야 합니다.

이게 잘 되면 다시 한번 질문 올려주시기 바랍니다.

RNpfmfnpr님의 프로필 이미지
RNpfmfnpr
질문자

강의 처음부터 복습했는데 다시 이 부분에서 안됩니다. 테이블에서 행을 클릭하면 같은 오류 떠요.

serverPage(item) {
  console.log("serverPage()...", item);
  location.href = `/blog/post/${item.id}/`;
},

이 부분의 item.id 를 인식 못하는거 같은데 왜 그런지 잘 모르겠습니다.

<template>
  <v-container>
    <v-data-table
      :headers="headers"
      :items="posts"
      :sort-by="name"
      class="elevation-1"
      :Items-per-page="5"
      @click:row="serverPage"
    >
      <template v-slot:top>
        <v-toolbar flat color="white">
          <v-toolbar-title>Post List
            <span v-if="tagname" class="body-1 font-italic ml-3">(with {{ tagname }} tagged)</span>
          </v-toolbar-title>
          <v-divider class="mx-4" inset vertical></v-divider>
          <v-spacer></v-spacer>
          <v-dialog v-model="dialog" max-width="500px">
            <template v-slot:activator="{ props }">
              <v-btn color="primary" dark class="mb-2" v-bind="props">
                New Post
              </v-btn>
            </template>
            <v-card>
              <v-card-title>
                <span class="text-h5">{{ formTitle }}</span>
              </v-card-title>

              <v-card-text>
                <v-container>
                  <v-row>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.name" label="Dessert name"></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.calories" label="Calories"></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.fat" label="Fat (g)"></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.carbs" label="Carbs (g)"></v-text-field>
                    </v-col>
                    <v-col cols="12" sm="6" md="4">
                      <v-text-field v-model="editedItem.protein" label="Protein (g)"></v-text-field>
                    </v-col>
                  </v-row>
                </v-container>
              </v-card-text>

              <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn color="blue-darken-1" variant="text" @click="close">
                  Cancel
                </v-btn>
                <v-btn color="blue-darken-1" variant="text" @click="save">
                  Save
                </v-btn>
              </v-card-actions>
            </v-card>
          </v-dialog>
          <v-dialog v-model="dialogDelete" max-width="500px">
            <v-card>
              <v-card-title class="text-h5">Are you sure you want to delete this item?</v-card-title>
              <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn color="blue-darken-1" variant="text" @click="closeDelete">Cancel</v-btn>
                <v-btn color="blue-darken-1" variant="text" @click="deleteItemConfirm">OK</v-btn>
                <v-spacer></v-spacer>
              </v-card-actions>
            </v-card>
          </v-dialog>
        </v-toolbar>
      </template>
      <template v-slot:[`item.actions`]="{ item }">
        <v-icon size="small" class="me-2" @click="editItem(item.raw)">
          mdi-pencil
        </v-icon>
        <v-icon size="small" @click="deleteItem(item.raw)"> mdi-delete </v-icon>
      </template>
      <template v-slot:no-data>
        <v-btn color="primary" @click="fetchPostList"> Reset </v-btn>
      </template>
    </v-data-table>
  </v-container>
</template>

<script>
import axios from "axios";

export default {
  data: () => ({
    dialog: false,
    dialogDelete: false,
    headers: [
      {
        title: 'ID',
        align: 'start',
        sortable: false,
        key: 'id',
      },
      { title: '제 목', key: 'title' },
      { title: '요 약', key: 'description' },
      { title: '수정일', key: 'modify_dt' },
      { title: '작성자', key: 'protein' },
      { title: 'Actions', key: 'actions', sortable: false },
    ],
    posts: [],
    tagname: '',
    editedIndex: -1,
    editedItem: {
      name: '',
      calories: 0,
      fat: 0,
      carbs: 0,
      protein: 0,
    },
    defaultItem: {
      name: '',
      calories: 0,
      fat: 0,
      carbs: 0,
      protein: 0,
    },
  }),

  computed: {
    formTitle() {
      return this.editedIndex === -1 ? 'New Item' : 'Edit Item'
    },
  },

  watch: {
    dialog(val) {
      val || this.close()
    },
    dialogDelete(val) {
      val || this.closeDelete()
    },
  },

  created() {
    const params = new URL(location).searchParams;
    this.tagname = params.get('tagname');
    this.fetchPostList()
  },

  methods: {
    fetchPostList() {
      console.log("fetchPostList()...", this.tagname);

      let getUrl = '';
      if (this.tagname) getUrl = `/api/post/list/?tagname=${this.tagname}`;
      else getUrl = '/api/post/list/'

      axios.get(getUrl)
      .then(res => {
        console.log("POST LIST GET RES", res);
        this.posts = res.data;
      })
      .catch(err => {
        console.log("POST LIST GET ERR.RESPONSE", err.response);
        alert(err.response.status + ' ' + err.response.statusText);
      });
    },

    serverPage(item) {
      console.log("serverPage()...", item);
      location.href = `/blog/post/${item.id}/`;
    },

    editItem(item) {
      this.editedIndex = this.posts.indexOf(item)
      this.editedItem = Object.assign({}, item)
      this.dialog = true
    },

    deleteItem(item) {
      this.editedIndex = this.posts.indexOf(item)
      this.editedItem = Object.assign({}, item)
      this.dialogDelete = true
    },

    deleteItemConfirm() {
      this.posts.splice(this.editedIndex, 1)
      this.closeDelete()
    },

    close() {
      this.dialog = false
      this.$nextTick(() => {
        this.editedItem = Object.assign({}, this.defaultItem)
        this.editedIndex = -1
      })
    },

    closeDelete() {
      this.dialogDelete = false
      this.$nextTick(() => {
        this.editedItem = Object.assign({}, this.defaultItem)
        this.editedIndex = -1
      })
    },

    save() {
      if (this.editedIndex > -1) {
        Object.assign(this.posts[this.editedIndex], this.editedItem)
      } else {
        this.posts.push(this.editedItem)
      }
      this.close()
    },
  },
}
</script>

<style scoped>
.v-data-table >>> tbody > tr {
  cursor: pointer;
}
</style>
RNpfmfnpr님의 프로필 이미지
RNpfmfnpr

작성한 질문수

질문하기