DjangoRestFramework - How do I customize the frontend? - django

This is my basic view using DjangoRestFramework:
class user_list(APIView):
"""
List all users, or create a new user.
"""
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request):
serializer = UserSerializer(data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
When I go to the URL which calls this view (localhost:8000/CMS/users), DjangoRestFramework already has a frontend which says:
GET /CMS/users
HTTP 200 OK
Vary: Accept
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
[
{
"username": "t",
}
]
How do I customize this? I want to use AngularJS on the frontend. I know that without DjangoRestFramework, I would return an html template which would be located in my Django App directory in a folder called "Templates". I've been told that DjangoRestFramework is useful becauase I can create a RESTful API which returns JSON objects. I tried adding the following to my settings.py file:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
)
}
but I still can't seem to figure out how I can customize the frontend of my Django application. I know that there is:
renderer_classes = (TemplateHTMLRenderer,)
in which I can return an html page in my view, like so:
return Response({'user': self.object}, template_name='user_detail.html')
but doesn't returning actual HTML pages defeat the purpose of DjangoRestFramework's ability to return JSON objects and create a RESTful API?

First off, there are some things to make Django Rest Framework play better with angular. Until I decided to try out ember, I was working through this utorial. To try to provide a useful example for your question:
Typically you want to put your DjangoRestFramework REST Api in something like /api.
If you're doing the seemingly ubiquitiious single page application, you can then have index.html get served up as a template (i.e. just have your default home view be a TemplateView with index.html as the template)
You would then have your angular html templates and your angular/jquery/etc. javsascripts and your css files as normal static files. NB that you generally don't want to have django templates generate angular code. First off, it makes things much more of a pain to debug, and secondly, the default angular and django template syntax use the same <% characters meaning you have to be careful to make sure one doesn't try to interpret the others templating code.
When you load http://yourserver/, this will bring up the index.html page which then pulls down angular stuff, which then starts making RESTful calls to your REST api to generate the data. Note that you can start mixing in stuff to use Django forms etc., but I'd recommend keeping it simple and then reading the linked article (or another) once you have the basics going.

Related

django rest framework defaultRouter without models

I am new to Python and Django (rest framework) and I love the DefaultRouter feature, by also giving an "API ROOT" page. But, I can only use the defaultrouter.register() method with viewsets. How to make a custom route without a model? For example I will have a "POST" route that multiplies 2 values from the body. (so body is something like {"value1":123, "value2":456}
Is it good practice to use viewsets for everything?
How should I implement a custom (multiply) function? I will have a serializer with the 2 params? And where to implement the code?
Am I doing things right?
EDIT1: I added the following code to my views.py
#api_view(['GET'])
def test1(request):
"""
Simple test
"""
data = {
"test": True
}
return Response(data, status=status.HTTP_200_OK)
Then I added the route to my urls.py. But, as expected, the hyperlink doesnt show up in the api root.
I am seeing this in the API Root:
So this is missing my function based view.
Why is there a distinction here?

How does interacting with the django rest api through the url work?

I get that the Django rest framework is for interacting with the Django server programmatically but one thing I still don't understand is how.what i want to do is have my client app (mobile app) send data (somehow) to the Django server in order to create/retrieve data based on variables and obviously this has to be done through the URL since there will be no direct GUI interaction with the API. (unless I'm mistaken, which I probably am) I have gone through the official documentation and followed the tutorial to the end and still don't understand how this is supposed to work.all I ask for is a quick and simple explanation because I have searched everywhere and haven't found a simple enough explanation to grasp the core concept of how this is all supposed to work.
I think what you're looking for is JSONResponse and related objects:
This will allow you to send JSON in response to a request.
from django.http import JsonResponse
def my_view_json(request):
response = JsonResponse({'foo': 'bar'})
return response
If your templates or webpages need to make a request to a view and specify different parameters, they can do so by adding POST variables (examples). These can be parsed in the view like so:
def myView(request):
my_post_var = request.POST.get('variable_name', 'default_value')
my_get_var = request.GET.get('variable_name', 'default_value')
You can then parse what was sent any way you like and decide what you want to do with it.
Basically,
You define the URLS upon which you perform Get/POST/PUT Requests and You can Send Data to that.
Eg:
urls.py
from django.conf.urls import url,include
from app import views
urlpatterns = [
url(r'^(?i)customertype/$',views.CustomerViewSet.as_view()),
url(r'^(?i)profile/$', views.Save_Customer_Profile.as_view()),
url(r'^(?i)customer_image/$', views.Save_Customer_Image.as_view()),
]
Now Whenever User would send a Request to:
example.com/profile ==> This would be received in the Save_Customer_Profile View based on the Method Type, Save_Customer_Profile is as follows:
class Save_Customer_Profile(APIView):
"""Saves and Updates User Profile!"""
def get(self, request, format=None):
return AllImports.Response({"Request":"Method Type is GET Request"})
def post(self, request, format=None):
return AllImports.Response({"Request":"Method Type is Post Request"})
def put(self,request, format=None):
return AllImports.Response({"Request":"Method Type is Put Request"})
I think the OP was referring to how to do GET/POST request programmatically. In that case, it is enough to do (values are dummy):
GET:
import requests
r = requests.get('http://localhost:8000/snippets/')
print(r.json())
print(r.status_code, r.reason)
POST:
data = {'code': 'print(" HELLO !!!")', 'language': 'java','owner': 'testuser'}
r = requests.post('http://localhost:8000/snippets/', data=data, auth=('testuser', 'test'))

