How to Consume Yahoo Finance API in Django - python

I'm trying to learn Django and built a small app that contains a list of companies with some general info for each of them. The home page shows a list of all companies and then a user can click on the company name to see more info. I'm now trying figure out how APIs are consumed in Django by using Yahoo Finance to get some stock data about the company and display it on the page. (I've used the yahoo-finance package multiple times in Python and it's pretty straight forward, which is why I've stared with this). I don't need to save the data to a database (unless that's the only way), I simply want to display it.
I've pip installed the packaged and added it to the APPS in the settings.py file.
Then in my views.py I've added the yahoo-finance dependencies and tried to work in the API in the code below. Then in the template I'm trying to use {{ mkt_cap }}. Doing it this way I'm getting a YQLResponseMalformedError. I realize this may not be the correct way to go about it, but I'm having a hard time figuring it out.
from django.views import generic
from .models import Company, Articles, Transcripts, TranscriptDetails
from yahoo_finance import Share
import json
class CompanyDetails(generic.DetailView):
model = Company
template_name = 'company_details.html'
def get_context_data(self, **kwargs):
pk = self.kwargs.get('pk')
context = super(CompanyDetails, self).get_context_data(**kwargs)
context['articles'] = Articles.objects.filter(company_id=pk).order_by('-date')
context['company'] = Company.objects.get(id=pk)
context['transcripts'] = Transcripts.objects.filter(company_id=pk).order_by('-date')
# Get Yahoo API data
stock_symbol = Company.objects.filter(id=pk).values_list('stock_symbol', flat=True)
data = Share(stock_symbol)
data = json.load(data)
context['mkt_cap'] = data
return context
EDIT
Here's the final code in case someone else has a similar question. I've kept all of the API calls in the View and created a dictionary to pass them to the template.
class CompanyDetails(generic.DetailView):
model = Company
template_name = 'company_details.html'
def get_context_data(self, **kwargs):
pk = self.kwargs.get('pk')
context = super(CompanyDetails, self).get_context_data(**kwargs)
context['articles'] = Articles.objects.filter(company_id=pk).order_by('-date')
context['transcripts'] = Transcripts.objects.filter(company_id=pk).order_by('-date')
# Get Yahoo API data
stock_symbol = self.object.stock_symbol
data = Share(stock_symbol)
stock_open = data.get_open()
year_range = data.get_year_range()
fifty_day_moving_average = data.get_50day_moving_avg()
market_cap = data.get_market_cap()
yahoo_finance = dict()
yahoo_finance['stock_open'] = stock_open
yahoo_finance['year_range'] = year_range
yahoo_finance['fifty_day_moving_average'] = fifty_day_moving_average
yahoo_finance['market_cap'] = market_cap
context['yahoo_finance'] = yahoo_finance
return context
Then in the template to access these I similar use the following:
{{ yahoo_finance.stock_open }}
{{ yahoo_finance.year_range }}
{{ yahoo_finance.fifty_day_moving_average }}
{{ yahoo_finance.market_cap }}

If you want the market cap, you can just do
data = Share(stock_symbol)
market_cap = data.get_market_cap()
context['mkt_cap'] = market_cap
It looks data is a yahoo_finance.Share object that you can use directly. You can look inside with data.__dict__ but should probably just use the methods documented in the API.
Alternatively, you can likely just pass data directly into the template and use {{data.get_market_cap}}.
Hope this helps.

The values_list method returns a list. You are passing this to Share, which expects a string.
The simplest fix would be to change it to:
stock_symbol = Company.objects.filter(id=pk).values_list('stock_symbol', flat=True)[0]
data = Share(stock_symbol)
However, you don't have to fetch the company from the database at all -- the DetailView does this for you. Therefore, you can use self.object.stock_symbol to get the stock symbol
def get_context_data(self, **kwargs):
context = super(CompanyDetails, self).get_context_data(**kwargs)
context['articles'] = Articles.objects.filter(company=self.object).order_by('-date')
context['transcripts'] = Transcripts.objects.filter(company=self.object).order_by('-date')
# Get Yahoo API data
stock_symbol = self.object.stock_symbol
data = Share(stock_symbol)
data = json.load(data)
context['mkt_cap'] = data
return context

Related

Safely store data from GET request - Django

Alright,
Let's say we need to create a website with Django where people can book a lodge for the weekends.
We add a search form on the homepage where people can fill in the check-in and check-out date
to filter for all available lodges.
We use a generic Listview to create an overview of all the lodges and we overwrite the queryset to grab the search parameters from the GET request to create a filtered view.
views.py
class ListingsView(ListView):
"""Return all listings"""
model = Listing
template_name = 'orders/listings.html'
def get_queryset(self):
"""
Visitors can either visit the page with- or without a search query
appended to the url. They can either use the form to perform a search
or supply an url with the appropriate parameters.
"""
# Get the start and end dates from the url
check_in = self.request.GET.get('check_in')
check_out = self.request.GET.get('check_out')
queryset = Calendar.objects.filter(
date__gte=check_in,
date__lt=check_out,
is_available=True
)
return queryset
Now this code is simplified for readability, but what I would like to do, is store the check-in and check-out date people are searching for.
Updated views.py
class ListingsView(ListView):
"""Return all listings"""
model = Listing
template_name = 'orders/listings.html'
def get_queryset(self):
"""
Visitors can either visit the page with- or without a search query
appended to the url. They can either use the form to perform a search
or supply an url with the appropriate parameters.
"""
# Get the start and end dates from the url
check_in = self.request.GET.get('check_in')
check_out = self.request.GET.get('check_out')
queryset = Calendar.objects.filter(
date__gte=check_in,
date__lt=check_out,
is_available=True
)
Statistics.objects.create(
check_in=check_in,
check_out=check_out
)
return queryset
We created a "Statistics" model to store all dates people are looking for.
We essentially add data to a model by using a GET request and I'm wondering if this is the right way of doing things? Aren't we creating any vulnerabilities?
The search form uses hidden text inputs, so there's always the possibility of not knowing what data is coming in. Is cleaning or checking the datatype from these input enough, or will this always be in string format?
Any ideas?
Greetz,

How do you access the primary key from a generic list view?

I am trying to access the primary key of the url from a generic list view. Let me explain. So, I currently have a simple page with a list of student instances from the model student. Each row is dedicated to one student, such as the name, age, email, etc. Now, I want to add a link to each row so that I can view each student's calendar once I clicked it. Right now, the calendar view is a generic ListView like below.
class CalendarView(LoginRequiredMixin, generic.ListView):
model = Class
template_name = 'leads/calendar.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# use today's date for the calendar
d = get_date(self.request.GET.get('month', None))
# Instantiate our calendar class with today's year and date
cal = Calendar(d.year, d.month)
# Call the formatmonth method, which returns our calendar as a table
html_cal = cal.formatmonth(withyear=True)
context['calendar'] = mark_safe(html_cal)
context['prev_month'] = prev_month(d)
context['next_month'] = next_month(d)
return context
However, I want the calendar to show only information related to the student I clicked on. In order to do this, I need to be able to use the primary key(or ID) of the student in my calendar view. Of course, I can embed the primary key in the url, but I have no idea how to access the pk in my generic.ListView. Also, I know some of you might suggest me to switch the view to a function, but I am not going to use a function as I did most of my coding with the generic version. I hope you guys can help, and give me any questions you have.
Here is the html template:
<a href="{% url 'calendar' lead.pk %}" class="text-indigo-600 hover:text-indigo-900">
Calendar
</a>
Here is the url:
path('personal/<int:pk>/calendar/', CalendarView.as_view(), name='calendar'),
Let's say your url path is like so:
path('calendar/<int:studentId>/', CalendarView.as_view(), name='CalendarView'),
Then you can do:
class CalendarView(LoginRequiredMixin, generic.ListView):
def get_queryset(self):
return Class.objects.filter(student__id=self.kwargs['studentId'])

Django Save Multiple Objects At Once

I have been practicing Django for a while now. Currently I am using it in a project where I'm fetching Facebook data via GET requests and then saving it to an sqlite database using Django models. I would like to know how can I improve the following code and save a list of Facebook posts and their metrics efficiently. In my current situation, I am using a for loop to iterate on a list containing several Facebook Posts and their respective metrics which is then associated to the specific Django model and finally saved.
def save_post(post_id, page_id):
facebook_post = Post(post_id=post_id,
access_token=fb_access_token)
post_db = PostsModel(page_id=page_id, post_id=post.post_id)
post_db.message = facebook_post.message
post_db.story = facebook_post.story
post_db.full_picture = facebook_post.full_picture
post_db.reactions_count = facebook_post.reactions_count
post_db.comments_count = facebook_post.comments_count
post_db.shares_count = facebook_post.shares_count
post_db.interactions_count = facebook_post.interactions_count
post_db.created_time = facebook_post.created_time
post_db.published = facebook_post.published
post_db.attachment_title = facebook_post.attachment_title
post_db.attachment_description = facebook_post.attachment_description
post_db.attachment_target_url = facebook_post.attachment_target_url
post_db.save()
post_db is a Django model object instantiated using PostsModel while Post is a normal Python Class which I wrote. The latter is simply a collection of GET requests which fetches data from Facebook's Graph API and returns JSON data whereby I associate relevant data to class attributes (message, 'shares_count`).
I read about the bulk_create function from Django's documentation but I don't know how to pass on the above. I also tried using multiprocessing and Pool but the above function does execute. Right now, I am just iterating sequentially on a list. As the list increases in length, it takes more time to save.
def create(self, request):
page_id = request.data['page_id']
page = get_object_or_404(PagesModel, pk=page_id)
post_list = get_list_or_404(PostsModel, page_id=page_id)
for post_id in post_list:
save_post(post_id=post_id, page_id=page)
The above function gets an already saved list from the database for a specific page based on the page_id. Then, the for loop iterates on each post in the list and its post_id and page instance are sent to the save_post function to fetch its data and save it.
Huge thanks if anyone can suggest a more effective way to tackle this. Thank you.
You are going in the right direction with the bulk_load. Generate a list of the PostsModel objects and then use bulk_create to upload them into the database. An important note here is that it won't work if the posts already exist in the database. For updating posts, try the bulk_update.
def save_post(post_id, page_id):
facebook_post = Post(post_id=post_id,
access_token=fb_access_token)
post_db = PostsModel(page_id=page_id, post_id=post.post_id)
post_db.message = facebook_post.message
post_db.story = facebook_post.story
post_db.full_picture = facebook_post.full_picture
post_db.reactions_count = facebook_post.reactions_count
post_db.comments_count = facebook_post.comments_count
post_db.shares_count = facebook_post.shares_count
post_db.interactions_count = facebook_post.interactions_count
post_db.created_time = facebook_post.created_time
post_db.published = facebook_post.published
post_db.attachment_title = facebook_post.attachment_title
post_db.attachment_description = facebook_post.attachment_description
post_db.attachment_target_url = facebook_post.attachment_target_url
return post_db
def create(self, request):
page_id = request.data['page_id']
page = get_object_or_404(PagesModel, pk=page_id)
post_list = get_list_or_404(PostsModel, page_id=page_id)
post_model_list = [save_post(post_id=post_id, page_id=page) for post_id in
post_list]
PostsModel.objects.bulk_create(post_model_list]

Django Haystack: How to index field from another class

I have a django model Story which I am successfully able to index using templates. However there is another model Reviews which has a static method which takes Story object and returns ratings as Integer. How can I index Story on ratings also.
{{ object.story_name }}
{{Reviews.ratings(object)}}
I tried to call this method in template story_text.txt, but that results in an error.
django.template.exceptions.TemplateSyntaxError: Could not parse the remainder: '(object)'....
Edit:
I tried using below in template, it doesn't give any error while building the index. But how can I now refer to this field while searching using SearchQuerySet
Reviews.average_start_rating( {{object}} )
I am confused. I don't think that you can use syntax like {{ Reviews.rating object }} with template engine in Django. If it is possible, that is what I didn't know.
Why don't you pass what you want to show in template in Context in the first place?
{{ object }} could be rendered because it has object in Context. For example, if you use UpdateView(class based view), It contains object in Context automatically.
class Example(UpdateView):
model = yourClass
form_class = yourFormClass
template_name = yourhtml
success_url = URL redirect page after success
you can use {{object}} in yourhtml.html because of UpdateView. you give pk number in url conf like (?P<pk>[0-9]+).
you can do like this without UpdateView
class anotherExample(View):
def get(self, request, *args, **kwargs):
render(request, 'yourhtml.html', {"object": Class.objects.get(id=self.kwargs['pk'])})
in form view, you can use
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['object'] = Class.objects.get(id= ... )
return context
my idea is passing story object and review object which has FK of story object together in context.
I was able to get it working using haystack advanced-data-preparation.
Advanced Data Preparation
Using an additional field one can have a prepare method for that. However only issue is I can order the data using this field but can't search using it.
class StoryIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
ratings = indexes.FloatField()
def prepare_ratings(self, obj):
return Reviews.ratings(obj)
def get_model(self):
return Story
Instead of using a template for the text field, here you can use the prepare or prepare_FOO methods:
class StoryIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True)
# text = indexes.CharField(document=True, use_template=True)
# ratings = indexes.FloatField()
def prepare_text(self, obj):
return "\n".join(f"{col}" for col in [obj.story_name, Reviews.ratings(obj)])
def get_model(self):
return Story

Initialising the flask-wtf form when the page loads

Whenever the flask server is started, all the forms are initialised at that time itself. The data is interconnected between the pages, so for one form, the choices come from the database and those choices can be edited using another form on another page but after the choices are updated on that page, they remain the same for the first form. To get the new values I need to restart the server. Is there any way to refresh the values without restarting the server?
This is how the form looks like
class AddExpenses(Form):
reason = wtforms.StringField('reason', [validators.Required()])
amount = wtforms.IntegerField('amount', [validators.Required()])
allnames = []
allnames = getSalesman()
salesperson = wtforms.SelectField('salesperson', choices=[names for names in allnames])
submitfield = wtforms.SubmitField('Submit')
getSalesman() function is used to query the database and get the choices.
Why not get the choices in the view function that requires the form? For example
#app.route('/index')
def index():
form = AddExpenses()
allnames = getSalesman()
form.salesperson.choices = [names for names in allnames]
...
You may also want to use some caching on getSalesman() if the database will not change often.

Categories

Resources