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.
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 have two related models (Events + Locations) with a serialzer shown below:
class Locations
title = models.CharField(max_length=250)
address = model.CharField(max_length=250)
class Events
title = models.CharField(max_length=250)
locations = models.ForeignKey(Locations, related_name='events'
class EventsSerializer(serializers.ModelSerializer):
class Meta:
model = Events
depth = 1
I set the depth to 1 in the serializer so I can get the information from the Locations model instead of a single id. When doing this however, I cant post to events with the location info. I can only perform a post with the title attribute. If I remove the depth option in the serializer, I can perform the post with both the title and location id.
I tried to create a second serializer (EventsSerialzerB) without the depth field with the intention of using the first one as a read-only response, however when I created a second serializer, viewset, and added it to the router, it would automatically override the original viewset.
Is it possible for me to create a serializer that outputs the related model fields, and allows you to post directly to the single model?
// EDIT - Here's what I'm trying to post
$scope.doClick = function (event) {
var test_data = {
title: 'Event Test',
content: 'Some test content here',
location: 2,
date: '2014-12-16T11:00:00Z'
}
// $resource.save() doesn't work?
$http.post('/api/events/', test_data).
success(function(data, status, headers, config) {
console.log('sucess', status);
}).
error(function(data, status, headers, config) {
console.log('error', status);
});
}
So when the serializers are flat, I can post all of these fields. The location field is the id of a location from the related Locations table. When they are nested, I can't include the location field in the test data.
By setting the depth option on the serializer, you are telling it to make any relation nested instead of flat. For the most part, nested serializers should be considered read-only by default, as they are buggy in Django REST Framework 2.4 and there are better ways to handle them in 3.0.
It sounds like you want a nested representation when reading, but a flat representation when writing. While this isn't recommended, as it means GET requests don't match PUT requests, it is possible to do this in a way to makes everyone happy.
In Django REST Framework 3.0, you can try the following to get what you want:
class LocationsSerializer(serializers.ModelSerializer):
class Meta:
model = Locations
fields = ('title', 'address', )
class EventsSerializer(serializers.ModelSerializer):
locations = LocationsSerializer(read_only=True)
class Meta:
model = Events
fields = ('locations', )
class EventViewSet(viewsets.ModelViewSet):
queryet = Event.objects.all()
serializer_class = EventsSerializer
def perform_create(self, serializer):
serializer.save(locations=self.request.data['locations'])
def perform_update(self, serializer):
serializer.save(locations=self.request.data['locations'])
A new LocationsSerializer was created, which will handle the read-only nested representation of the Locations object. By overriding perform_create and perform_update, we can pass in the location id that was passed in with the request body, so the location can still be updated.
Also, you should avoid having model names being plurals. It's confusing when Events.locations is a single location, even though Locations.events is a list of events for the location. Event.location and Location.events reads a bit more clearly, the Django admin will display them reasonably, and your fellow developers will be able to easily understand how the relations are set up.
I'm fair new to Django and I am trying to log visitor for my blog. I'm using generic view for my blog and here is part of code:
#blog/urls.py
urlpatterns = patterns('',
#index
url(r'^(?P<page>\d+)?/?$', PostListView.as_view(
model=Post,
paginate_by=3,
)),
#individual post
url(r'^(?P<pub_date__year>\d{4})/(?P<pub_date__month>\d{1,2})/(?P<slug>[a-zA-Z0-9-]+)/?$',
DetailView.as_view(model=Post,)),
#cat
url(r'^category/(?P<slug>[a-zA-Z0-9]+)/?$', CategoryListView.as_view(
paginate_by=3,
model=Category,
)),
#tag
url(r'^tag/(?P<slug>[a-zA-Z0-9]+)/?$', TagListView.as_view(
paginate_by=3,
model=Tag,
)),
and I wrote a simple model for visitor log:
#tasks/models.py
class Visitor(models.Model):
visit_stamp = models.DateTimeField(auto_now_add=True)
referer = models.CharField(max_length=100, blank=True)
ip = models.IPAddressField(blank=True)
user_agent = models.CharField(max_length=100, blank=True)
page = models.CharField(max_length=100)
and its view:
#tasks/views.py
def log(request, page):
try:
hit = Visitor()
hit.page = page
hit.ip = request.META.get('REMOTE ADDR', '')
hit.last_visit = datetime.now()
hit.referer = request.META.get('HTTP REFERER', '')
hit.user_agent = request.META.get('HTTP_USER_AGENT', '')
hit.save()
except IntegrityError:
pass
def tracking(request, page):
log(request, page)
return render_to_response(page)
My question is how and where can I call this methods so that I can log a user is visiting a specific page. I'd appreciate any advices.
First off, I assume you don't have access to the apache (or whatever host is running your django app) logs and/or you want to eventually add other things and/or you want it available in the database, as otherwise, you can skip a lot of work and just grep the logs.
Anyways, I'd recommend rewriting track to work as a decorator (and adjust log as you need it... note that I believe you can get the URL from the request object versus passing it in as a page value in case you want to know which specific instance was visited). There are also ways you could probably do this with middleware, but this gives you a pretty good mix of simplicity and ability to control which views get logged.
To borrow an example from http://www.djangofoo.com/253/writing-django-decorators
def track(page):
def decorator(func):
def inner_decorator(request, *args, **kwargs):
log(request, page)
return func(request, *args, **kwargs)
return wraps(func)(inner_decorator)
return decorator
And then in your urls (or you can also do #track to decorate function based views)
url(r'^(?P<page>\d+)?/?$', track("index")(PostListView.as_view(
model=Post,
paginate_by=3,
))),
url(r'^someregexp$', track("pagename")(SomeListView.as_view(
model=Post,
paginate_by=3,
))),
Edit: meant to add. Do note that in general, GET requests are supposed to be idempotent; logging is a gray area but the main thing to keep in mind is that some requests may not get logged as you might expect if the page is cached (for Posts this shouldn't be an issue)
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.
I'm a bit of a newbie in Python, so go easy on me. I'm writing an AJAX handler in Django. Everything's been pretty straight-forward on this until now. I've been banging my head against this bit for the better part of a day. I'd like to return a JSON string that contains a dict that contains a queryset:
#
# models.py
#
class Project(models.Model):
unique_name = models.CharField(max_length=32, unique=True)
title = models.CharField(max_length=255, blank=True)
description = models.TextField('project description', blank=True)
project_date = models.DateField('Project completion date')
published = models.BooleanField()
class ProjectImage(models.Model):
project = models.ForeignKey('Project', related_name='images')
image = models.ImageField(upload_to=get_image_path)
title = models.CharField(max_length=255)
sort_metric = models.IntegerField()
#
# views.py
#
...
projects = Project.Project.objects.filter(published=True)
...
response_dict({
'success' : True,
'maxGroups' : 5, # the result of some analysis on the projects queryset
'projects' : projects
});
# This one works if I remove 'projects' from the dict
# response = json.dumps( response_dict )
# This one works only on projects
# response = serializers.serialize( 'json', response_dict, relations=('images') )
return HttpResponse( response, mimetype='application/javascript' )
I've commented out the two serialization lines, because:
The first one seems to only work with 'simple' dicts and since projects is included in my dict, it fails with [<Project: Project object>] is not JSON serializable
The second one seems to only work with querysets/models and since the 'outer' part of my dict is non-model, it complains that 'str' object has no attribute '_meta'. Note that I am using the wadofstuff serializer with the understanding that it would resolve the OneToMany relationship as defined in my model. But even when I get this working by only serializing projects, I do not have any of my ProjectImages in the output.
QUESTION 1: What is the best way to serialize the whole response_dict? Surely, I'm not the first person to want to do this, right?
QUESTION 2: Why am I unable to get the ManyToOne relationship to work?
Many thanks for your help.
UPDATE: Just found this one: Django JSON Serialization with Mixed Django models and a Dictionary and it looked promising, but I get 'QuerySet' object has no attribute '_meta' =(
You can't serialize a python object like that. There is a section in the django documentation on what to do.
https://docs.djangoproject.com/en/dev/topics/serialization/#id2
Here is the key part to look at:
json_serializer.serialize(queryset, ensure_ascii=False, stream=response)