API Integration in Django - python

I am trying to use the OpenDOTA API in my pet project. At the moment, I am having problem displaying the content of the API into my CBV.
My views.py:
from django.views.generic import TemplateView
import requests
import json
# Create your views here.
class HeroList(TemplateView):
template_name = 'dota/heroes.html'
url = 'https://api.opendota.com/api/heroes'
r = requests.get(url)
r.text
result = r.json()
I am lost on how to call the json in my HTML. I've tried running the same code in python IDLE, and when I type the "result" and hit enter, it gives my the dict. Any idea on how should I display the dict into my template?

What you need to do is first dump your json to a dictionary format.
import json
from django.shortcuts import render
rdict = json.loads(r.json())
return render(request, template_name=<template name>, context=rdict)
All this reside insides your function inside your views.py
Now after this using Django template language - https://docs.djangoproject.com/en/1.11/topics/templates/
You can render data in keys in your dictionary to your template.

If you mean accessing the result in html, then below is a sample.
choices = {'key1':'val1', 'key2':'val2'}
Here's the template:
<ul>
{% for key, value in choices.items %}
<li>{{key}} - {{value}}</li>
{% endfor %}
</ul>
from this answer how to access dictionary element in django template?

Related

Rendering bs4.Tag's text with Django Templates

I'm trying to render the text from bs4 Tags using Django Template Language.
For some reason bs4.Tag.text isn't accessible from template. I can render it's text only if I get it before and pass itself to template instead of Tag object.
Here is code that I used:
from django.template import Template, Context
from bs4 import BeautifulSoup
html_string = '<p>Some text.</p>'
soup = BeautifulSoup(html_string)
tag = soup.find('p')
template = Template('{{ tag.text }} - {{ tag_text }}')
context = Context({'tag': tag, 'tag_text': tag.text})
print(template.render(context))
Expected output:
Some text. - Some text.
Actual output:
- Some text.
I feel confused a bit. For my purposes it's necessary to pass raw bs4.Tag objects to extract text and other attributes after. Hope you can help me
You can try writing a custom templatetag for this.
app/templatetags/bstags.py
from django import template
register = template.Library()
#register.filter
def get_attr(obj, attr):
return getattr(obj, attr)
and then in templates call it as:
{% load bstags %}
{{ tag|get_attr:text }}

How to generate absolute urls in Django 2 templates

I have an html template, which is used to render an email, in that template, I want to attach verification links.
I am using the following code to generate the link
{% url 'verify_email' token=token email=email %}
but this one generates following URL instead of absolute URL.
I read this SO thread
How can I get the full/absolute URL (with domain) in Django?
and some initial google results but all of them seems old and not working for me.
TLDR: How do I generate absolute URLs in Django2 template files
You can use the build_absolute_uri() referenced in the other thread and register a custom template tag. Request is supplied in the context (that you enable via takes_context) as long as you have django.template.context_processors.request included in your templates context processors.
from django import template
from django.shortcuts import reverse
register = template.Library()
#register.simple_tag(takes_context=True)
def absolute_url(context, view_name, *args, **kwargs):
request = context['request']
return request.build_absolute_uri(reverse(view_name, args=args, kwargs=kwargs))
More on where and how to do that in the docs.
Then you can use the tag in your template like this:
{% absolute_url 'verify_email' token=token email=email %}

I want to use the if statement based on the existence of a web page

