Google App Engine: Retrieving data from database - python

I am trying to build a simple blog application to use the skills that I learned from Udacity so far. However, I am having trouble retrieving data from the database and displaying it for the user. Now, my blog has a permalink which displays the post that was just being submitted by the user, and also the main blog page which will display the latest 10 posts in descending order. But when I submit a post, the post is stored in the database successfully, and I am being redirected to a permalink. However, all I get is a blank page instead of the post that I just submitted.
Also, when I go back to my main blog page, I see this instead of all the posts being submitted by the user:
Here's the main python code:
import os
import re
import webapp2
import jinja2
from string import letters
from google.appengine.ext import db
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir), autoescape=True)
def render_str(template, **params):
t = jinja_env.get_template(template)
return t.render(params)
class Handler(webapp2.RequestHandler):
def write(self, *a, **kw):
self.response.out.write(*a, **kw)
def render_str(self, template, **params):
return render_str(template, **params)
def render(self, template, **kw):
self.write(self.render_str(template, **kw))
def render_post(response, post):
response.out.write('<b>' + post.subject + '</b><br>')
response.out.write(post.content)
def post_key(name = "dad"):
return db.Key.from_path('blog', name)
class Blogger(db.Model):
name = db.StringProperty()
content = db.TextProperty()
created = db.DateTimeProperty(auto_now_add = True)
def render(self):
self._render_text = self.content.replace('\n', '<br>')
return render_str("post.html", p = self)
class MainPage(Handler):
def get(self):
self.response.write("Visit our blog")
class BlogHandler(Handler):
def get(self):
posts = db.GqlQuery("SELECT * FROM Blogger order by created desc")
self.render("frontblog.html", posts = posts)
class SubmitHandler(Handler):
def get(self):
self.render("temp.html")
def post(self):
name = self.request.get("name")
content = self.request.get("content")
if name and content:
a = Blogger(parent = post_key(), name = name, content = content)
a.put()
self.redirect('/blog/%s' % str(a.key().id()))
else:
error = "Fill in both the columns!"
self.render("temp.html", name = name, content = content, error = error)
class DisplayPost(Handler):
def get(self, post_id):
post_id = self.request.get("post_id")
if post_id:
po = Blogger.get_by_id(int(post_id), parent = post_key())
if po:
self.render("perma.html", po = po)
self.response.write("No")
app = webapp2.WSGIApplication([('/', MainPage),
('/blog', BlogHandler),
('/blog/submit', SubmitHandler),
(r'/blog/<post_id:([0-9]+)>', DisplayPost)], debug=True)
Here's the HTML code for the base of all the HTML pages:
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="/static/main.css" />
<title>CS 253 Blog</title>
</head>
<body>
<a href="/blog" class="main-title">
CS 253 Blog
</a>
<div id="content">
{% block content %}
{% endblock %}
</div>
</body>
</html>
Here's the HTML code for the permalink page:
{% extends "base.html" %}
{% block content %}
{{po.render() | safe}}
{% endblock %}
HTML code for the front of the blog:
{% extends "base.html" %}
{% block content %}
{% for p in posts %}
{{ p.render() | safe }}
<br><br>
{% endfor %}
{% endblock %}
I have been struggling with this for over two days now. I see no bugs in the logs either. What seems to be the problem?
EDIT:
Edited the source code based on the answers below. However, I still get a 404 error.