Testing Wagtail page views using client.get

I want to test my views functionality for a Wagtail Page that uses the RoutablePageMixin. I've found that Wagtail includes some testing functionality, but I'm trying to check the content on different urls. Although the Wagtail test functions work, testing using self.client.get doesn't work -- I get an 404 response. I'm trying the following test:
def test_subpage(self):
response = self.client.get(
self.page.full_url + self.page.reverse_subpage('subpage')
)
self.assertEqual(response.status_code, 200,
'Request the open positions page')
I assume the error lies in the way in which the page is created. I have used several ways, but cannot find one for which this works. The most intuitive way I found to create the page is the following:
def setUp(self):
self.login()
parent = Page.get_root_nodes()[0] # Home
self.assertCanCreate(parent, MyPage, {
'title': 'Title!',
'title_sv': 'Title2!',
'slug': 'test',
'published': datetime.datetime.now(),
})
self.page = MyPage.objects.get(slug='apply')
The subpages have been manually tested and do seem to work.
The simplest way to create a page within test code (or any other code...) is:
parent = Page.objects.get(url_path='/home/')
page = MyPage(title='Title!', slug='test', body='...')
parent.add_child(instance=page)
Note that Page.get_root_nodes()[0] will not return your site homepage - the tree root is a non-editable placeholder, and site homepages are usually children of it. You can create additional children of the root, but unless you give them a corresponding Site record, they won't be accessible under any URL (which is probably why you're getting a 404 in your test).

Django REST Framework - 405 METHOD NOT ALLOWED using SimpleRouter

