Projeto biblioteca fullstack Django e Vue.js

Criação de projeto de biblioteca fullstack Django e Vue.js
Voltar
Conteúdo disponível
Github do projeto

Pré-requisitos:

  • Instalar python3 (habilitar privilégios de administrador);
  • Instalar nodejs (npm);
  • Instalar VSCode e extensões para Django, Vue.js e Postman;
  • No diretório de sua preferência, criar pasta do projeto (onde colocaremos as 2 pastas dentro, back-end e front-end);
  • Entrar na pasta do projeto.

Back-end (Django):

Criação de API REST Django, via Django REST framework (DRF), utilizando generic views.

  1. Criar projeto DRF:
    
    pip3 install django djangorestframework markdown django-filter django-cors-headers --break-system-packages
    django-admin startproject mylibrary
    cd mylibrary
    python manage.py startapp library
    
  2. Em mylibrary/settings.py, informar conteúdo faltante:
    
    INSTALLED_APPS = [
        # Outras apps
        'rest_framework',
        'corsheaders',
        'library',
    ]
    
    MIDDLEWARE = [
        # Outro conteúdo
        'corsheaders.middleware.CorsMiddleware',
    ]
    CORS_ALLOW_ALL_ORIGINS = True
    # CORS_ALLOWED_ORIGINS = [
    #    "http://localhost:8080",
    # ]
    
  3. Em library/models.py, criar model Book:
    
    from django.db import models
    
    class Book(models.Model):
        title = models.CharField(max_length=255)
        author = models.CharField(max_length=255)
        published_date = models.DateField()
        isbn = models.CharField(max_length=13, unique=True)
        pages = models.IntegerField()
        cover = models.CharField(max_length=255, blank=True)
        language = models.CharField(max_length=50)
    
        def __str__(self):
            return self.title
    
  4. Em library/serializers.py, criar arquivo de serializadores:
    
    from rest_framework import serializers
    from .models import Book
    
    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            fields = '__all__'
    
  5. Em library/views.py, criar generic views:
    
    from rest_framework import generics
    from .models import Book
    from .serializers import BookSerializer
    
    class BookListCreateView(generics.ListCreateAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
    class BookRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
  6. Em library/urls.py, criar arquivo de routes locais:
    
    from django.urls import path
    from .views import BookListCreateView, BookRetrieveUpdateDestroyView
    
    urlpatterns = [
        path('books/', BookListCreateView.as_view(), name='book-list-create'),
        path('books/<int:pk>/', BookRetrieveUpdateDestroyView.as_view(), name='book-detail'),
    ]
    
  7. Em mylibrary/urls.py, criar routes globais:
    
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/', include('library.urls')),
    ]
    
  8. Executar projeto:
    
    python manage.py makemigrations library
    python manage.py migrate
    python manage.py runserver
    

Testes no Postman:

  1. Listar books: Criar Request, método GET, url http://localhost:8000/api/books/. Confirmar Send. Retornará código status 200 e JSON com lista de books;
  2. Adicionar book: Criar Request, método POST, url http://localhost:8000/api/books/, com Body tipo raw e JSON:
    
    {
        "title": "The Great Gatsby",
        "author": "F. Scott Fitzgerald",
        "published_date": "1925-04-10",
        "isbn": "9780743273565",
        "pages": 180,
        "cover": "hardcover",
        "language": "English"
    }
    
    Confirmar Send. Retornará código status 201 e JSON de book adicionado;
  3. Editar book 1: Criar Request, método PUT, url http://localhost:8000/api/books/1/, com Body tipo raw e JSON:
    
    {
        "title": "EDITADO AQUI The Great Gatsby",
        "author": "F. Scott Fitzgerald",
        "published_date": "1925-04-10",
        "isbn": "9787432735615",
        "pages": 180,
        "language": "English"
    }
    
    Confirmar Send. Retornará código status 200 e JSON de book editado;
  4. Deletar book 1: Criar Request, método DELETE, url http://localhost:8000/api/books/1/. Confirmar Send. Retornará código status 204 sem conteúdo (book deletado).

Front-end (Vue.js):

