DRF+VueJS pagination wrong number of pages - django

I am trying to use DRF pagination backend and VueJs Frontend. I am trying to create pagination links but until now i only get first number. I have added PageNumberPagination to my settings.py. After articles viewset:
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
lookup_field = "slug"
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]
pagination_class = PageNumberPagination
I have used Bootstrap-Vue Pagination
<div class="col" id="article-list" :articles="articles" v-for="article in articles" :key="article.pk"
:per-page="perPage"
:current-page="currentPage">...</div>
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
aria-controls="articles-list"
></b-pagination>
and VueJS script:
export default {
name: "ArticleList",
components: {
HelloWorld,
} ,
data() {
return {
articles: [],
next: null,
loadingArticles: false,
perPage: 2,
currentPage: 1,
size:2,
}
},
methods: {
getArticles() {
this.loadingArticles = true;
let url = "http://localhost:8000/api/articles";
axios.get(url)
.then(response => {
this.articles = response.data.results;
});
},
},
computed: {
rows() {
console.log(this.articles.length);
return this.articles.length;
}
},
created() {
this.getArticles();
},
};
If i check the api address in the browser, I can see the next and previous data.
{
"count": 4,
"next": "http://localhost:8000/api/articles/?page=2",
"previous": null,
"results": [...]
}
How do I change the data of total-rows to make pagination work? Thanks

Simply I added my next to fetching articles, I answer my question as to request, I now use vuex but VueJs is also similar:
fetchArticles (context) {
context.commit("fetchStart");
let endpoint = "articles/"
if (context.state.next) {
endpoint = context.state.next;
}
return ApiService.get(endpoint)
.then(data=>{
console.log(data);
context.commit("setArticles", data.data.results)
if (data.data.next) {
context.commit("setNext", data.data.next);
} else {
context.commit("setNext", null)
}
})
.catch((response) => {
console.log(response);
context.commit("setError", response.data.errors)
})
},
from my vuetemplate I call it for eg:
<script>
import { mapGetters, mapActions} from "vuex";
export default {
name: "Articles",
computed: {
},
methods: {
...mapActions(['articles/fetchArticles']),
getArticles() {
return this.$store.dispatch('articles/fetchArticles');
},
},
created() {
this.$store.dispatch('articles/fetchArticles').
then(() => {
this.isLoading = false;
})
}
};
</script>
and my button
<li class="page-item disabled">
<button
v-show="next"
#click="getArticles"
class="btn btn-sm btn-primary"
>Devamı...</button>
</li>

Related

How do I display Django Rest Framework Foreign Key in Angular?

