I would like to access and print metadata (attributes and values) served by an ERDDAP server, which is a variety of OPeNDAP, on my Django website.
so I prepared a simple example function called get_conventions to access a metadata field on this public server hosting data and metadata. To get started, I install the required packages:
$ pip install pandas erddapy
and then,
import pandas as pd
from erddapy import ERDDAP
def get_conventions(dataset_id):
e = ERDDAP(server='https://gliders.ioos.us/erddap/', protocol='tabledap', response='csv')
url = e.get_info_url(dataset_id, response='csv')
df = pd.read_csv(url)
# this replace spaces with underscores in column names
df.columns = [col_name.replace(' ', '_') for col_name in df.columns]
conventions = df[df.Attribute_Name == 'Conventions'].Value
return conventions
Using a Python interpreter, one could call the function like this with this sample dataset id as an argument (amelia-20180501T0000), which is found on the server; the output follows:
>>> get_conventions('amelia-20180501T0000')
6 Unidata Dataset Discovery v1.0, COARDS, CF-1.6
Name: Value, dtype: object
>>>
I would like my website to print on a webpage the output of the above function.
I can print the argument string in a page (model.py, views.py and a related html templates - those being similar to the Django tutorial), but I am unsure how to refer the argument to the function (and expect a return) or how/where to integrate the function in the Django structure.
The argument is stored in a local database which can be referred using models/views/template
But I am unsure how to integrate the part involving the function in Django?
The approach is going to depend on whether the function is going to be used cross-app, in a single app, or is tied to a Django model.
For cross-app functions, I typically create a separate app called core or utils with python files for each set of functionality. For the sake of your example let's call it erddapy.py. In the views.py that your view is in, simply from utils.erddapy import get_conventions and call it in your view passing in the argument. Then return the result as context to the template.
If it's only being used in a single app but not tied to a model you can also create a utils.py file in that app and instead do from .utils import get_conventions in your views.py then call it in your view.
If you've defined a model for the datasets and store the dataset id there it's probably better suited for a model method. That way you can use the queryset API in your view to get the instance then simply call instance.get_conventions(). You wouldn't need an argument for this approach since you could simply get the dataset id from self.dataset_id in the method logic.
# view
id = self.kwargs.get('id')
if id:
instance = get_object_or_404(MyModel, id=id)
data = instance.get_conventions()
# return the data as context in an http response
# model method
class MyModel(models.Model):
# model fields
def get_conventions(self):
dataset_id = self.dataset_id
# get the data
I guess the underlying question is how are you storing or obtaining the dataset id? If it's in a separate local database you may need to query it to get the dataset id which I would do in a separate function. Then either pass it into the function as an argument or store it in your Django db as a model instance and use the method approach explained above.
I was able to make this work after reading this, by D. Patel.
It involved adding the following to the file named services.py which was placed an app subfolder titled templatetags. This is where the get_conventions custom function reside:
from django import template
...
register = template.Library()
#register.filter
def get_conventions(dataset_id):
...
return conventions
and in the template, I had to load the services file and add the following to the html template:
{% load services %}
{% block content %}
<p><strong>Conventions:</strong> {{ station.dataset_id | get_conventions }}</p>
...
{% endblock %}
station.dataset_id being the argument for the get_conventions function paired | with the return value
and it worked as intended. It was quite straightforward.
Related
I have a form and am passing the variable as below using Jinja template
<form action = "/user_data/{{period}}" method="POST">
It is not redirecting required page /user_data/Oct-2022
But while just using {{period}} for testing in html page, variable is returning the result as Oct-2022. Not sure why the same variable is not getting passed in the form action.
Variable is printing as below in html page,
{{period}}
But it is not printing in the form,
<form action = "/user_data/{{period}}" method="POST">
This is the route method,
#app.route("/user_listing/<period>", methods = ['POST', 'GET'])
def user_data(period):
....
....
return render_template('user_data.html', period=period)
First we imported the Flask class. An instance of this class will be our WSGI application.
Next we create an instance of this class. The first argument is the name of the application’s module or package. If you are using a single module (as in this example), you should use name because depending on if it’s started as application or imported as module the name will be different ('main' versus the actual import name). This is needed so that Flask knows where to look for templates, static files, and so on. For more information have a look at the Flask documentation.
We then use the route() decorator to tell Flask what URL should trigger our function.
The function is given a name which is also used to generate URLs for that particular function, and returns the message we want to display in the user’s browser.
I have a simple Flask web app. My index template has various ways of interacting with clients using javascript and HTML. I am also have a form that, upon submission, routes to another flask process and uses the request.form command to retrieve user-submitted data.
However, I want to do something a little different. I would like to initiate a Flask redirection upon javascript event but include a parameter, and not use form.
For example, my index.html file would display something like this after template rendering:
function startRedirect(parameter) {
window.location.pathname = '/myRedirect';
}
<input type="checkbox" id="sample" name="sample" onChange="startRedirect(parameter);">
And part of my Flask script would have:
#app.route('/myRedirect')
def myRedirectFunction():
# do something with the parameter here
return render_template('index.html')
I realize this can be done with using a form, but I am interested in accomplishing this task without having a form. I was thinking about somehow using request.args, but don't quite understand what to do.
You can use a dynamic route to capture a simple input and pass it to the route's function.
app.route('/myRedirect/<param>')
def myRedirectFunction(param='hello world'):
return render_template('index.html', param=param)
Using this route as a redirect, you can pass a single param (or multiple if you serialize them) that you can use to do something. From there, you can either display or you can redirect again to a common endpoint so the user does not see the param in the url.
There's no need for a form or an explicit redirect, just attach a route and some parameter to the dynamic route.
Let's say you have a model to list the departments in your company:
class Departments(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True)
Now you have a department selection page:
#app.route('/departments_home', methods=['GET'])
def departments_home():
departments = Departments.query.all()
return render_template('departments_home.html',
departments=departments)
On the frontend you might have a variety of selections, each giving a link to the same route but with a different department_id:
{% for department in departments %}
Click to go to {{ department.name }}
{% endfor %}
Now you just need another route to handle this, taking the variable department_id that was passed in the GET request:
#app.route('/load_department/<department_id>', methods=['GET'])
def load_department(department_id):
department = Departments.query.get(int(department_id))
department_data = # do stuff here with the specific department
I'm currently trying to create a custom ModelView for a detail_view using a template on Flask Admin. However I'm struggling to figure out how to access the instance of a model that the user is viewing the details of.
This is my custom ModelView:
class ShopModelView(MyModelView):
can_view_details = True
details_template = "custom_detail_view.html"
#expose('/details/', methods=('GET', 'POST'))
def details_view(self):
self._template_args['all_transactions'] = #current_shop_object#.transactions.order_by(Transaction.timestamp.desc())
return super(ShopModelView, self).details_view()
Looking at this post, the class of the model can be obtained using self.model, however this returns the class rather than the instance of the specific model being accessed.
The documentation on Templates and ModelView doesn't seem to explain it.
How can I get the instance of the specific model being accessed?
Turns out that the current instance of the model can be accessed via the variable model inside the jinja2 template file. So instead of parsing the variable as a template argument like I was trying to: self._template_args['all_transactions'] = #current_shop_object#.transactions.order_by(Transaction.timestamp.desc()),
{{ model.transactions.all() }} achieves the result I was aiming for.
Need to access URL by name at model, can't just hardcode it. Need it for error message for a new object creating. Any suggestions?
Update: Just need to put url to error message, not reverse
Your question is not totally clear, but I think you are asking about the reverse function.
You can define get_absolute_url method in your model and than access it in other model's methods. Check https://docs.djangoproject.com/en/2.1/ref/models/instances/#get-absolute-url
I suggest you use a template tag. You can build one for your model and avoid polluting the model about stuff not related to the domain level and keep the presentation level to the template.
Check the docs here on how add a templatetags your app.: https://docs.djangoproject.com/en/2.1/howto/custom-template-tags/
Here a snippet of code to use as starting point for your url generation
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def url_for_object(context, object):
# you have both the context and the object available to
# generate your url here
url = ....
return url
In your template use
{% url_for_object my_object %}
This is using Google App Engine. I am not sure if this is applicable to just normal Django development or if Google App Engine will play a part. If it does, would you let me know so I can update the description of this problem.
class MessageModel(db.Model):
to_user_id = db.IntegerProperty()
to_user = db.StringProperty(multiline=False)
message = db.StringProperty(multiline=False)
date_created = db.DateTimeProperty(auto_now_add=True)
Now when I do a query a get a list of "MessageModel" and send it to the template.html to bind against, I would like to include a few more properties such as the "since_date_created" to output how long ago since the last output, potentially play around with the message property and add other parameters that will help with the layout such as "highlight" , "background-color" etc...
The only way I thought of is to loop through the initial Query Object and create a new list where I would add the property values and then append it back to a list.
for msg in messagesSQL:
msg.lalaland = "test"
msg.since_created_time = 321932
msglist.append(msg)
Then instead of passing the template.html messagesSQL, I will now pass it msglist.
You should still be able to send it messagesSQL to the template after you've added elements to it via the for loop. Python allows that sort of thing.
Something else that might make sense in some cases would be to give your MessageModel methods. For instance, if you have a
def since_date_created(self):
'''Compute the time since creation time based on self.date_created.'''
Then (assuming you have "messagesSQL" in the template), you can use the function as
{% for msg in messagesSQL %}
{{ msg.since_date_created }}
{% endfor %}
Basically, you can call any method in the model as long as you it needs no arguments passed to it.
You can obtain that by defining methods in the model
like
class MessageModel(db.Model):
# Definition
def since_date_created(self):
# ...
Now in the template, you can use it like
Time since created {{ message.since_date_created }}