So basically i have a complicated scenario. I am current using Django to build a website and i have current made two apps. Both apps have almost identical fields. The field I would want to focus on though is the Information field(which they both have and which i have auto generated with the help of the Wikipedia model)
So the case here is that I want to create an if and else statement in the html in such a way that if the page i am hyperlinking to exists it would go to the link dealing with DetailView but if it doesnt exist I would redirected to the Create View
I should also note that the two apps have their names linked with the help of the foreign key but when i try to open information links using the same name , they gave me different pks
I dont feel like i explained my problem well enough but I hope someone can understand what i mean
UPDATE
ok I create the get function using
def get(self, request, *args, **kwargs):
try:
self.object = self.get_object()
except Http404:
return redirect('/create/')
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
but i dont know how to use the CreateView fuction i created instead of the link i put
This is the Detail View Html
{%extends "home.html"%}
{%block head_title%} {{block.super}}{%endblock head_title%}
{% block content%}
<!-- verify authentication -->
{% if request.user.is_authenticated%}
<h3>{{object.title}}</h3><br/>
{% endif %}
<ul type="disc">
<div class="container">
<li><b>Artist: </b>{{object.Summary}}</li>
<li><b>Genre: </b>{{object.Genre}}</li>
<li><b>Bio: </b><br>{{object.Bio}}</li>
EDIT
</div>
</ul>
{%endif%}
{% endblock %}
This is my model
from django.db import models
from django.conf import settings
from Blog.models import MainPage
from django.urls.base import reverse
from Blog.Retrieve import retriever
from django.db.models.signals import pre_save,post_save
import InfoPedia
class InfoPedia(models.Model):
user =models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
Name =models.ForeignKey(MainPage,on_delete=models.CASCADE)
Location =models.CharField(max_length= 50,null=True,blank=True)
Information =models.TextField(null=True,blank=True)
TrackListing=models.TextField(null=True,blank=True)
Published=models.BooleanField(default=True)
Timestamp=models.DateTimeField(auto_now=True)
Updated=models.DateTimeField(auto_now=True)
def get_absolute_url(self):
# return f"/Blog/{self.slug}"
return reverse('InfoPedia:DetailView', kwargs={"pk":self.pk})
class Meta:
ordering=["-Updated","-Timestamp"] #orranges in order of updated
def get_tracklist(self):
return self.TrackListing.split(",")
def Information_create_pre_save( instance, sender, **kwargs):
instance.Information=retriever(instance.Name)
def rl_post_save_reciever(sender, instance,created,*args,**kwargs):
print("saved")
print(instance.Timestamp)
pre_save.connect(Information_create_pre_save, sender=InfoPedia)
post_save.connect(rl_post_save_reciever, sender=InfoPedia)
An alternative - rather than checking the if/else in the HTML, just make all the links to the DetailView URL.
Then, in the get() handler for the DetailView, you perform a queryset lookup for the object. If no object is found, then instead of displaying the DetailView HTML, return to the user a 302 redirect (i.e. a temporary redirect) to the CreateView for that object. So all your if/else logic is in the view function or class, instead of HTML.

getting an error didn't return an HttpResponse object. What is going on?

For the first time i am trying to create a small django application. It is just a basic page with two forms, a search form, which returns values from a database (working well), and a insert form (the problem). In the insert form there are three text fields asking for values for a new row in the database. The app is called "searcher". Here is my code:
views.py
from django.http import HttpResponse
from django.template import Context
from django.template.loader import get_template
from searcher.models import Publisher
from django.db.models import Q
def search(request):
if "search" in request.GET:
value = request.GET['search']
results = Publisher.objects.filter(Q(city__contains=value) | Q(name__contains=value))
else:
value = "No term"
results = []
template = get_template("base.html")
html = template.render(Context({"value":value, "results":results}))
return HttpResponse(html)
def insert(request):
if "name" in request.POST:
for key in request.POST:
counter = 0
if key != '':
counter = counter + 1
if counter == len(request.POST):
row = Publisher(name=request.POST['name'], city=request.POST['city'], website=request.POST['website'])
row.save()
base.html
<html>
<body>
<form method = "GET" action = "">
<input type = "text" name = "search"><input type = "submit">
</form><br>
you searched for:{{value}}<br>
your results:
{% for result in results %}
<br>{{result}}<br>
{% endfor %}
<br><br>
<form method = "POST" action = "/test/insert/">
<input type = "text" name = "name" value = "name"><br>
<input type = "text" name = "city" value = "city"><br>
<input type = "text" name = "website" value = "website"><br>
<input type = "submit">
</form>
</body>
</html>
urls.py
from django.conf.urls import patterns, include, url
from searcher import views
urlpatterns = patterns('',
url(r'^test/$', views.search),
url(r'^test/insert/$', views.insert),
)
the error is saying "ValueError at /test/insert The view searcher.views.insert didn't return an HttpResponse object." as a result of me submitting the insert form.
I understand what the error is saying: sorry but all views in views.py MUST return something to show.
Firstly why is this?
And secondly what is the correct way to accomplish the insert form ? Currently i have set the form action = "/test/insert/" so that in urls.py it can recognise the insert form was submitted and call the function insert, how should the function be called instead?
You seem to be asking two questions
"Why must all views return something to show?"
You're misinterpreting the error message. The error you saw didn't
say "all views MUST return something to show" - it only said
"searcher.views.insert didn't return an expected HttpResponse".
Since you're dealing with a HTTP request (a form POST), you're
expected to send back a HTTP Response. All HTTP requests expect a
response - this is a matter of a standard agreement on how to
communicate - the details are part of the http standard
definition, and somewhat explained as part of this answer.
"What is the correct way to accomplish the insert form?"
You're implementing it correctly - just send back a HTTP Response
for the POST. The code for it is almost identical to what you're
sending back in response to the Search request (maybe you'll want to
add a message in saying that the insert completed successfully,
where in the Search request you might have returned the item that
was being searched for?).
If you'd like to avoid having to refresh the form altogether, you might want to use an ajax call, instead of a http call, again, as described here.
All views must return an HttpResponse object because that's what the server returns to the browser that initially makes the request. Pretty much by definition, a view is a callable that accepts a request object and returns a response object.
It's fine to have code in views.py that doesn't return a response, such as helper functions for a view, but it's not fine to use such code as a view.
The usual pattern when working with POST requests is to return a redirect to a success page, on success, and to redisplay the form with the appropriate error messages on failure. See for instance the "Using a form in a view" docs.