DISCLAIMER: This is gonna be a pretty long description of a problem, to which I can't seem to find sources for that could help me. So any piece of information is appreciated!
I might add that I am fairly new to Angular and text script.
I have set up my backend with Django and set up the Rest Framework. These are my models:
models.py
class Person(models.Model):
name = models.CharField(max_length = 250, blank = True)
job = models.ForeignKey('Job', on_delete = models.CASCADE, null = True)
def __str__(self):
return self.name
class Job(models.Model):
job_name = models.CharField(max_length = 200, blank = False)
rank = models.PositiveIntegerField(default = 0, unique = True)
def __str__(self):
return self.job_name
In the backend I have set up different job names (Doctor, Teacher, etc...). So when I create a new Person, I get to choose from one of the different jobs, which have been created prior to this. (This is exactly how I woukd want my frontend to work)
Here are my serializer.py, views.py and urls.py files:
serializer.py
class PersonSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Person
fields = ('id', 'name', 'job')
class JobSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Job
fields = ('id', 'job_name')
views.py
class PersonViewSet(viewsets.ModelViewSet):
queryset = Person.objects.all()
serializer_class = PersonSerializer
def list(self, request, *args, **kwargs):
person = Person.objects.all()
serializer = PersonSerializer(person, many = True, context = {'request': request})
return Response(serializer.data)
class JobViewSet(viewsets.ModelViewSet):
queryset = Job.objects.all()
serializer_class = JobSerializer
def list(self, request, *args, **kwargs):
job = Job.objects.all()
serializer = JobSerializer(job, many = True, context = {'request': request})
return Response(serializer.data)
urls.py
router = routers.DefaultRouter()
router.register('person', views.PersonViewSet)
router.register('job', views.JobViewSet)
urlpatterns = [
path('', include(router.urls)),
]
The Rest Framework has successfully been integrated and I can now create, read, update and delete (CRUD) my models.
In Angular I have created a person component with a service, which contains very simple functions, to use CRUD on my frontend.
person.service.ts
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class PersonService {
backendURL = 'http://127.0.0.1:8000'
httpHeaders = new HttpHeaders({'Content-Type': 'application/json'})
constructor(private http:HttpClient) { }
getPerson():Observable<any> {
return this.http.get(this.backendURL + '/person/',
{headers: this.httpHeaders});
}
getOnePerson(id):Observable<any> {
return this.http.get(this.backendURL + '/person/' + id + '/',
{headers: this.httpHeaders});
}
updatePerson(person): Observable<any> {
const body = { name: person.name, job: person.job, };
return this.http.put(this.backendURL + '/person/' + person.id + '/', body,
{headers: this.httpHeaders});
}
createPerson(person): Observable<any> {
const body = { name: person.name, job: person.job, };
return this.http.post(this.backendURL + '/person/', body,
{headers: this.httpHeaders});
}
deletePerson(id): Observable<any> {
return this.http.delete(this.backendURL + '/person/' + id + '/',
{headers: this.httpHeaders});
}
}
And my component ts file:
person.component.ts
import { Component, OnInit } from '#angular/core';
import { PersonService } from '../../services/person.service';
#Component({
selector: 'app-person',
templateUrl: './person.component.html',
styleUrls: ['./person.component.css'],
providers: [PersonService]
})
export class PersonComponent implements OnInit {
person_all = [{ name: ''}, ];
selectedPerson;
constructor(private api: PersonService) {
this.getPerson();
this.selectedPerson = { id:-1, name: '', job: '', }
}
getPerson = () => {
this.api.getPerson().subscribe(
data => {
this.person_all = data;
},
error => {
console.log(error);
}
)
}
personClicked = (person) => {
this.api.getOnePerson(person.id).subscribe(
data => {
this.selectedPerson = data;
},
error => {
console.log(error);
}
)
}
updatePerson = () => {
this.api.updatePerson(this.selectedPerson).subscribe(
data => {
this.getPerson();
},
error => {
console.log(error);
}
);
}
createPerson = () => {
this.api.createPerson(this.selectedPerson).subscribe(
data => {
this.person_all.push(data);
},
error => {
console.log(error);
}
);
}
deletePerson = () => {
this.api.deletePerson(this.selectedPerson.id).subscribe(
data => {
this.getPerson();
},
error => {
console.log(error);
}
);
}
ngOnInit(): void {
}
}
In HTML I created a very simple view, in where I can see every created Person and some buttons to create, update or delete some.
person.component.html
<h2>Person List</h2>
<ul>
<li *ngFor="let person of person_all">
<h2 (click)="personClicked(person)">{{person.nachname}}</h2>
</li>
</ul>
Name <input type="text" [(ngModel)]="selectedPerson.name"/><br>
Job <input type="text" [(ngModel)]="selectedPerson.job"/><br>
<button *ngIf="selectedPerson.id != -1" (click)="updatePerson()"> PUT </button>
<button *ngIf="selectedPerson.id == -1" (click)="createPerson()"> CREATE </button>
<button *ngIf="selectedPerson.id != -1" (click)="deletePerson()"> DELETE </button>
This is my problem: When I try to create a new Person, I don't see the different job names, which I have set up in the backend. I want to know, how do I get a "list" of jobs to select from, similar to how it works in Django with a Foreign Key?
Thanks for the read and thanks in advance!

How to capture object props in Vue snapshot tests

