Consumir API REST Django de músicas, via projeto Vue.js.
python3 manage.py makemigrations
python3 manage.py migrate
python3 manage.py runserver
Listar musics: (GET) http://localhost:8000/api/musics/
Adicionar music: (POST) http://localhost:8000/api/musics/ (json) { "title": "Nome da música", "artist": "Nome do artista" }
Ver music 1: (GET) http://localhost:8000/api/musics/1/
Atualizar music 1: (PUT) http://localhost:8000/api/musics/1/ (json) { "title": "Nome da música", "artist": "Nome do artista" }
Deletar music 1: (DELETE) http://localhost:8000/api/musics/1/
/src/assets/styles/global.css:
.error {
color: red;
font: 14px;
}
/src/components/AddMusic.vue:
<template>
<h1>Adicionar Música</h1>
<form @submit.prevent="addMusic">
<input type="text" placeholder="Título" v-model="newMusic.title" required>
<input type="text" placeholder="Artista" v-model="newMusic.artist" required>
<button type="submit">Confirmar</button>
<p v-if="errorMessage" class="error">{{ errorMessage }}</p>
</form>
<button @click="goBack">Cancelar</button>
</template>
<script>
import { ref } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
export default {
setup() {
const newMusic = ref({ title: '', artist: '' });
const errorMessage = ref('');
const router = useRouter();
const addMusic = async () => {
try {
const response = await axios.get('http://localhost:8000/api/musics/', {
params: {
title: newMusic.value.title,
artist: newMusic.value.artist,
},
});
const musicExists = response.data.some(
(music) =>
music.title === newMusic.value.title &&
music.artist === newMusic.value.artist
);
if (musicExists) {
errorMessage.value = 'Música já cadastrada';
} else {
await axios.post('http://localhost:8000/api/musics/', newMusic.value);
router.push({ name: 'list-music' });
}
} catch (error) {
console.error("Erro ao adicionar música", error);
errorMessage.value = 'Erro ao adicionar música';
}
};
const goBack = () => {
router.push({ name: 'list-music' });
};
return {
newMusic,
errorMessage,
addMusic,
goBack,
};
},
};
</script>
<style scoped></style>
src/components/ConfirmDialogDelete.vue:
<template>
<div class="confirm-dialog">
<p>Excluir música "{{ music.title }}" (id: {{ music.id }})?</p>
<button @click="cancel">Cancelar</button>
<button @click="confirm">Confirmar</button>
</div>
</template>
<script>
export default {
props: {
music: {
type: Object,
required: true
}
},
methods: {
cancel() {
this.$emit('cancel');
},
confirm() {
this.$emit('confirm', this.music.id);
}
}
};
</script>
<style scoped></style>
src/components/DeleteMusic.vue:
<template>
<h1>Exclusão</h1>
<p>Excluir música?</p>
<p>Id: {{ music.id }}</p>
<p>Título: {{ music.title }}</p>
<p>Artista: {{ music.artist }}</p>
<button @click="confirmDelete">Confirmar</button>
<button @click="cancel">Cancelar</button>
</template>
<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
export default {
setup() {
const router = useRouter();
const music = ref({});
const musicId = router.currentRoute.value.params.id;
const fetchMusicDetails = async () => {
try {
const response = await axios.get(`http://localhost:8000/api/musics/${musicId}/`);
music.value = response.data;
} catch (error) {
console.error('Erro ao buscar dados da música:', error);
}
};
const confirmDelete = async () => {
try {
await axios.delete(`http://localhost:8000/api/musics/${musicId}/`);
router.push({ name: 'list-music' });
} catch (error) {
console.error('Erro ao excluir música:', error);
}
};
const cancel = () => {
router.push({ name: 'list-music' });
};
onMounted(fetchMusicDetails);
return {
music,
confirmDelete,
cancel,
};
},
};
</script>
<style scoped></style>
/src/components/EditMusic.vue:
<template>
<h1>Editar música</h1>
<form @submit.prevent="submitEdit">
<input type="text" placeholder="Título" v-model="music.title" required>
<input type="text" placeholder="Artista" v-model="music.artist" required>
<button type="submit">Confirmar</button>
<button @click="cancelEdit">Cancelar</button>
</form>
<div v-if="errorMessage" class="error">{{ errorMessage }}</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { useRoute, useRouter } from 'vue-router';
export default {
setup() {
const music = ref({ title: '', artist: '' });
const originalMusic = ref({ title: '', artist: '' });
const errorMessage = ref('');
const router = useRouter();
const route = useRoute();
const musicId = route.params.id;
const musics = ref([]);
const fetchMusics = async () => {
try {
const response = await axios.get('http://localhost:8000/api/musics/');
musics.value = response.data;
} catch (error) {
console.error('Erro ao buscar música', error);
}
};
const fetchMusic = async () => {
try {
const response = await axios.get(`http://localhost:8000/api/musics/${musicId}/`);
music.value = response.data;
originalMusic.value = { ...response.data };
} catch (error) {
console.error('Erro ao buscar música', error);
}
};
const validateMusic = () => {
if (music.value.title === originalMusic.value.title && music.value.artist === originalMusic.value.artist) {
return true;
}
const existingMusic = musics.value.find(
(m) => m.title === music.value.title && m.artist === music.value.artist && m.id !== musicId
);
if (existingMusic) {
errorMessage.value = 'Música já cadastrada';
return false;
}
errorMessage.value = '';
return true;
};
const submitEdit = async () => {
if (!validateMusic()) {
return;
}
try {
await axios.put(`http://localhost:8000/api/musics/${musicId}/`, music.value);
router.push({ name: 'list-music' });
} catch (error) {
console.error('Erro ao editar música', error);
}
};
const cancelEdit = () => {
router.push({ name: 'list-music' });
};
onMounted(() => {
fetchMusics();
fetchMusic();
});
return {
music,
errorMessage,
submitEdit,
cancelEdit,
};
},
};
</script>
<style scoped></style>
/src/components/ListMusic.vue:
<template>
<div v-if="musics.length === 0">
<p>Não há músicas disponíveis.</p>
</div>
<ul v-else>
<li v-for="music in musics" :key="music.id">
<div v-if="!confirmingMusicId || confirmingMusicId !== music.id">
Id: {{ music.id }}
Título: {{ music.title }}
Artista: {{ music.artist }}
<button @click="viewMusic(music.id)">Ver</button>
<button @click="goToEditMusic(music.id)">Editar</button>
<button @click="goToDeleteMusic(music.id)">Excluir</button>
<button @click="startConfirmDelete(music.id)">ExcluirDiretamente</button>
</div>
<ConfirmDialogDelete v-if="confirmingMusicId === music.id" :music="music" @cancel="cancelDelete" @confirm="confirmDelete" />
</li>
</ul>
<button @click="goToAddMusic">Adicionar música</button>
</template>
<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
import ConfirmDialogDelete from './ConfirmDialogDelete.vue';
export default {
components: {
ConfirmDialogDelete
},
setup() {
const musics = ref([]);
const confirmingMusicId = ref(null);
const router = useRouter();
const fetchMusics = async () => {
try {
const response = await axios.get('http://localhost:8000/api/musics/');
musics.value = response.data;
} catch (error) {
console.error('Erro ao buscar música', error);
}
};
onMounted(fetchMusics);
const viewMusic = (id) => {
router.push({ name: 'music-detail', params: { id } });
};
const goToDeleteMusic = (id) => {
router.push({ name: 'delete-music', params: { id } });
};
const goToEditMusic = (id) => {
router.push({ name: 'edit-music', params: { id } });
};
const goToAddMusic = () => {
router.push({ name: 'add-music' });
};
const startConfirmDelete = (id) => {
confirmingMusicId.value = id;
};
const cancelDelete = () => {
confirmingMusicId.value = null;
};
const confirmDelete = async (id) => {
try {
await axios.delete(`http://localhost:8000/api/musics/${id}/`);
musics.value = musics.value.filter((music) => music.id !== id);
confirmingMusicId.value = null;
} catch (error) {
console.error('Erro ao excluir música', error);
}
};
return {
musics,
confirmingMusicId,
viewMusic,
goToDeleteMusic,
goToEditMusic,
goToAddMusic,
startConfirmDelete,
cancelDelete,
confirmDelete,
};
},
};
</script>
<style scoped></style>
/src/components/ShowMusic.vue:
<template>
<h1>Música:</h1>
<p>Id: {{ music.id }}</p>
<p>Título: {{ music.title }}</p>
<p>Artista: {{ music.artist }}</p>
<button @click="goBack">Voltar</button>
</template>
<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
export default {
setup() {
const music = ref({});
const router = useRouter();
const musicId = router.currentRoute.value.params.id;
const fetchMusicDetails = async () => {
try {
const response = await axios.get(`http://localhost:8000/api/musics/${musicId}/`);
music.value = response.data;
} catch (error) {
console.error("Erro ao buscar dados da música:", error);
}
};
onMounted(fetchMusicDetails);
const goBack = () => {
router.push({ name: 'list-music' });
};
return {
music,
goBack,
};
},
};
</script>
<style scoped></style>
/src/router/index.js:
import { createRouter, createWebHistory } from "vue-router";
import ListMusic from '../components/ListMusic.vue';
import ShowMusic from '../components/ShowMusic.vue';
import EditMusic from '../components/EditMusic.vue';
import AddMusic from '../components/AddMusic.vue';
import DeleteMusic from '../components/DeleteMusic.vue';
const routes = [
{
path: '/',
name: 'list-music',
component: ListMusic,
},
{
path: '/music/:id',
name: 'music-detail',
component: ShowMusic,
},
{
path: '/edit/:id',
name: 'edit-music',
component: EditMusic,
},
{
path: '/add',
name: 'add-music',
component: AddMusic,
},
{
path: '/delete/:id',
name: 'delete-music',
component: DeleteMusic,
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
/src/App.vue:
<template>
<div id="app">
<h1>Lista de músicas UB Social</h1>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
<style scoped></style>
/src/main.js:
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import './assets/styles/global.css';
const app = createApp(App);
app.use(router);
app.mount('#app');
vue.config.js:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})
Elaborado por Mateus Schwede
ubsocial.github.io