tl:dr
How would a hosted django app correctly transform resource paths to match any hosted location (/ or /test or /testtest)?
Full Description
Let me try to explain what I am trying to do.
I am trying to write a somewhat re-usable django app which I intend to use from within multiple projects. This app is called systemstatus.
The systemstatus app provides a page under '$^' which provides a simple interface to query the system status.
This page makes an ajax query back to the systemstatus app to determine the actual system status and report it on the UI.
The systemstatus app provides a location '^service/$' which points to the ajax call handler.
This page has to somehow figure out the correct URI for the ajax handler depending on where this app is hosted (e.g. under / or /status or /blahblah).
I am wondering what an ideal way of doing this would be. I would say that this applies to other resources bundled inside the app too (stylesheets, images).
Right now I am using request.path to determine what the target path should be. This path is then passed down as a parameter to the template. But this approach will soon become too cumbersome to handle.
def system_status (request):
queryPath = request.path + "service/"
return render_to_response ('systemstatus.html', {'queryPath': queryPath})
My page template looks like this:
function do_ajax () {
$.getJSON ('{{ queryPath }}', function (data) {
$("#status").html (data.status);
});
}
Thanks!
You shouldn't hardcode your urls like that, but use reverse instead!
Django also has a built-in template tag to reverse urls. So you could do something like
function do_ajax () {
$.getJSON ('{% url path.to.my_ajax_view %}', function (data) {
$("#status").html (data.status);
});
}
directly in your template!
You can also send the ajax request directly to your current page's url and check if it is an ajax request or not:
def my_view(request):
if request.is_ajax():
# generate response for your ajax script
else:
# generate the response for normal request
# (render template of your page)
Related
I want to scrape a website using python and send as json the information that I got to my flutter application without saving it to the database.
I don't want to use the Django Restframework database. I want to send a string to the backend and trigger a function that scrapes a certain website and sends back a json to the client. Without using the database.
For simple usage, you can setup a view function, bind it to a url that will accept your string, and inside the function build the logic to scrape the third-party page and return a JsonResponse
Something like below:
in urls.py:
urlpatterns = [
path('my_scrape/<str:input_string>', views.scrape)
]
in views.py:
def scrape(request, input_string):
#scrape a website
url = 'http://google.com/{}'.format(input_string)
third_party_data = BeautifulSoup(requests.get(url).content)
my_payload = { 'data1' : third_party_data.find(text='data1') }
return JsonResponse(my_payload)
Note that this doesn't account for anything like errors and timeouts from the third-party, you will have to handle those errors in your view function, depending on what you receive.
I am working on a project for which I need to build a web interface that will allow interaction with a Raspberry Pi camera. I am using Django and python for the back end. I want to be able to press a button on the web interface that will execute a python script to take a picture with the pi camera. I am thinking I need to use AJAX to complete this, but I don't really understand how I would set this up. I am relatively new to Django itself.
So let's assume you have a Python function that takes a picture with the camera and returns the path to the file.
from my_module import take_pic
my_pic = take_pic()
print(my_pic) # '/path/to/the/picture'
You're saying you don't know what the views.py should look like so I'll assume that you don't have any Django code ready. To create a Django project, install Django and use django-admin startproject NAME.
So what you need is a Django view and it's associated URL. Let's begin with the URL:
# urls.py
from . import views
urlpatterns = [
url(r'^take_pic/', views.take_picture, name='take_pic'),
]
Now, create the views.py module in the same folder that urls.py is in.
# views.py
from django.http import JsonResponse
from my_module import take_pic
def take_picture(request):
my_pic = take_pic()
return JsonResponse({'path': my_pic})
Finally in your Javascript code (most probably in an Django HTML template, rendered with another view, but I'll leave that as an exercise):
// this example uses JQuery, but you can find plain Javascript examples on the net
<button id="my-button"></button>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$(document).ready(function () {
$('my-button').click(function() {
// url name here is what you wrote in urls.py
$.getJSON('{% url "take_pic" %}', function (data) {
var picture_path = data.path;
// add an HTML <img> tag where you need it, using the picture path
// note that your view might need to return a relative path, not absolute
});
});
});
</script>
I think this is a good place to start!
I suppose this should be very simple but I think I'm missing something.
Simple description: I have a page that lists specific words. Each word has an ID. I'm passing this ID to a function, which then posts it to a URL. What I'm attempting to do is to pass the ID, query it in the backend, and get transferred to the edit page with the query result.
Here's my code:
AngularJS function
$scope.editDefinition = function (searchItem) {
var param = { id: searchItem.id };
var url = "/table-based";
$http.post(url, param).success(function (data) {
console.log(data);
});
};
Flask/Python route function
#app.route("/table-based", methods=["GET", "POST"])
def edit_definition():
if request.method == "POST":
j = json.loads(request.data)
uid = j["id"]
cdef = db.getDefinitionById(uid)
return render_template("edit.html", definition=cdef)
return render_template("edit.html")
HTML
<div ng-init="init('{{ definition |tojson|safe }}')" ng-controller="editCtrl">
<ng-include src="'/_partial_edit_form'"></ng-include>
</div>
EditCtrl has the relevant $scope.init function to receive definition. Basically, it returns a response that contains the HTML of the edit.html template, but it does not redirect to the URL.
What gives?
You haven't implemented anything that will cause the browser to redirect to your new template page. In the data returned you should just see the html generated by render_template.
You have two options that I can see. The first would be to store the cdef in flask's g or session object and then reload the page with angular $route.reload() or $window.location.reload() and have flask pick up the cdef from g or session on GET.
The second option would be to refactor your code so that you have angular doing the page rendering, then you just update your definition object on POST. In this case, flask would return json.dumps(cdef) instead of the render template and you would use the angular templating to render it.
The second option is much better, and is pretty much exactly what Angular is designed to do. When I create Angular-Flask apps, I very rarely use render_template, instead I have Angular doing all the rendering after retrieving the data as JSON from a flask-based API.
What I've already done
I have a frontend entirely built with React.JS.
All the business logic is handled by Django and exposed by Django REST Framework via an API.
I'm able to build for different mobile environnements (Android and iOS via Cordova)
The web application is accessible via my Django project (the same that exposes the api), the frontend is sill the same ReactJS code bundled via webpack.
The App has a single entry point, main.js which is a bundled version of my react.js components and dependencies, so my index.html typically looks like this :
<body>
<script type="text/javascript" src="/static/bundles/main-3997ad3476694c3c91cf.js"></script>
</body>
What I want to do
I want to provide a server-side rendering of my web application to let web crawlers correctly index my app on web (I'm not looking for server-side rendering for mobile builds)
How can I handle this considering the fact that my app is a Single Page Application ? I do not want to reinvent the wheel nor to duplicate my code. What kind of node.js server do I have to write in order to achieve this automatic server-side rendering ? Is there any way to provide the server side rendering directly in Django (via some tools reading and interpreting the final results of the page as displayed on the client-side and returning this raw html ?)
You have probably solved your problem by now, but I wanted to share my solution for this.
I have a very similar setup, and have something that seems to work pretty well so far. I basically have a django w/ DRF backend api and isomorphic React/Flux javascript app. I also run a node server next to the python backend server, which acts only as a 'template rendering' service. In essence, replacing the django render function.
So I simply replace the django View with a special IsoView which calls off via http to the node server and returns the rendered html.
from rest_framework.renderers import JSONRenderer
import requests
class IsoView(View):
def user_json(self):
if self.request.user.is_anonymous():
return {'anonymous': True}
else:
return UserSerializer(self.request.user, context={'request': self.request}).data
#classmethod
def render(cls, request, template, data):
req_data = JSONRenderer().render(data)
try:
headers = {'content-type': 'application/json'}
query_params = request.GET
resp = requests.post(_build_url(request.path), params=query_params, data=req_data, headers=headers, timeout=0.1)
reply = resp.json()
if resp.status_code == 302:
return redirect(reply.get('redirect'))
if 'error' in reply:
raise Exception("\n\nRemote traceback: {}".format(reply.get('traceback')))
except requests.exceptions.RequestException as err:
logger.warn('IsoView request exception: {}'.format(err))
reply = {}
return render(request, template, {
'react': reply.get('result'),
'data': data
})
And use it like so:
class HomePage(IsoView):
def get(self, request, *args, **kwargs):
return self.render(request, 'app.html', {
'user': json_data...
})
This also assumes a django template which uses something like this
<html>
<head>
<script>
window.data = {{ data|json }};
</script>
</head>
<body>{{ react|safe }}</body>
</html>
What this does is it renders the html returned from node in the body tag and also dumps the json data required for bootstrapping the app on the client in the window.data object.
This is a really simplified version of the system, but it should work. You should be careful with XSS attacks on the window.data bit, so make sure to escape all your json data but other than that, you should be all good.
Then the node template server looks really similar to any of the tutorials online that you can find for server-side react. Just a simple express app.
Alternatively, you don't need to mess around with django templates at all if you render the full ... in node and return that as a string.
Hope that helps.
I am getting into single-page apps with AngularJS but rather than using Node or similar, I am most comfortable with Python on the server. So, given I am somewhat familiar with Pyramid, I plan to use the pyramid_rpc module to return JSON objects to the client app. That's all straight forward enough, however, what then is the best way to serve the starting index file which contains the AngularJS initial AngularJS app? Usually, static files are served from a static directory, but is there any problem serving a index.html file from the root? Or should I use a view-callable and '/' route with a renderer to a html template? All that being said, is Pyramid overkill for this kind of application? Any advice would be great.
If you're planning to return some JSON responses then Pyramid is a great option. But I wouldn't recommend using pyramid_rpc. JSON-RPC is a protocol that is intended for RPC communication between servers. Straight json responses fit most clients (like browsers) better such as just a bunch of routes that return JSON responses in response to GET/POST requests. This is also a good place to serve up index.html, probably with a good http_cache parameter to prevent clients from requesting that page too often (of course you can go further with optimizing this route, but you should probably save that for later).
config.add_route('index', '/')
config.add_route('api.users', '/api/users')
config.add_route('api.user_by_id', '/api/users/{userid}')
#view_config(route_name='index', renderer='myapp:templates/index.html', http_cache=3600*24*365)
def index_view(request):
return {}
#view_config(route_name='api.users', request_method='POST', renderer='json')
def create_user_view(request):
# create a user via the request.POST parameters
return {
'userid': user.id,
}
#view_config(route_name='api', request_method='GET', renderer='json')
def user_info_view(request):
userid = request.matchdict['userid']
# lookup user
return {
'name': user.name,
}