Front: Nuxt Backend: Django REST Framework (DRF) When I started a new project etc. in, it became troublesome to check the settings etc. one by one, so I will summarize it.
However, writing only the configuration file is not enough as an article personally.
So, I plan to write up to the point of implementing membership registration by hitting the API, which is the basic operation, to perform CRUD. This time, DB uses SQLite. If you want to use another RDB such as PostgreSQL or MySQL, just rewrite the DB settings on the DRF side, so please make those settings yourself.
For the time being, this article is about hitting the API created by DRF from Nuxt to update and delete the contents of the database.
The source code can be found at here, so if you have any questions, please take a look.
You can also answer by asking Twitter or in the comments section of this article if you have any questions.
Last time CRUD POST with Nuxt & Django REST Framework
Add view and url as before.
views.py
#View for editing / deleting products
class ItemEditView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = ItemSerializer
queryset = Item.objects.all()
I didn't mention it last time, but I use the generics view because it's easy to use.
The generics.RetrieveUpdateDestroyAPIView
used this time is a view that can GET, PUT, and DELETE information.
The scene to use this view is when you want to combine the product editing and deleting screens like this one.
In addition, this time I am using this which can also be GET to display the original registered data so that it is easy to understand where to change when editing.
See here for how to use view and a list.
urls.py
.....
path('items/<int:pk>/edit',ItemEditView.as_view()),
As was the case last time, this completes the settings.
http://localhost:8000/api/items/1/edit
Let's access.
As mentioned above, the information of the product with id 1 is displayed, and PUT and DELETE can be performed.
Let's move on to the front end.
This time, create ʻedit.vue in the
_id` folder.
_id/edit.vue
<template>
<v-card
light
:loading="isUpdating"
>
<div id="app">
<v-app id="inspire">
<v-container>
<div class="col-12 text-center my-3">
<h3 class="mb-3 display-4 text-uppercase">
{{ item.name }}
</h3>
</div>
<div class="col-md-6 mb-4">
<img
class="img-fluid"
style="width: 400px; border-radius: 10px; box-shadow: 0 1rem 1rem rgba(0,0,0,.7);"
:src="item.image"
alt
>
</div>
<div class="col-md-6">
<div class="item-details">
<h4>Category</h4>
<p>{{ item.category }}</p>
<h4>price</h4>
<p>{{ item.price }}</p>
<h4>Description</h4>
<p>{{ item.description }}</p>
</div>
</div>
</v-container>
<v-form @submit.prevent="editItem">
<v-container>
<v-row>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="item.name"
label="Product name"
required
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="item.category"
:counter="10"
label="Category"
required
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="item.price"
label="price"
required
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="item.description"
label="Description of item"
required
></v-text-field>
</v-col>
<v-col
cols="12"
md="4"
>
<v-file-input
v-model="item.image"
label="Product image"
filled
prepend-icon="mdi-camera"
></v-file-input>
</v-col>
<v-card-actions>
<v-spacer />
<v-btn
class="btn-primary"
:loading="isUpdating"
color="primary"
depressed
@click="isUpdating = true"
type="submit"
>
<v-icon left>
mdi-post
</v-icon>
Update product
</v-btn>
</v-card-actions>
</v-row>
</v-container>
</v-form>
<v-form @submit.prevent="deleteItem">
<button class="btn btn-sm btn-danger">
Delete
</button>
</v-form>
</v-app>
</div>
</v-card>
</template>
<script>
export default {
async asyncData ({ $axios, params }) {
try {
const item = await $axios.$get(`/items/${params.id}`)
return { item }
} catch (e) {
return { item: [] }
}
},
data () {
return {
item: {
name: '',
category: '',
image: '',
price: '',
description: '',
},
preview: '',
autoUpdate: true,
isUpdating: false,
title: 'Product editing',
}
},
watch: {
isUpdating (val) {
if (val) {
setTimeout(() => (this.isUpdating = false), 3000)
}
}
},
methods: {
createImage (file) {
const reader = new FileReader()
const vm = this
reader.onload = (e) => {
vm.preview = e.target.result
}
reader.readAsDataURL(file)
},
async deleteItem () { // eslint-disable-line
try {
const deleteditem = this.item
await this.$axios.$delete(`/items/${deleteditem.id}/edit`) // eslint-disable-line
const newItems = await this.$axios.$get('/items/')
this.items = newItems
this.$router.push('/items')
} catch (e) {
console.log(e)
}
},
async editItem () {
const editeditem = this.item
//if (editeditem.image.includes('http://)') !== -1) {
//delete editeditem.image
//}
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
const formData = new FormData()
for (const data in editeditem) {
formData.append(data, editeditem[data])
}
try {
const response = await this.$axios.$patch(`/items/${editeditem.id}/edit`, formData, config) // eslint-disable-line
this.$router.push('/items')
} catch (e) {
console.log(e)
}
}
}
}
</script>
Then automatically
http://localhost:3000/items/${id}/edit
The routing settings to are generated.
And to be able to dynamically transition from an existing page
_id/index.vue
Update as follows,
_id/index/vue
<template>
<main class="container my-5">
<div class="row">
<div class="col-12 text-center my-3">
<h2 class="mb-3 display-4 text-uppercase">
{{ item.name }}
</h2>
</div>
<div class="col-md-6 mb-4">
<img
class="img-fluid"
style="width: 400px; border-radius: 10px; box-shadow: 0 1rem 1rem rgba(0,0,0,.7);"
:src="item.image"
alt
>
</div>
<div class="col-md-6">
<div class="item-details">
<h4>Category</h4>
<p>{{ item.category }}</p>
<h4>price</h4>
<p>{{ item.price }}</p>
<h4>Description</h4>
<p>{{ item.description }}</p>
<h3>Edit</h3>
<nuxt-link :to="`/items/${item.id}/edit`" class="btn btn-info">
Edit product
</nuxt-link>
</div>
</div>
</div>
</main>
</template>
<script>
export default {
async asyncData ({ $axios, params }) {
try {
const item = await $axios.$get(`/items/${params.id}`)
return { item }
} catch (e) {
return { item: [] }
}
},
data () {
return {
item: {
id:'',
name:'',
category:'',
image:'',
price:'',
description:'',
}
}
},
head () {
return {
title: 'Product Details'
}
}
}
</script>
<style scoped>
</style>
The main change is
nuxt-link :to ~
Added. Note that if you do dynamic routing with nuxt-link
, you will get an error if there is no colon (:) before to.
A product edit page has been added to the detail page
Clicking on that link will take you to the product edit page with the form below.
There is a column for updating and erasing properly, and I confirmed the operation firmly here.
thank you for your hard work! Now you can do CRUD processing as you like!
If you understand the process properly, you can freely create web applications with Nuxt / DRF to some extent.
Next time, instead of making it possible to post to anyone, we will implement around authentication such as membership registration to manage authority.
Recommended Posts