I'm using the SimpleRouter tuorial within the docs.
Just to test I've created a temporary Authentication class:
class BackboneBasicAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
user = User.objects.filter(username="james")
return (user, None)
settings look like this
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'core.rest_authentication.BackboneBasicAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
}
Submitting a PUT request returns a 405 METHOD NOT ALLOWED
{"detail": "Method 'PUT' not allowed."}
I've tried with X-HTTP-Method-Override as well. No go.
Any ideas what I'm doing wrong?
I've spent a whole day trying to figure this out, hopefully someone can help! :)
The simple router adds the put attribute to the view for a url matching the pattern you supply with the pk added as an additional pattern element.
For example if you used:
simple_router.register('widgets/', WidgetViewSet)
The framework will create two url patterns:
'^widgets/$'
'^widgets/<?P<pk>[^/]+/$'
I am guessing that you are only trying urls that satisfy the first match for which the viewset instance will only have 'get' ('list') and 'post' ('create') support added by the framework so it will cause the error you are seeing if you try to put/patch or delete. For those methods to work you need to supply the pk so that the framework knows which widget you are modifying or deleting and so that your url matches the view that supports those methods.
This is confusing and you may choose not to use the simple_router at all if you find it too confusing. Then you can specify your own method mapping so that the rest_framework will dispatch to your put methods e.g.
url('^widgets/<?P<pk>[^/]+/$', WidgetViewSet.as_view({'put': 'update',
'get': 'retrieve',
'patch': 'partial_update',
'delete': 'destroy'}...)
To me that seems to be caused by the routed viewset not implementing or not allowing PUT requests. If it was an authentication issue, you would get a 401 UNAUTHORIZED status code.

Django-RestFramework 2 and backbone.js routing

I am terribly confused about the routing and url set up when using backbone.js and django together with the Django-restframework.
Where does the the template fit in when using a REST framework?
For example, i have a class based view defined for one of my urls where i want to use backbone.js to just update the div displaying students:
url(r'^home/students/$', views.StudentList.as_view()),
class StudentList(APIView):
"""
List all students
"""
def get(self, request, format=None):
students = Person.objects.filter(person_type = Person.STUDENT)
serializer = PersonSerializer(students)
return Response(serializer.data, "core/teachers/teacher_teaching.html")
def pre_save(self, obj):
obj.owner = self.request.user
How does Backbone routes fit in with the url routing of django. I have in a file router.js, something like this:
function($,jqueryui, _, Backbone, HomeView, StudentsView) {
var AppRouter = Backbone.Router.extend({
routes: {
// Define some URL routes
':home': 'showStudents',
'users': 'showContributors',
// Default
'*actions': 'defaultAction'
}
});
var initialize = function(){
var app_router = new AppRouter;
app_router.on('route:showStudents', function(){
// Call render on the module we loaded in via the dependency array
var studentsView = new StudentsView();
studentsView.render();
});
Yet the routes never actually get to my views?
Where does the the template fit in when using a REST framework?
It doesn't; Django templates are for normal Django HTML pages, not REST framework APIs. REST framework API responses are programmatically-generated documents (typically JSON or XML), so they don't need templates.
How does Backbone routes fit in with the url routing of django
They are 100% completely separate. Let's say you have a URL like:
www.example.com/foo/bar#baz
Django will handle this much of that URL:
www.example.com/foo/bar
while Backbone will handle the remaining:
#baz
Of course, that assumes that Django returns a webpage that uses Backbone; if it doesn't, the #baz will never even come in to play.
As a quick summary, when you visit a URL like the above, your browser will ask www.example.com/ for /foo/bar. This is when your urls.py in Django comes in; it has:
url(r'^foo/bar/?$', views.FooBar.as_view()),
so your webserver knows to send back whatever views.FooBar.as_view() gives it (whether that is REST framework-produced JSON document or an old school Django HTML document).
Once that document comes back to the server, it renders it, and this is where client-side code like Backbone and its router take their turn. The Backbone Router (whenever you call Backbone.history.start()) looks at the URL (ie. window.location) and looks for the anchor or "hash" part of the URL (eg. #baz). It then compares that part of the URL to its route list (similar to how your webserver compared the earlier part to urls.py) and if it finds a match, it "sends you to a page".
It's key to understand though, it doesn't really send you anywhere at all; all its really doing is changing the hash part of the URL and then running some Javascript which manipulates the DOM. Therefore, it is impossible for the Backbone Router to ever send you to a Django URL; if you want to hit a server route, you need to either use AJAX or window.location =.