I always get [object Object] in place of object props due to object to string coercion when I use snapshot testing. How can I fix it? I've tried wrapping element into JSON.stringify(), but it causes "Converting circular structure to JSON" Error.
The example of a resulting snapshot:
exports[`SalesList.vue Снапшот десктоп 1`] = `
<magic-grid-stub
class="sales-list"
cols="[object Object]"
gaps="[object Object]"
usemin="true"
>
<sales-item-stub
class="item"
sale="[object Object]"
/>
<sales-item-stub
class="item"
sale="[object Object]"
/>
<sales-item-stub
class="item"
sale="[object Object]"
/>
<sales-item-stub
class="item"
sale="[object Object]"
/>
<sales-info-stub
class="item"
content="additionalInfo"
/>
</magic-grid-stub>
`;
I have the simple corresponding snapshot tests, like this one:
import { createLocalVue, shallowMount } from '#vue/test-utils'
import SalesList from '#/components/sales/SalesList.vue'
let localVue
const fakeSale = {
code: 'code',
description: 'description',
title: 'title',
image: 'image',
archive: false,
visible: true,
date_to: '2020/08/01',
short_description: 'short_description',
slug: 'slug',
date_from: '2020/06/01',
seo: {
seo_description: 'seo_description',
seo_title: 'seo_title',
seo_keywords: 'seo_keywords',
},
}
function createWrapper(component, options) {
return shallowMount(component, {
localVue,
...options,
})
}
beforeAll(() => {
localVue = createLocalVue()
})
describe('SalesList.vue', () => {
it('Снапшот десктоп', async () => {
expect.assertions(1)
const wrapper = createWrapper(SalesList, {
propsData: {
sales: Array.from({ length: 4 }, (_, index) => ({
...fakeSale,
slug: `slug-${index}`,
})),
additionalInfo: 'additionalInfo',
},
mocks: {
$device: { isDesktop: true },
},
})
expect(wrapper.element).toMatchSnapshot()
})
})
And the component in question itself:
<script lang="ts">
import SalesItem from '#/components/sales/SalesItem.vue'
import MagicGrid from '#/components/MagicGrid.vue'
import SalesInfo from '#/components/sales/SalesInfo.vue'
import Vue from 'vue'
export default Vue.extend({
name: 'SalesList',
components: {
SalesItem,
MagicGrid,
SalesInfo,
},
props: {
sales: {
type: Array,
required: true,
},
additionalInfo: {
type: String,
default: null,
},
},
computed: {
colsAndGaps(): {
cols: { 0: number }
gaps: { 0: number }
} {
return this.$device.isDesktopOrTablet
? {
cols: {0: 2},
gaps: {0: 30},
}
: {
cols: {0: 1},
gaps: {0: 16},
}
},
},
})
</script>
<template>
<magic-grid v-bind="colsAndGaps" class="sales-list">
<sales-item
v-for="sale in sales"
:key="sale.slug"
:sale="sale"
class="item"
/>
<sales-info v-if="additionalInfo" :content="additionalInfo" class="item"/>
</magic-grid>
</template>
You could use a custom jest snapshot serializer.
For VueJs 2 you could use https://github.com/tjw-lint/jest-serializer-vue-tjw - but it doesn't work for VueJs 3 (https://github.com/tjw-lint/jest-serializer-vue-tjw/pull/64).
Example configuration for VueJs 2:
npm install jest-serializer-vue-tjw
// package.json
{
...
"jest": {
"snapshotSerializers": ["jest-serializer-vue-tjw"]
}
}

how can i POST data from ant design form into Django Backed?