How to add button next to Add User button in Django Admin Site

I am working on Django Project where I need to extract the list of user to excel from the Django Admin's Users Screen. I added actions variable to my Sample Class for getting the CheckBox before each user's id.
class SampleClass(admin.ModelAdmin):
actions =[make_published]
Action make_published is already defined. Now I want to append another button next to Add user button as shown in fig. . But I dont know how can I achieve this this with out using new template. I want to use that button for printing selected user data to excel. Thanks, please guide me.
Create a template in you template folder: admin/YOUR_APP/YOUR_MODEL/change_list.html
Put this into that template
{% extends "admin/change_list.html" %}
{% block object-tools-items %}
{{ block.super }}
<li>
Export
</li>
{% endblock %}
Create a view function in YOUR_APP/admin.py and secure it with annotation
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
def export(self, request):
... do your stuff ...
return HttpResponseRedirect(request.META["HTTP_REFERER"])
Add new url into YOUR_APP/admin.py to url config for admin model
from django.conf.urls import patterns, include, url
class YOUR_MODELAdmin(admin.ModelAdmin):
... list def stuff ...
def get_urls(self):
urls = super(MenuOrderAdmin, self).get_urls()
my_urls = patterns("",
url(r"^export/$", export)
)
return my_urls + urls
Enjoy ;)
The easy and accepted way is to override the template.
If you don't want to mess with the Django templates, you could add a Media class to your admin and add some javascript to create the button although I think creating elements with javascript is a bit nasty and should be avoided.
Though other answers are entirely valid, I think it is important to note that it is absolutely not necessary to add a button to get such behavior. You can use admin actions, as you did for the make_published action.
This as the advantage of not requiring to override any template, and thus prevent from potential troubles when upgrading django version (as admin templates may change, and changes might not be "compatible" with the way you overrode it).
import csv
from django.http import HttpResponse
from django.utils import timezone
def export_as_csv(modeladmin, request, queryset):
opts = modeladmin.model._meta
filename = format(timezone.now(), "{app}_{model}-%Y%m%d_%H%M.csv").format(
app=opts.app_label, model=opts.model_name)
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
writer = csv.writer(response)
field_names = [f.get_attname() for f in opts.concrete_fields]
writer.writerow(field_names)
for obj in queryset.only(*field_names):
writer.writerow([str(getattr(obj, f)) for f in field_names])
return response
Admin actions are made for this, adding a custom button is one step closer to "over-customization", which means it's probably time to write your own views.
The admin has many hooks for customization, but beware of trying to use those hooks exclusively. If you need to provide a more process-centric interface that abstracts away the implementation details of database tables and fields, then it’s probably time to write your own views.
Quote from the introduction paragraph of Django Admin's documentation

Categories

Resources