I am doing the basic Django polls tutorial and am placing the polls results into Kendo graphs. I Currently have a bar, donut, and bubble chart showing the results for the selected poll question. I want to add a line chart, but instead of using just the results from the selected poll, to also include data from the other questions. Is there a way of doing this, for I am unable to find the answer. I have placed my current code below.
# Javascript in results.html
$("#chart4").kendoChart({
legend: {
position: "bottom"
},
seriesDefaults: {
type: "line"
},
series: [
# Don't know what to do here
{
name: ?
data: ?
}
],
valueAxis: {
labels: {
format: "{0}%"
}
},
categoryAxis: {
categories: [{% for answer in question.answer_set.all %}"{{answer.choice_text}}", {% endfor %}]
}
});
views.py
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('id')
models.py
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('Date Published')
def __str__(self):
return self.question_text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
class Answer(models.Model):
question = models.ForeignKey(Question)
choice_position = models.IntegerField(default=0)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
The easiest way for doing that is querying Django through Ajax. You must define a view that return some data (preferably json) and then call it from your js script.
from django.core import serializers
from django.http import HttpResponse
def all_questions(request):
"""
Most of the time you will have to do some extra work formating the json in
order to match the format that the JS library is expecting.
I can see your graph is expecting something like:
series: [{
name: <some_sustom_name>
data: <array of values>
}
],
So you need to provide to the JS library the data it is expecting.
That means some array (or perhaps similar data structure).
"""
questions = Questions.objects.all()
response = serializers.serialize('json', questions)
# Now response contain a representation of a
# json object that has a property called data
# besides, that property contain a list of Django objects.
response = "{data: %s}" % response
return HttpResponse(response, content_type="application/json")
For more info about the content of response see: Serializing Django Objects
Now, lets assume you got the data in your javascript (the Ajax call was successfull) you will have something like:
{data: [list of serialized Django objects]}
You just have to process list above and extract the data for your graph. Of course you can obtain that list directly from the Django view . That's your call.
See this Kendo demo for more info on what to put in series section of graphs.
Querying Django through Ajax from JQuery.
For this you need code like this:
$.ajax({
type: "GET", // 1
url:"url_to/you_view", // 2
data: { // 3
'zip': zip,
},
success: function(data){ // 4
},
error: function(error){ // 5
alert("Error");
}
});
1 - Specification of the request method (GET, POST, UPDATE, etc ....)
2 - The url pointing to your view.
Note that there are any protocol specification (http). As your js lives in your django app
the url is relative to this app. For this you need some url like:
urlpatterns = patterns('your_app.views',
...
url(r'url_to/you_view', 'your_view')
...
)
3 - Optional, some data to send to de django view. (in your case you don't need it)
4 - This is important, here you will process the data returned by the server when the request success.
Following the example above data here is a json object like:
{data: [...]}
where [...] stands for a list of django serialized objects with jason format.(See the links for documentation).
5 - On error this function will be called instead of that specified by success.
For more info on $.ajax object from JQuery see the $.ajax API Reference.
Related
I am building a map with different station location.
The stations belong to different fields.
I have to show all station and, I have all station of a field.
At some point my api is called
"""Markers API URL Configuration."""
# Maps with Django (2)
# https://www.paulox.net/2021/07/19/maps-with-django-part-2-geodjango-postgis-and-leaflet/
from rest_framework import routers
from map.viewsets import MarkerViewSet
router = routers.DefaultRouter()
router.register(r"map", MarkerViewSet)
urlpatterns = router.urls
then the MarkerViewSet is called (viewsets.py)
class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
"""Marker view set."""
#print(self.kwargs.get('idfield'))
bbox_filter_field = "location"
filter_backends = (filters.InBBoxFilter,)
queryset = Stations.objects.filter(station_active=1, map=1)
serializer_class = StationsSerializer
def get_queryset(self, **kwargs):
#field_id = self.kwargs['idfield']
#print("pp:", self.kwargs)
return Stations.objects.filter(fields_id_field=1)
The code above works, but it only show the markers of the field 1
If I change this to 2
return Stations.objects.filter(fields_id_field=2)
It show all station of the field with the id 2
My url is as the follwoing http://127.0.0.1:8080/map/ to print all stations of all fields.
If I want to have the stations of the field 1, my url will be
http://127.0.0.1:8080/map/1/field
and for the field 2 and 4
http://127.0.0.1:8080/map/2/field
http://127.0.0.1:8080/map/4/field
My problem, I can not not and I do not know of to get the id 1,2,4 or another form my url
this does not work
#field_id = self.kwargs['idfield']
#print("pp:", self.kwargs)
Here is my map/urls.py
an my project urls.py
Should I try to get the id of the field from the viewsets.py or how would you suggest me to archive my goal?
EDIT:
Here is my views.py. Here I can get idfield
Would it have a way to pass it my viewsets.py?
I beleive that your proposition only works in views.py as it start with
def field(request, idfield):
request.GET.get("idfield")
isn't?
I am working with a viewsets.py starting with
class MarkerViewSet(viewsets.ReadOnlyModelViewSet):
"""Marker view set."""
bbox_filter_field = "location"
filter_backends = (filters.InBBoxFilter,)
queryset = Stations.objects.filter(station_active=1, map=1)
serializer_class = StationsSerializer
"""
def get_tags(self):
return Stations.objects.filter(fields_id_field=1)
"""
def get_queryset(self):
#field_id = self.kwargs['idfield']
#print("ppp:", self.kwargs['idfield'])
return Stations.objects.filter(fields_id_field=1)
I do know where your proposition could be added.
However, I think I am getting close to the solution, but I still get error.
I hope you can help me
First, in my map.js file. I added
async function load_markers() {
const markers_url = `/api/map/1/`; // LOOK AT THIS LINE
console.log("markers_url: ",markers_url);
const response = await fetch(markers_url);
//console.log("response: ",response);
const geojson = await response.json();
console.log("geojson: ",geojson);
return geojson;
}
In my urls.py file, I changed to
path("api/<int:idf>/", include("map.api")),
which will redirect to the api.py file. I think, all is correct until this point, isn't?
In my api.py file, I have tried serveral things getting inspirated from the django-rest-framework - router page
router = routers.DefaultRouter()
#router.register(r"map", MarkerViewSet) #INITIAL
#router.register(r"^map/{idf}/$", MarkerViewSet) #I tried this
#router.register(r"^map/(?P<idf>[0-9]+)/$", MarkerViewSet) #this generate an error because of idf
router.register(r"^map/(?P<id_f>[0-9]+)/$", MarkerViewSet)
urlpatterns = router.urls
Another error message is on Firefox:
enter image description here
I believe, I should find the right format, but something else should be wrong
Many thanks for your help
Args
I believe you API urls should be as follows
/maps/ --> returns a list of objects
/maps/<id>/ --> returns a single object
If you do this you should be able to get results according to the given id (if primary key) without doing anything in your view. viewsets automatically returns an object based on the lookup field. If it is not a primary key you can set different lookup field.
Kwargs
in kwargs your URL should be as /maps/?idfield=<id>, then you will be able to do the followings.
kwargs from GET request can be obtained as below
request.GET.get("idfield")
I created a nice Django app, anything worked perfectly. Unfortunely I have now to QUICKLY migrate everything to django-rest-framework and to a VUE frontend. I'm in "charge" just of the backend side, someone else will write the frontend code.
The problem is that I have really complex Django views and I can't figure out how to change them correctly and easily and, most important, how to test them without a frontend.
Here is an example of my view. I created an app that will search on google for some query and then extract the main text from each link. On the same page where you insert the query will be displayed the first 10 links, if you want to scrape more, you have to go to the next page.
This means that on the same view I have the first QueryForm. If a string is passed the scraping starts, if you want to scrape more, a query_id is passed.
def query_create(request):
form = QueryForm(request.POST)
user_p = UserProfile.objects.filter(user=request.user).first()
context={'form1':form1}
if request.method == 'POST' :
if form.is_valid():
query_obj = form.save(commit=False)
query_obj.user = user_p
query_string=query_obj.query
search_engine=query_obj.search_engine
query_obj.save()
data=multi_scraping.scrape(query_obj.search_dict, 0, 10,query_string)
queryset=[]
for index, row in data.iterrows():
link = Link(scraping_id=str(query_obj),
isBlocked=row['isBlocked'],
published_Date=row['published'],
modified_date=row['update'],
language=row['language'],
search_engine=row['search_engine'],
source=row['source'],
source_url=row['source_url'],
title=row['title'],
transcript=row['transcript'],
url=row['url']
)
link.save()
queryset.append(link)
context = {
"object_list": queryset,
"query_id": query_obj.query_id,
"p":query_obj.p,
"user":user_p
}
return render(request, "news/home.html", context)
if(request.POST.get('query_id', None) is not None):
query_id=request.POST.get('query_id', None)
query_obj=Query.objects.get(pk=query_id)
query_string=query_obj.query
query_obj.p=query_obj.p+10
query_obj.save()
data = multi_scraping.scrape(search_dict, query_obj.p, query_obj.p+10, query_string)
queryset = []
for index, row in data.iterrows():
link = Link(scraping_id=str(query_obj),
isBlocked=row['isBlocked'],
published_Date=row['published'],
modified_date=row['update'],
language=row['language'],
search_engine=row['search_engine'],
source=row['source'],
source_url=row['source_url'],
title=row['title'],
transcript=row['transcript'],
url=row['url']
)
link.save()
queryset.append(link)
context = {
"object_list": queryset,
"query_id": query_obj.query_id,
"p": query_obj.p,
"user": user_p
}
return render(request, "news/home.html", context)
Have you got any idea how to help?
anything would be appreciated!!!!!
Since, you are going to use vue.js as your front-end with a
django backend so probably you need to make rest api's for the same. Now django comeup with great features which includes rest
api imeplentation, but then you have to create your api views which
have to be serialized, so that the resultant rest api have either Json
or xml format. In your case, you have to convert the views, so
firstly need to convert your views to django rest serialized format.
So that you can handle the request which are coming from the frontend.
Let's suppose below is your model :
class User(models.Model):
username = models.CharField(max_length=200)
created_at = models.DateTimeField(default=timezone.now)
So now, in order to get all the users data, you have to serialize the user object:
class UserSerializers(serializers.ModelSerializer):
class Meta:
model = User
fields='__all__'
Your output json data look like this:
[
{
"id": 37,
"username": "kevin morgan",
"created_at": "2020-02-02T16:26:16.779900Z"
},
{
"id": 38,
"username": "john smith",
"created_at": "2020-02-02T16:43:04.242829Z"
}
]
You can read here for more info
I'm implementing a website and I need to save in database some variables that comes from HTML. The idea is save the paragraphs of the text that the user marked and save on database to show it when the user access the page (like in medium.com).
When the user click on paragraph I can't refresh the page, I just need to save on database the paragraph id (data-artigo) that was clicked.
That's my view details.html and I need to save in the database the values artigo.pk and lei.pk
<!-- Begin Post Content -->
<div class="article-post">
{% for artigo in artigos %}
<p class='artigo' data-artigo = "{{artigo.pk}}" data-lei = "{{lei.pk}}">
{{artigo}}
</p>
{% endfor %}
</div>
<!-- End Post Content -->
I have a js function that receive those values and set a yellow background (.highlight) to mark the paragraph that was clicked. So I have to save those data in database:
$("p.artigo").on("dblclick",(function(e){
let artigo = $(this).data('artigo');
let lei = $(this).data('lei');
let is_marked;
if ($(this).hasClass( "highlight" )){
$(this).removeClass("highlight");
is_marked = false;
}else{
$(this).addClass("highlight");
is_marked = true;
}
}));
That is the table (my model) when I need to store those data:
class Marcacao(models.Model):
lei = models.ForeignKey(Lei, on_delete=models.CASCADE, verbose_name='Lei', related_name='marcacaoArtigos')
artigo = models.ForeignKey(Lei, on_delete=models.CASCADE, verbose_name='Artigo', related_name='marcacaoLei')
usuario = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='marcacaoUsuário', related_name='marcacaoUsuario')
is_marked = models.BooleanField('Está Marcado?', blank=True, default=False)
description = models.TextField('Descrição', blank = True, null=True)
If I'm not wrong, I think that I need to create a function in the view and pass those data as parameter to this function, but I don't know how to do it.
Your best bet with this depends on how you want this to work.
If the user clicking a paragraph is considered a 'submit' perhaps run an Ajax query from the JS to the view and POST the data back to be put into the model.
However if you want to continue activity on the page consider using Django Rest Framework and creating an API call you can again send off data in an Ajax query to.
My suggestion would be DRF, it's pretty simple to serialize data and save into the model.
https://www.django-rest-framework.org/
You register your api in urls.py:
router = routers.DefaultRouter()
api_patterns = [
router.register(r'selected-paragraph', api.selectedParagraphViewSet)
]
urlpatterns = [
url(r'api/', include(api_patterns)),
Then in your api.py:
class selectedParagraphViewSet(viewsets.ModelViewset):
queryset = Marcacao.objects.all()
serializer_class = MarcacaoSerializer
def create(self, request, *args, **kwargs):
try:
data = {
'lei': request.data.pop('lei'),
'artigo': request.data.pop('artigo'),
'is_marked': request.data.pop('is_marked'),
'usuario': request.user
}
serializer = self.get_serializer(data=data, method='post')
serializer.is_valid(raise_exception=True)
return Response(serializer.data)
except serializers.ValidationError:
print(traceback.print_exc())
raise
Then in your serializers.py:
class MarcacaoSerializer(serializers.ModelSerializer):
def Meta:
model = Marcacao
fields = '__ALL__'
And finally a nice ajax call to send it all off:
var submitData = {
'let': lei,
'artigo': artigo,
'is_marked': is_marked
}
$.ajax({
url: '/api/selected-paragraph',
method: 'PUT',
data: submitData,
dataType: 'json',
crossDomain: true,
xhrFields: {
withCredentials: true
},
success: function(data, stat, xhr) {
console.log("Was a success");
},
failure: function(xhr, stat, err){
console.log('POST error');
console.log((xhr.responseJSON && xhr.responseJSON.msg) ? xhr.responseJSON.msg : '"'+err+'" response when communicating with server.');
}
});
Also good to note is you can eliminate even the need for the create function in api.py if you can pass the request.user in the Ajax call, but I figured this way would show better how the data is input into the database.
I saw swagger documentation of Flask and Django. In Flask I can design and document my API hand-written.(Include which fields are required, optional etc. under parameters sections).
Here's how we do in Flask
class Todo(Resource):
"Describing elephants"
#swagger.operation(
notes='some really good notes',
responseClass=ModelClass.__name__,
nickname='upload',
parameters=[
{
"name": "body",
"description": "blueprint object that needs to be added. YAML.",
"required": True,
"allowMultiple": False,
"dataType": ModelClass2.__name__,
"paramType": "body"
}
],
responseMessages=[
{
"code": 201,
"message": "Created. The URL of the created blueprint should be in the Location header"
},
{
"code": 405,
"message": "Invalid input"
}
]
)
I can chose which parameters to include, and which not. But how do I implement the same in Django? Django-Swagger Document in
not good at all. My main issue is how do I write my raw-json in Django.
In Django it automates it which does not allows me to customize my json. How do I implement the same kind of thing on Django?
Here is models.py file
class Controller(models.Model):
id = models.IntegerField(primary_key = True)
name = models.CharField(max_length = 255, unique = True)
ip = models.CharField(max_length = 255, unique = True)
installation_id = models.ForeignKey('Installation')
serializers.py
class ActionSerializer(serializers.ModelSerializer):
class Meta:
model = Controller
fields = ('installation',)
urls.py
from django.conf.urls import patterns, url
from rest_framework.urlpatterns import format_suffix_patterns
from modules.actions import views as views
urlpatterns = patterns('',
url(r'(?P<installation>[0-9]+)', views.ApiActions.as_view()),
)
views.py
class ApiActions(APIView):
"""
Returns controllers List
"""
model = Controller
serializer_class = ActionSerializer
def get(self, request, installation,format=None):
controllers = Controller.objects.get(installation_id = installation)
serializer = ActionSerializer(controllers)
return Response(serializer.data)
My questions are
1) If I need to add a field say xyz, which is not in my models how do I add it?
2) Quiet similar to 1st, If i need to add a field which accepts values b/w 3 provided values,ie a dropdown. how do I add it?
3) How I add an optional field? (since in case of PUT request, I might only update 1 field and rest leave it blank, which means optional field).
4) Also how do I add a field that accepts the json string, as this api does?
Thanks
I can do all of these things in Flask by hardcoding my api. But in Django, it automates from my models, which does not(as I believe) gives me the access to customize my api. In Flask, I just need to write my API with hands and then integrate with the Swagger. Does this same thing exist in Django?
Like I just need to add the following json in my Flask code and it will answer all my questions.
# Swagger json:
"models": {
"TodoItemWithArgs": {
"description": "A description...",
"id": "TodoItem",
"properties": {
"arg1": { # I can add any number of arguments I want as per my requirements.
"type": "string"
},
"arg2": {
"type": "string"
},
"arg3": {
"default": "123",
"type": "string"
}
},
"required": [
"arg1",
"arg2" # arg3 is not mentioned and hence 'opional'
]
},
Django-rest-framework does have a lot of useful utility classes such as serializers.ModelSerializer which you are using. However these are optional. You can create totally custom API endpoints.
I suggest that you follow the django rest tutorial here. Part one starts with a custom view like this
from django.forms import widgets
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
class SnippetSerializer(serializers.Serializer):
pk = serializers.Field() # Note: `Field` is an untyped read-only field.
title = serializers.CharField(required=False,
max_length=100)
code = serializers.CharField(widget=widgets.Textarea,
max_length=100000)
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES,
default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES,
default='friendly')
def restore_object(self, attrs, instance=None):
"""
Create or update a new snippet instance, given a dictionary
of deserialized field values.
Note that if we don't define this method, then deserializing
data will simply return a dictionary of items.
"""
if instance:
# Update existing instance
instance.title = attrs.get('title', instance.title)
instance.code = attrs.get('code', instance.code)
instance.linenos = attrs.get('linenos', instance.linenos)
instance.language = attrs.get('language', instance.language)
instance.style = attrs.get('style', instance.style)
return instance
# Create new instance
return Snippet(**attrs)
Note in particular that every API field is specified manually and populated by code here. So they do not have to correspond with model fields.
Your questions
1. Custom field xyz :
As I addressed above, just create a custom serialiser and add a line
class SnippetSerializer(serializers.Serializer):
xyz = serializers.CharField(required=False, max_length=100)
...
2. For options in a list, what you're looking for is a "choice" field.
See the Django documention on choice as Swagger is just the same.
3. How do I make a field optional?
Set the kwarg required=False - note that it's set above for field xyz in my example.
4. Best way to accept a JSON string
Two ways to do this.
Just accept a text string and use a JSON parser in the restore_object code
Define a serialiser that consumes / creates the JSON code and refer to it by name as described here
check it the suffix needs to be:
RestangularProvider.setRequestSuffix('/?format=json');
otherwise django was tossing a redirect backed to the client, and while it worked - it was not correct.
The last change is to add:
RestangularProvider.setRequestSuffix('?format=json');
in your module definition, and to remove it from the service definition. This way restangular can build the urls in a way that is expected by the django rest framework back-end. Therefore a service should be setup on the JS side as:
var spaceJam = angular.module('spaceJam', ['ui.bootstrap', 'restangular'])
.config(function (RestangularProvider) {
RestangularProvider.setRequestSuffix('?format=json');
RestangularProvider.setResponseExtractor(function (response, operation) {
// blah
return response ;
})
})
spaceJam.factory('EventsResource', function (Restangular) {
return Restangular.all('events')
})
injected into your controllers and accessed as a list:
EventsResource.getList().then(function(events){
$scope.events = events
})
and to remove an item (fire the HTTP Delete):
$scope.events[someRandomIndex].remove()
and on the server side, you need to make sure the ID is being returned. In django rest framework, this includes a solutions like the one below where a mixin is used to include the id.
Thanks for anybody who has put thought into this.
Making progress - apparently the url was being returned instead of the id. This link http://abesto.net/django-rest-framework-add-id-to-in-hyperlinkedmodelserializer/ outlines adding a mixin o the the serializer that adds the id.in case the link doesn't work in the future, here is the relevant snippet:
class WithPkMixin(object):
def get_pk_field(self, model_field):
return self.get_field(model_field)
class EventSerializer(WithPkMixin, serializers.HyperlinkedModelSerializer):
class Meta:
model = Event
I think the issue is now in my factory method of my resource / restangular resource definition as the Id is appended in the wrong place (i think because I was hacking originally):
spaceJam.factory('EventsResource', function (Restangular) {
return Restangular.all('events/?format=json')
})
so the string '/?format=json' needs to be removed, and added back in at some other point.
Update looks like the method being sent is to root node. I am using angularjs and restangular my javascript looks like:
spaceJam.factory('EventsResource', function (Restangular) {
return Restangular.all('events/?format=json')
})
var CalendarListCtrl = function($scope, $timeout, EventsResource) {
var timeout = 10000
$scope.events = []
$scope.refresh = function (){
EventsResource.getList().then(function(events){
$scope.events = events
})
}
var asychUpdate = function(){
$scope.refresh()
//$timeout(asychUpdate, timeout);
}
asychUpdate()
$scope.delete = function(urlToDelete) {
var eventToDelete = _.find($scope.events, function(one) {
return one.url === urlToDelete;
});
eventToDelete.remove();
$scope.refresh()
}
}
so while the remove method looks to be documented as the way to remove an item from the list - it doesn't seem to have the proper URL.
I am learning django / python as I build a site for a non-profit I am associated with. I am able to issue the gets for the list / array of elements. The "add" (post) method works, and now I am trying to hook up the delete. My server response is :
{"detail": "Method 'DELETE' not allowed."}
So I assume that i need to permission that verb - but I have no idea where. I am using the DJANGO REST Framework. I haven't been able to locate this on google, so I am askng the kind and knowledgeable folks here. Thank you in advance, and please let me know if I need to post more code.
urls.py
router = routers.DefaultRouter()
router.register(r'events', views.EventViewSet)
urlpatterns = patterns('',
(r'^', include(router.urls)),
)
models.py
class Event(models.Model):
title = models.CharField(max_length=200)
recurring = models.BooleanField()
day = models.CharField(max_length=20, blank=True)
date = models.DateField(null=True, blank=True)
time = models.TimeField()
description = models.CharField(max_length=500)
venue = models.CharField(max_length=200, blank=True)
venueAddress = models.CharField(max_length=200, blank=True)
venueCity = models.CharField(max_length=200, blank=True)
serializers.py
class EventSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Event
views.py
class EventViewSet(viewsets.ModelViewSet):
paginate_by = None
queryset = Event.objects.all()
serializer_class = EventSerializer
Answer in original post but includes
making sure the ID is returned in the rest services and that
the angular factory that encapsulates the restangular resource is done such that the URL can be built dynamically, which includes
moving any URL parameters to the request suffix.