i trying to post data from ant design React.js into Python Django rest frame work.
so I am using method OnFinish to send data, but its not working.
MY big problem is , i don't know how can i Introduction Data i want to send them data from Form , by using React-redux or something else way , so please Help me .
#react.js Form:
import React, { Component } from "react";
import {
Form,
Input,
Button,
PageHeader,
Select,
DatePicker,
message,
} from "antd";
import "antd/dist/antd.css";
import { connect } from "react-redux";
import axios from "axios";
// defualt setting for django
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.xsrfHeaderName = "X-CSRFToken";
// from layout setting
const formItemLayout = {
labelCol: {
xs: {
span: 24,
},
sm: {
span: 8,
},
},
wrapperCol: {
xs: {
span: 24,
},
sm: {
span: 16,
},
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
};
// end fform layout setting
// const onFinish = (values) => {
// console.log(values);
// axios.post("http://127.0.0.1:8000/api/create/", {
// title: values.title,
// manager: values.manager,
// });
// };
// const title = event.target.elements.title.value;
// const manager = event.target.elements.manager.value;
class ExtrashiftForm extends React.Component {
constructor(props) {
super(props);
this.state = {
Extrashifts: [],
};
}
// componentDidMount() {
// this.fetchExtrashift();
// }
handleSubmit = () => {
axios
.post("http://127.0.0.1:8000/api/create", {
data: {
title: this.target.elements.title.value,
manager: this.data.item.manager,
},
})
.then((res) => {
if (res.status == 200) message.success("data successfully updated!");
this.fetchExtrashift();
})
.catch((err) => {
message.error("data profile failed to update ...");
});
};
render() {
return (
<div>
<Form {...formItemLayout} name="update">
<Form.Item label="Title :">
<Input name="title" placeholder="Put a title here" />
</Form.Item>
<Form.Item label="Manager :">
<Input name="manager" placeholder="Enter manager name" />
</Form.Item>
<Form.Item {...tailFormItemLayout}>
<Button
type="primary"
htmlType="submit"
onFinish={this.handleSubmit}
>
create
</Button>
</Form.Item>
</Form>
</div>
);
}
}
export default ExtrashiftForm;
#back end api/urls.py :
from Extrashift.api.views import ExtrashiftViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'', ExtrashiftViewSet, basename='Extrashift')
urlpatterns = router.urls
#backend : api/views.py:
from rest_framework import viewsets
from Extrashift.models import Extrashift
from .Serializers import ExtrashiftSerializers
class ExtrashiftViewSet(viewsets.ModelViewSet):
serializer_class = ExtrashiftSerializers
queryset = Extrashift.objects.all()
from rest_framework import permissions
from rest_framework.generics import (
ListAPIView,
RetrieveAPIView,
CreateAPIView,
UpdateAPIView,
DestroyAPIView
)
from my back end everything is work but Please help me to i can send only one data from this form.
if is possible please ,change my Code to the Correct code
Nothing spectacular here, you can read the docs
Rather than giving the name as a prop to the Input field.
I've passed it as a prop to Form.Item component
You can check the example here
import React, { Component } from "react";
import {
Form,
Input,
Button,
PageHeader,
Select,
DatePicker,
message,
} from "antd";
import "antd/dist/antd.css";
import axios from "axios";
// defualt setting for django
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.xsrfHeaderName = "X-CSRFToken";
// from layout setting
const formItemLayout = {
labelCol: {
xs: {
span: 24,
},
sm: {
span: 8,
},
},
wrapperCol: {
xs: {
span: 24,
},
sm: {
span: 16,
},
},
};
const tailFormItemLayout = {
wrapperCol: {
xs: {
span: 24,
offset: 0,
},
sm: {
span: 16,
offset: 8,
},
},
};
// end fform layout setting
// const onFinish = (values) => {
// console.log(values);
// axios.post("http://127.0.0.1:8000/api/create/", {
// title: values.title,
// manager: values.manager,
// });
// };
// const title = event.target.elements.title.value;
// const manager = event.target.elements.manager.value;
export default class ExtrashiftForm extends React.Component {
constructor(props) {
super(props);
this.state = {
Extrashifts: [],
};
}
// componentDidMount() {
// this.fetchExtrashift();
// }
handleSubmit = (values) => {
console.log(values)
// axios
// .post("http://127.0.0.1:8000/api/create", {
// data: {
// title: this.target.elements.title.value,
// manager: this.data.item.manager,
// },
// })
// .then((res) => {
// if (res.status == 200) message.success("data successfully updated!");
// this.fetchExtrashift();
// })
// .catch((err) => {
// message.error("data profile failed to update ...");
// });
};
render() {
return (
<div>
<Form {...formItemLayout} name="update" onFinish={this.handleSubmit}>
<Form.Item label="Title :" name="title">
<Input placeholder="Put a title here" />
</Form.Item>
<Form.Item label="Manager :" name="manager">
<Input placeholder="Enter manager name" />
</Form.Item>
<Form.Item {...tailFormItemLayout}>
<Button
type="primary"
htmlType="submit"
>
create
</Button>
</Form.Item>
</Form>
</div>
);
}
}

How to use PUT to update something in Vue using Django REST framework

