Render data passed from Django to Vue - django

I get my data through axios with this:
get_questions: function (){
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = 'X-CSRFToken'
axios.post('{% url 'sw_app:api_get_questions' %}', this.test)
.then(response=>{
this.questions = response.data.questions
console.log(response.data.questions)
})
}
Here's the view function:
def do_get_questions(request):
data = json.loads(request.body.decode('utf-8'))
test_code = data['code']
is_owner = check_test_owner(request, test_code)
response = {}
if is_owner:
# Get Questions
questions = models.Question.objects.filter(test__code=test_code)
ser_questions = serializers.serialize('json', questions)
response['result'] = 'ok'
response['message'] = 'Questions fetched!'
response['questions'] = ser_questions
return JsonResponse(response)
else:
response['result'] = 'failed'
response['message'] = 'You are not the owner of this test!'
return JsonResponse(response)
It returns this:
[{"model": "sw_app.question", "pk": 2, "fields": {"test": 40, "content": "What is the phrase that beginner coders commonly use to display a string on the screen?"}}]
models.py for reference:
class Question(models.Model):
test = models.ForeignKey(Test, on_delete=models.CASCADE)
content = models.CharField(max_length=900)
def __str__(self):
return "Question: {} - Test: {}".format(self.id, self.test.id)
Back in my vue (template), I store the questions here:
data: {
test: {
code: '',
},
questions: {}
},
Now when I do this:
<li class="list-group-item" v-for="question in questions" :key="question.pk">
[[ question.content ]]
</li>
It just display a lot of empty list objects. When I try doing this:
<li class="list-group-item" v-for="question in questions" :key="question.pk">
[[ question]]
</li>
It displays this:
Any ideas? Thanks a lot!

Suppose you have 2 questions so we can show them using v-for.
const app = new Vue({
data() {
return {
test: {
code: '',
},
questions: [{"model": "sw_app.question1",
"pk": 1,
"fields": {
"test": 40, "content": "Content for question 1"
}
},
{"model": "sw_app.question2",
"pk": 2,
"fields": {
"test": 40, "content": "Content for question 2"
}
}
],
}
},
})
app.$mount("#app")
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<li class="list-group-item" v-for="question in questions" :key="question.pk">
{{ question.pk}} - {{ question.fields.content }}
</li>
</div>

Related

vue3 filtered data on computed with chartjs

im learning about vuejs 3 and for example i want to show a chartjs with a combo to filter data. (Without the combo, everything works correctly)
i have a parent component chartjs.vue where import the chartjs and the combo. when i select a year, i change the variable and show it on the template
<script setup>
import ChartJsBarChart from '#/views/charts/chartjs/ChartJsBarChart.vue'
import SelectYear from '#/componentes/selectYear.vue'
const api = ref('exp')
const year = ref([])
function selectYear(x) {
year.value = x
}
</script>
<template>
<!-- 👉 Latest Statistics -->
<VCol cols="24" md="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Choose a year</VCardTitle>
</VCardItem>
<VCardText>
<SelectAno #input="selectAYear" />
</VCardText>
</VCard>
</VCol>
<VCol cols="24" md="12">
<VCard>
<VCardItem class="d-flex flex-wrap justify-space-between gap-4">
<VCardTitle>Total</VCardTitle>
</VCardItem>
<VCardText>
<ChartJsBarChart :yearSelected="year" :url="api" />
</VCardText>
</VCard>
</VCol>
</template>
one child component with a select (to try props and emit) selectYear.vue
<script setup>
import { ref, defineEmits, watch } from 'vue'
const emit = defineEmits(['input'])
let selected = ref([])
const years = [
'2005',
'2008',
'2009',
'2010',
'2011',
'2012',
'2014',
'2015',
'2016',
'2017',
'2018',
'2019',
'2020',
'2021',
'2022',
'2023',
]
watch(
() => selected.value,
(newValue, oldValue) => {
change(newValue)
},
)
const change = val => emit('input', val)
</script>
<template>
<VSelect
v-model="selected"
:items="years"
label="choose a year"
/>
</template>
and last child component with the chartjs ChartJsBarChart.vue
<script setup>
import BarChart from '#/libs/chartjs/components/BarChart'
import axios from '#axios'
import { ref, onMounted, computed } from "vue"
import { useStatesStore } from '#/store/states'
const props = defineProps({
url: {
type: null,
required: true,
},
yearSelected: {
type: null,
required: true,
},
})
const apiData = ref([]) //data from API
const data = ref({}) // data parsed to chartjs
const store = useStatesStore()
const states = ref(store.states) // legend
const filterByYear = computed(() => {
return apiData.value.filter(item => item.year === '2005')
})
onMounted(async () => {
await axios
.get('/api/dashboard/expProvActiv')
.then(res => {
apiData.value = res.data['hydra:member']
})
})
</script>
<template>
{{ props.yearSelected }}
<hr>
{{ apiData }}
<BarChart
:height="400"
:chart-data="data"
/>
</template>
the result (shortened) of {{ apiData }} with hundred of items is:
[ { "state": "state 1", "activity": "string", "subactivity": "string", "year": 2015, "surface": "3.5500", "total": 3, "rcs": 3 }, { "state": "state 2", "activity": "string", "subactivity": "string", "year": 2016, "surface": "10.9400", "total": 13, "rcs": 13 }]
if i try to show filterByYear, the first time i have not data and how can i do to call filterByYear when i change the year?
[answer myself] the year was string instead number
how can i update the child component BarChart to update the information?
thank you!