You are creating your Blogger entity with a parent key that you are not specifying when you are trying to retrieve your post in your DisplayPost.
An entity's key is composed of multiple parts. It's Kind ("Blogger"), it's ID (int(post_id)), and also it's ancestor, or parent.
Because you create your entity with:
a = Blogger(parent = post_key(), name = name, content = content)
You need to specify the same parent when calling get_by_id() (see the get_by_id() docs).
Your solution will be to change
po = Blogger.get_by_id(int(post_id))
to
po = Blogger.get_by_id(int(post_id), parent=post_key())
Later, if you change the name of the blog (looking at the code for post_key()), then you'll need to carry that as an argument to the post display mechanism. (You might use a subdomain as the 'name' of the blog for example).
Here is the previous answer that addressed issues with the regex URL mapping:
In your WSGIApplication definition, you have ('/blog/([0-9]+)', DisplayPost)], debug=True).
You are missing the r before the url string to identify the string as a regular expression:
(r'/blog/([0-9]+), ....
You also have the option to be more verbose with parameter naming:
(r'/blog/<post_id:([0-9]+)>, ....
Reference: http://webapp-improved.appspot.com/guide/routing.html

Related

How to dynamically generate URL with flask and my database?

I try to dynamically generate my url_for like this and it is not working...:
search.html
<a href="{{ url_for('{{ country }}') }}"
This is where I query my data for my link from my database.
routes.py
from app import app, db
from app.models import
#app.route('/search')
def search():
country = Country.query.get(3)
return render_template('search.html', country=country.country)
#this is one of the final page where the link in the search results lead
#I will have /portugal, /france, etc...
#app.route('/germany')
def germany():
return render_template('germany.html')
and this is a clip of the information in my database:
models.py
from app import db
class Country(db.Model):
id = db.Column(db.Integer, primary_key=True)
country = db.Column(db.String(120), index=True, unique=True)
html_page = db.Column(db.String(255), index=True, unique=True)
def __repr__(self):
return '<Country {}>'.format(self.country)
Do I even need to store the URL since it is the same as the country name aka #germany
If you already have a base template file setup for your countries then typically you want to generate that template with the specific information.
Then typically you want to have your function definition setup something like this
# renders the given country page from the base template
# EX: if given "Germany" the resulting url will be:
# mywebsite.com/country/Germany
#app.route("country/<str:country>", methods=["GET"])
def render_country(country):
# your specific code here
return render_template("countrybase.html", country=country)
While your html link would look something like this:
Link to Germany page
If you don't already have a base html template setup I would recommend researching Jinja templates and how Flask uses them. The example project from the official documentation has a great guide on how to get started.
But as a quick example your countrybase.html might look like this:
{% extends base.html %}
{% block header %}
<h1>{% block title %}{{ country["name"] }}{% endblock %}</h1>
{% endblock %}
{% block content %}
<div class="my-custom-css-class">
{{ country["other_country_data"] }}
<!-- etc.. -->
</div>
{% endblock %}

Unit test on custom django admin template

No clue how to deal with this situation.
Recently start unit test with django.
In my project, i have a custom change_list.html to add a button on the admin page.
I'm looking for a unit test which can verify this custom button.
Here is my code in admin.py :
class LocationAdmin(OSMGeoAdmin):
def get_urls(self):
urls = patterns('', url(
r'^import/$',
self.admin_site.admin_view(self.import_process)
))
super_urls = super(LocationAdmin, self).get_urls()
return urls + super_urls
def import_process(self, request):
pass
admin.site.register(Location, LocationAdmin)
This code automaticaly load the template in app/admin/app/app/change_list.html
Which is :
{% extends "admin/change_list.html" %}
{% load staticfiles %}
{% block extrahead %}
<link rel="stylesheet" href="{% static "css/custom_upload.css" %}">
{% endblock %}
{% block object-tools-items %}
{{ block.super }}
<li><a role="button" href="import/" class="btn btn-primary btn-margin-left">Import</a></li>
{% endblock %}
But how about when you want to proove that work's with a unit test?
I'm able to test template when i can use a view and the function render_to_string
Like this :
response = my_view(HttpRequest())
expected_html = render_to_string(
'template.html',
{'arg_key': arg}
)
self.assertEqual(response.content.decode(), expected_html)
But here, i don't figure it out how to call the admin view with the associate template part.
Here a start of what i found to add unit test on admin page.
class MockRequest(object):
pass
class MockSuperUser(object):
def has_perm(self, perm):
return True
request = MockRequest()
request.user = MockSuperUser()
class ModelAdminTests(TestCase):
def setUp(self):
self.site = AdminSite()
def test_admin_import_button_on_location_admin_page(self):
ma = ModelAdmin(Location, self.site)
#Here the correct assert ?
Thank you all.
This might not be a good idea (probably a better alternative: using the test client). But for reference, here is how you can render the changelist_view programmatically directly from the admin method:
from django.contrib.admin import AdminSite, ModelAdmin
from django.test.client import RequestFactory
from django.contrib.admin.templatetags.admin_urls import admin_urlname
from django.shortcuts import resolve_url
class MockUser:
is_active = True
is_staff = True
def has_perm(self, *args):
return True
url = resolve_url(admin_urlname(MyModel._meta, "changelist"))
request = RequestFactory().get(url)
request.user = MockUser()
class MyAdmin(ModelAdmin):
change_list_template = "path/to/custom/template/if/needed"
admin = MyAdmin(Work, AdminSite())
html = admin.changelist_view(request).rendered_content

Creating hyperlinks in Google App Engine using Jinja2 template

I'm trying to scrape links from a RSS page from Quora, which I have been successful in doing so. However, I want those links to appear as hyperlinks in my application, instead of simply appearing as plain text.
Here's the application so far:
http://deploymentapp.appspot.com/
Here's the main python code:
import os
import webapp2
import jinja2
from google.appengine.ext import db
import urllib2
import re
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir), autoescape=True)
class Handler(webapp2.RequestHandler):
def write(self, *a, **kw):
self.response.out.write(*a, **kw)
def render_str(self, template, **params):
t = jinja_env.get_template(template)
return t.render(params)
def render(self, template, **kw):
self.write(self.render_str(template, **kw))
class MainPage(Handler):
def get(self):
content = urllib2.urlopen('http://www.quora.com/Python-programming-language-1/rss').read()
allTitles = re.compile('<title>(.*?)</title>')
allLinks = re.compile('<link>(.*?)</link>')
list = re.findall(allTitles,content)
linklist = re.findall(allLinks,content)
self.render('frontrss.html', list = list, linklist = linklist)
app = webapp2.WSGIApplication([('/', MainPage)], debug=True)
here's the HTML source code:
<h1>Quora Live Feed</h1><br><br><br>
{% extends "rssbase.html" %}
{% block content %}
{% for e in range(1, 19) %}
{{ (list[e]) }} <br>
{{ (linklist[e]) }}
<br><br>
{% endfor %}
{% endblock %}
So basically, I don't know how to make the links appear as hyperlinks when scraped from an outside source code in Jinja2 template.
This is just basic HTML: you put the link inside the href attribute of an a tag:
{{ list[e] }}