Criação de projeto Vue.js para consumir, via Axios, API REST Django. Cada componente realizará função específica do sistema. Utilização de Bootstrap 5 para estilização dos componentes e página front-end.

  1. Criar projeto Vue.js:
    
    npm i -g @vue/cli
    vue create library-client
    cd library-client
    npm i axios vue-axios bootstrap@5.3.3 popper.js
    npm run serve
    
  2. Em library-client/src/main.js, alterar conteúdo, vinculando-o a API REST Django:
    
    import { createApp } from 'vue';
    import App from './App.vue';
    import axios from 'axios';
    import VueAxios from 'vue-axios';
    
    import 'bootstrap/dist/css/bootstrap.min.css';
    import 'bootstrap/dist/js/bootstrap.bundle.min.js';
    
    axios.defaults.baseURL = 'http://localhost:8000/api/';
    createApp(App).use(VueAxios, axios).mount('#app');
    
  3. Em library-client/src/App.vue, alterar conteúdo do arquivo de componente principal, para incorporar todos os demais componentes do projeto:
    
    <template>
      <div id="app">
        <h1>Livraria UB Social</h1>
        <AddBook @book-added="fetchBooks" />
        <ListBooks @edit-book="selectBookToEdit" ref="listBooks" />
        <EditBook v-if="selectedBook" :book="selectedBook" @book-updated="handleBookUpdated" @cancel-edit="cancelEdit" />
      </div>
    </template>
    
    <script>
    import ListBooks from './components/ListBooks.vue';
    import AddBook from './components/AddBook.vue';
    import EditBook from './components/EditBook.vue';
    
    export default {
      components: {
        ListBooks,
        AddBook,
        EditBook,
      },
      data() {
        return {
          selectedBook: null,
        };
      },
      methods: {
        fetchBooks() {
          this.$refs.listBooks.fetchBooks();
        },
        selectBookToEdit(book) {
          this.selectedBook = book;
        },
        handleBookUpdated() {
          this.selectedBook = null;
          this.fetchBooks();
        },
        cancelEdit() {
          this.selectedBook = null;
        },
      },
    };
    </script>
    
    <style>
    #app {
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
    }
    
    form input {
      display: block;
      margin: 10px 0;
      padding: 5px;
    }
    
    button {
      margin: 5px;
      padding: 5px 10px;
      cursor: pointer;
    }
    </style>
    
  4. Criar componente library-client/src/components/ListBooks.vue para listagem de books:
    
    <template>
      <div>
        <h2>Livros no acervo:</h2>
        <ul>
          <li v-for="book in books" :key="book.id">
            {{ book.title }} by {{ book.author }}
            <button @click="$emit('edit-book', book)" class="btn btn-warning">Editar</button>
            <button @click="deleteBook(book.id)" class="btn btn-danger">Deletar</button>
          </li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          books: [],
        };
      },
      methods: {
        async fetchBooks() {
          try {
            const response = await this.axios.get(`books/`);
            this.books = response.data;
          } catch (error) {
            console.error('Error fetching books:', error);
          }
        },
        async deleteBook(bookId) {
          try {
            await this.axios.delete(`books/${bookId}/`);
            this.fetchBooks();
          } catch (error) {
            console.error('Error deleting book:', error);
          }
        },
      },
      mounted() {
        this.fetchBooks();
      },
    };
    </script>
    
  5. Criar componente library-client/src/components/EditBook.vue para edição de book:
    
    <template>
      <div v-if="book">
        <h2>Editar livro:</h2>
        <form @submit.prevent="updateBook">
          <input v-model="form.title" placeholder="Title" required />
          <input v-model="form.author" placeholder="Author" required />
          <input v-model="form.published_date" type="date" placeholder="Published Date" required />
          <input v-model="form.isbn" placeholder="ISBN" required />
          <input v-model="form.pages" type="number" placeholder="Pages" required />
          <input v-model="form.cover" placeholder="Cover" />
          <input v-model="form.language" placeholder="Language" required />
          <button type="submit" class="btn btn-success">Confirmar</button>
          <button @click="$emit('cancel-edit')">Cancelar</button>
        </form>
      </div>
    </template>
    
    <script>
    export default {
      props: {
        book: Object,
      },
      data() {
        return {
          form: { ...this.book },
        };
      },
      watch: {
        book(newBook) {
          this.form = { ...newBook };
        },
      },
      methods: {
        async updateBook() {
          try {
            await this.axios.put(`books/${this.book.id}/`, this.form);
            this.$emit('book-updated');
          } catch (error) {
            console.error('Error updating book:', error);
          }
        },
      },
    };
    </script>
    
  6. Criar componente library-client/src/components/AddBook.vue para adição de book:
    
    <template>
      <div>
        <h2>Adicionar novo livro:</h2>
        <form @submit.prevent="addBook">
          <input v-model="form.title" placeholder="Title" required />
          <input v-model="form.author" placeholder="Author" required />
          <input v-model="form.published_date" type="date" placeholder="Published Date" required />
          <input v-model="form.isbn" placeholder="ISBN" required />
          <input v-model="form.pages" type="number" placeholder="Pages" required />
          <input v-model="form.cover" placeholder="Cover" />
          <input v-model="form.language" placeholder="Language" required />
          <button type="submit" class="btn btn-success">Confirmar</button>
        </form>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          form: {
            title: '',
            author: '',
            published_date: '',
            isbn: '',
            pages: '',
            cover: '',
            language: '',
          },
        };
      },
      methods: {
        async addBook() {
          try {
            await this.axios.post(`books/`, this.form);
            this.$emit('book-added');
            this.resetForm();
          } catch (error) {
            console.error('Error adding book:', error);
          }
        },
        resetForm() {
          this.form = {
            title: '',
            author: '',
            published_date: '',
            isbn: '',
            pages: '',
            cover: '',
            language: '',
          };
        },
      },
    };
    </script>
    
  7. Criar componente library-client/src/components/DeleteBook.vue para deleção de book:
    
    <template>
      <button @click="deleteBook">Confirmar deleção</button>
    </template>
    
    <script>
    import axios from 'axios';
    
    export default {
      props: {
        bookId: Number,
      },
      methods: {
        async deleteBook() {
          try {
            await axios.delete(`http://localhost:8000/api/books/${this.bookId}/`);
            this.$emit('book-deleted');
          } catch (error) {
            console.error('Error deleting book:', error);
          }
        },
      },
    };
    </script>
    

Integração do sistema:

Integrar (juntar) back-end Django com front-end Vue.js, para tornar sistema unificado.

  1. Executar back-end (django): python manage.py runserver
  2. Executar front-end (vuejs): npm run serve

Elaborado por Mateus Schwede
ubsocial.github.io