DRF+VueJS pagination wrong number of pages

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>

Upload Image File along with regular Form Data

I am using django with VueJS. The data is updating properly in my database.
I need to accomplish two things:
Post the correct content to the field image_file.
Get the downloaded image file pasted onto the servers folder which is media/shop/images
My attempted code is as below:
models.py
...
image_file = models.ImageField(upload_to='shop/images/', blank=True, null=True)
urls.py
...
urlpatterns += [
url(r'^Post-Items-Axios$', myviews.Post_Items_Axios, name='Post-Items-Axios'),
]
views.py
#api_view(['GET', 'POST', 'PUT', 'DELETE'])
def Post_Items_Axios(request):
if request.method == 'POST':
data_itemfullhd = request.data['Item Name']
data_image_file = request.data['Item Image File']
td_items, created = Md_Items.objects.get_or_create(
itemfullhd = data_itemfullhd)
td_items.imagefl = data_imagefl
td_items.image_file = data_image_file
td_items.save()
data = { 'data_itemfullhd': data_itemfullhd }
return Response(data)
bm_home.html
<template id="product-edit">
<div>
<h2>Product details</h2>
<form method="post" enctype="multipart/form-data">{% csrf_token %}
<div class="form-group">
<label for="edit-name">Item Name</label>
<input class="form-control" id="edit-name" v-model="product.itemfullhd" required/>
</div>
<!-- Upload single Image files -->
<div class="form-group">
<label for="edit-imagefile">Image</label>
<input type="file" id="edit-imagefile" #change="onFileChanged" required/>
</div>
<button type="submit" class="btn btn-primary" #click.prevent="updateProduct">Save</button>
<a class="btn btn-dark"><router-link to="/product-list">Cancel</router-link></a>
</form>
</div>
</template>
Vue template
var ProductEdit = Vue.extend({
template: '#product-edit',
data: function () {
return {
product: findProduct(this.$route.params.product_id),
selectedImage: null,
};
},
methods: {
onFileChanged (event) {
this.selectedImage = event.target.files[0]
this.product.image_file = this.selectedImage.name
},
updateProduct: function () {
let product = this.product;
products[findProductKey(product.id)] = {
id: product.id,
itemfullhd: product.itemfullhd,
image_file: product.image_file,
};
const data = {
"Item Name": product.itemfullhd,
"Item Image File": product.image_file,
}
// const formData = new FormData()
// formData.append('image_file', this.selectedImage, this.selectedImage.name)
// axios.post('/biggmount_home/Post-Items-Axios', formData, data)
axios.post('/biggmount_home/Post-Items-Axios', data)
router.push('/product-list');
},
}
});
The changes below have given me the result that I was looking to achieve:
Vue template
var ProductEdit = Vue.extend({
template: '#product-edit',
data: function () {
return {
product: findProduct(this.$route.params.product_id),
selectedImage: null,
};
},
methods: {
onFileChanged (event) {
this.selectedImage = event.target.files[0]
},
updateProduct: function () {
let product = this.product;
const formData = new FormData()
formData.append('Item Image', this.selectedImage, this.selectedImage.name)
formData.append('Item Name', product.itemfullhd)
axios.post('/biggmount_home/Post-Items-Axios', formData)
router.push('/product-list');
},
}
});
views.py
#api_view(['GET', 'POST', 'PUT', 'DELETE'])
def Post_Items_Axios(request):
if request.method == 'POST':
data_itemfullhd = request.data['Item Name']
td_items, created = Md_Items.objects.get_or_create(
itemfullhd = request.data['Item Name'],
)
td_items.image_file = request.data['Item Image']
td_items.save()
return HttpResponse('Success!')

Django: fullcalendar drag events and save