Django Template Page Outputting Nothing

Can someone help me figure out why my Django template pages won't render anything?
I'm using Python Requests (http://docs.python-requests.org/en/latest/) to retrieve JSON data from the external urls. I decode the data by using .json(). This works as I would expect when executing this from command line, but when it does nothing in the view.
When I run the server, the page is blank. It has no title, no 'testing' printed, nothing.
My Template:
<html>
<head><title>Offer List</title></head>
<body>
<p>Testing</p>
{% load dictparser %}
{% for offers in network1_offers %}
{% autoescape off %}
<div>
<p>name: {{ offers|lookup:"name" }}</p>
<p>pay: {{ offers|lookup:"payout" }}</p>
<p>description: {{ offers|lookup:"description" }}</p>
</div>
{% empty %}
<li>Sorry, no surveys available.</li>
{% endautoescape %}
{% endfor %}
</body>
</html>
My View:
class OffersList(View):
template_name="generic_app/offers.html"
def load_offers(request):
"""
import example network offers.
"""
user = request.user
user_agent = request.META['HTTP_USER_AGENT']
amparams = {'user_subid':user.sub_id, 'useragent':user_agent, 'user_ip':user.ip_address}
examplenetwork = requests.get('http://example.com/api-get.php?pubid=00000&key=000000000000&mode=offers&incent=1', data=amparams)
exampleoffers= examplenetwork.json()
"""
import example network 2 offers.
"""
clparams = {'subid':user.sub_id, 'ua':user_agent, 'geoip':user.ip_address}
examplenetwork2 = requests.get('http://www.examplenetwork2.com/blahblah', data=clparams)
exampleoffers2 = examplenetwork2.json()
render(request, 'generic_app/offers.html', {'network1_offers':exampleoffers, 'network2_offers':exampleoffers2})
The url:
url(r'^dashboard/offers$', OffersList.as_view(), name="offers"),
You're seeing this because you haven't defined how to get to the load_offers() method in your view, currently your load_offers() method is just a method floating around in your class.
Using the base class View comes with it's methods that you need to implement, for example
class OfferView(View):
template_name = "generic_app/offers.html"
def get(self, request, *args, **kwargs):
return load_offers(request)
or much better change this to a TemplateView(because that's what it really is).
class OfferView(TemplateView):
template_name = "generic_app/offers.html"
def get_context_data(self, **kwargs):
context = super(OfferView, self).get_context_data(**kwargs)
context['offers'] = load_offers(self.request)
return context

ImageField in Django

I am attempting to build a basic database of song information, regarding artists, songs and albums. On my page for any given album I am attempting to display the album art for a given album. I am using the built-in image upload feature within Django Admin to upload the images. The images urls are being saved correctly within mysql (I checked), but the images are not being displayed on the site. From what I have read online the possible problematic areas are either in the URLs file(which I have not modified to accommodate media) or in my MEDIA_URL, but I have followed instructions as Django has outlined. Below I am linking the code I think is relevant. Thank you in advance for the help!
views.py
from django.shortcuts import render_to_response
from django.template import RequestContext
from Radio.models import Song, Artist, Album
def SongsAll(request):
songs = Song.objects.all().order_by('songName')
context = {'songs' : songs}
return render_to_response('songsall.html', context, context_instance = RequestContext(request))
def SpecificSong(request, songname):
song = Song.objects.get(songName = songname)
context = {'song':song}
return render_to_response('specificsong.html',context, context_instance=RequestContext(request))
def SpecificArtist(request, artistname):
singer = Artist.objects.get(artistName = artistname)
songs = Song.objects.filter(artist = singer)
context = {'songs':songs}
return render_to_response('specificartist.html',context, context_instance=RequestContext(request))
def SpecificAlbum(request, albumname):
album = Album.objects.get(albumName = albumname)
songs = Song.objects.filter(album = album)
context = {'songs':songs}
return render_to_response('specificalbum.html', context, context_instance=RequestContext(request))
settings.py (only the relevant parts)
MEDIA_ROOT = '/home/kyle/Downloads/Django-1.5.1/radioSite/media/'
MEDIA_URL = 'http://127.0.0.1:8000/media/'
models.py
from django.db import models
class Artist(models.Model):
artistName = models.CharField(max_length = 30)
artistInfo = models.TextField()
def __unicode__(self):
return self.artistName
class Album(models.Model):
albumName = models.CharField(max_length = 30)
artist = models.ForeignKey('Artist')
date = models.DateTimeField('Release Date')
albumInfo = models.TextField()
albumArt = models.ImageField(upload_to="images/albumart/")
def __unicode__(self):
return self.albumName
class Song(models.Model):
songName = models.CharField(max_length = 30)
artist = models.ForeignKey('Artist')
album = models.ForeignKey('Album')
def __unicode__(self):
return self.songName
base.html
<html>
<head>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/Radio.css"/>
{% block extended %}{% endblock %}
</head>
<body>
<div id="pageContainer">
{% block content %}
{% endblock %}
</div>
</body>
</html>
specificalbum.html
{% extends "base.html" %}
{% block content %}
<div id="singlealbum">
<p><img src="{{ songs.0.album.albumart.url }}"/>Name: {{ songs.0.album }}</p>
<p>Artist:{{ songs.0.artist }}</a></p>
<p>Song list:</p>
{% for song in songs %}
<p>{{ song }}</p>
{% endfor %}
</div>
{% endblock %}
Are your images actually getting uploaded somewhere where a web server is expecting to serve them?
From your MEDIA_URL setting I'm guessing that you're trying to get the Django server to serve the images. You can check the documentation for how to do this during development, or if you're just learning Django, but please don't try to do this for anything at all serious - you'll be much better served by using a dedicated web server like Apache or the like.

Categories