I am new to Vue but have experience with Django. I am using this boilerplate from Github: https://github.com/gtalarico/django-vue-template
I really like the structure of that boilerplate because it is not overwelming at all and not a lot of code is written to succesfully interact with the back-end API of Django.
It has GET, POST & DELETE already pre-installed and connected to Django REST. So far so good. However I try to add a PUT method to it so I can update models. I try to follow the same structure but I can't get it to work.
My productService.js:
import api from '#/services/api'
export default {
fetchProducts() {
return api.get(`products/`)
.then(response => response.data)
},
postProduct(payload) {
return api.post(`products/`, payload)
.then(response => response.data)
},
deleteProduct(proId) {
return api.delete(`products/${proId}`)
.then(response => response.data)
},
updateProduct(proId) {
return api.put(`products/${proId}`)
.then(response => response.data)
}
}
The updateProduct is the new code I added.
Then in store --> products.js:
const actions = {
getProducts ({ commit }) {
productService.fetchProducts()
.then(products => {
commit('setProducts', products)
})
},
addProduct({ commit }, product) {
productService.postProduct(product)
.then(() => {
commit('addProduct', product)
})
},
deleteProduct( { commit }, proId) {
productService.deleteProduct(proId)
commit('deleteProduct', proId)
},
updateProduct( { commit }, proId) {
productService.updateProduct(proId)
commit('updateProduct', proId)
}
}
const mutations = {
setProducts (state, products) {
state.products = products
},
addProduct(state, product) {
state.products.push(product)
},
deleteProduct(state, proId) {
state.products = state.products.filter(obj => obj.pk !== proId)
},
updateProduct(state, proId) {
state.products = state.products.filter(obj => obj.pk !== proId)
}
}
Here again I added updateProduct.
Then in my Products.vue:
......
<b-tbody>
<b-tr v-for="(pro, index) in products" :key="index">
<b-td>{{ index }}</b-td>
<b-td variant="success">{{ pro.name }}</b-td>
<b-td>{{ pro.price }}</b-td>
<b-td>
<b-button variant="outline-primary" v-b-modal="'myModal' + index">Edit</b-button>
<b-modal v-bind:id="'myModal' + index" title="BootstrapVue">
<input type="text" :placeholder="pro.name" v-model="name">
<input type="number" :placeholder="pro.price" v-model="price">
<b-button type="submit" #click="updateProduct(pro.pk)" variant="outline-primary">Update</b-button>
</b-modal>
</b-td>
<b-td><b-button #click="deleteProduct(pro.pk)" variant="outline-primary">Delete</b-button></b-td>
</b-tr>
.....
<script>
import { mapState, mapActions } from 'vuex'
export default {
name: "Products",
data() {
return {
name: "",
price: "",
};
},
computed: mapState({
products: state => state.products.products
}),
methods: mapActions('products', [
'addProduct',
'deleteProduct',
'updateProduct'
]),
created() {
this.$store.dispatch('products/getProducts')
}
};
</script>
Everything works fine except the PUT action to update a product. I figured that you have to use the ID of a product to be able to edit it with PUT. So that's why I used the same snippet as DELETE. But right now I am still deleting it instead of editing.
I also used now placeholder to display the text of a product entry, which is also not the correct way.. I want to use the modal to edit a product entry and then update it.
Can someone point me in the right direction?

tastypie filter data in front-end

I am using Tastypie to filter data in backend
class Meta:
queryset = Inventory.objects.all()
resource_name = 'inventory'
filtering = {'barcode': ALL}
in api/v1/inventory/?format=json&barcode=1232141542625235624 i got filtered data by barcode
How can i use it and filter it in front-end using AngularJS ?
app.controller('InventoryListCtrl', function($scope, Inventory, Restangular, inventoryItems) {
$scope.inventories = inventoryItems;
};
My State
app.config(function config( $stateProvider, $urlRouterProvider) {
$stateProvider.state('inventory',{
url:'/inventory',
views: {
"main": {
controller: 'InventoryCtrl',
templateUrl: 'inventory/main.tpl.html'
}
},
data:{ pageTitle: 'Inventory' }
}
).state('inventory.listview',{
url:'/listview/',
views: {
"listview": {
controller: 'InventoryListCtrl',
templateUrl: 'inventory/inventory.listview.tpl.html'
}
},
data:{ pageTitle: 'Listview' },
resolve: {
inventoryItems: function(Inventory, $stateParams){
return new Inventory().query();
}
}
})
have my template
<ul style="list-style:none;">
<li>{{inventory.manufacturer}} {{inventory.model}}</li></a>
<li><b>Barcode:</b> {{inventory.barcode}}</li>
<li><b>Holder:</b> {{inventory.user.first_name}} {{inventory.user.last_name}}</li>
<li><b>Tags:</b> {{inventory.tags.tags}}</li>
</ul>
You have to fetch the data from the REST interface using an asynchronous call. See e.g. the documentation for ngResource.