I 'm trying to drag the events and store them in the database with getJSON
javascript file :
<script type='text/javascript'>
function saveMyData(event, dayDelta, minuteDelta) {
$.getJSON("{% url events_drag %}", {'title': event.title, 'start': event.start,
'end': event.end}, function(data) {
});
}
</script>
<script type='text/javascript'>
$(document).ready(function() {
$('#eventFilterCalendar').fullCalendar({
eventDrop: function(event, dayDelta, minuteDelta) {
saveMyData(event);
},
eventResize: function (event, dayDelta, minuteDelta) {
saveMyData(event);
},
</script>
views :
def eventsdrag(request):
print 'events...'
untitre= request.GET['title']
unstart= request.GET['start']
unend= request.GET['end']
print 'untitre', untitre
print 'unstart', unstart
print 'unend', unend
Event.title = untitre
Event.start = unstart
Event.end = unend
Event.save()
event_list = []
event_list.append({
...
'start': Event.start,
'end': Event.end,
'title': Event.title,
})
return http.HttpResponse(json.dumps(event_list),
content_type='application/json')
model :
class Event(models.Model):
title = models.CharField(_(u"Nom de l'étude"), max_length=100)
start = models.DateTimeField(_(u"début"))
end = models.DateTimeField(_('fin'))
the function eventdrag should read event.title, event.start, event.end and save them in the database
but request.GET don't work..I display 'events..' and nothing else..

twitter typeahead.js autocomplete remote not working

I have a site with stocks. I would like to add typeahead functionality to my bootstrap template. Since there are about 5000 stocks and will be even more in the future. I am using haystack with whoosh index. I should be using remote version of typeahead.js, but it is not working. Could you please take a look and tell me what am I missing?
<script type="text/javascript">
var stocks = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.name);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 5,
remote: {
url: "/search/autocomplete/",
replace: function(url, query) {
return url + "?q=" + query;
},
filter: function(stocks) {
return $.map(stocks, function(data) {
return {
tokens: data.tokens,
symbol: data.symbol,
name: data.name
}
});
}
}
});
stocks.initialize();
$('.typeahead').typeahead(null, {
name: 'stocks',
displayKey: 'name',
minLength: 1, // send AJAX request only after user type in at least X characters
source: stocks.ttAdapter()
});
</script>
This is my form
<form class="input-prepend" method="get" action="/search/">
<div id="remote">
<button type="submit" class="btn">Search</button>
<input type="text" class="typeahead" id="id_q" placeholder="Stock symbol or name" autocomplete="off" name="q">
</div>
</form>
Urls.py
url(r'^search/autocomplete/', 'stocks.views.autocomplete'),
url(r'^search/', include('haystack.urls')),
autocomplete view
from haystack.query import SearchQuerySet
import json
def autocomplete(request):
sqs = SearchQuerySet().autocomplete(content_auto=request.GET.get('q', ''))[:5]
array = []
for result in sqs:
data = {"symbol": str(result.symbol),
"name": str(result.name),
"tokens": str(result.name).split()}
array.insert(0, data)
return HttpResponse(json.dumps(array), content_type='application/json')
json response:
[{"tokens": ["Arbor", "Realty", "Trus"], "symbol": "ABR", "name": "Arbor Realty Trus"}, {"tokens": ["ABM", "Industries", "In"], "symbol": "ABM", "name": "ABM Industries In"}, {"tokens": ["AmerisourceBergen"], "symbol": "ABC", "name": "AmerisourceBergen"}, {"tokens": ["ABB", "Ltd", "Common", "St"], "symbol": "ABB", "name": "ABB Ltd Common St"}, {"tokens": ["Allianceberstein"], "symbol": "AB", "name": "Allianceberstein "}]
This is my domain name: digrin.com and this is autocomplete url.
What am I missing?
I can see two problems:
1) Your script declaration is missing a type attribute:
<script src="http://code.jquery.com/jquery-1.11.0.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.js"></script>
<script type='text/javascript' src="http://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>
add "type='text/javascript'" to the script declarations for jquery and bootstrap.
A more modern way of declaring your script tags can be found here.
2) To initialise Typeahead you need to place the code into your jQuery ready method i.e.
$(function(){
var stocks = new Bloodhound({
datumTokenizer: function (datum) {
return Bloodhound.tokenizers.whitespace(datum.name);
},
queryTokenizer: Bloodhound.tokenizers.whitespace,
limit: 5,
remote: {
url: "/search/autocomplete/",
replace: function(url, query) {
return url + "?q=" + query;
},
filter: function(stocks) {
return $.map(stocks, function(data) {
return {
tokens: data.tokens,
symbol: data.symbol,
name: data.name
}
});
}
}
});
stocks.initialize();
$('.typeahead').typeahead(null, {
name: 'stocks',
displayKey: 'name',
minLength: 1, // send AJAX request only after user type in at least X characters
source: stocks.ttAdapter()
});
});
As it is currently the typeahead code wont get loaded.