How to dynamically generate URL with flask and my database? - python

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 %}

Related

How to minimise number of routes in my Flask webapp

I am attempting to minimize the number of routes and database tables in my simple clothing ecommerce site. My site has four product categories: Coat, Shirt, Trouser, Shoe.
Each category currently has its own table in the database:
class Coat(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text, nullable=False)
description = db.Column(db.Text, nullable=True)
price = db.Column(db.Float)
image1 = db.Column(db.String(100), nullable=True, default='default.jpg')
image2 = db.Column(db.String(100), nullable=True)
I would like to combine all products into a single database with 'Category' being a column inside this Product database.
Each category also currently has its own route and template:
#app.route('/coats')
def coats():
products = Coat.query.all()
return render_template('coats.html', products=products, title='Coats')
{% extends "base.html" %}
{% block content %}
{% for product in products %}
<div class="product">
<a href="{{ url_for('product_coat', id=product.id) }}">
<img src="static/images/coats/{{ product.image1 }}" class="image"></a>
<p class="name">{{ product.name }}</p>
<p class="price">{{ product.price }}</p>
</div>
{% endfor %}
{% endblock %}
When a products link is followed, a product page is displayed, again using individual routes for each category:
#app.route('/product_coat/<id>', methods=['GET', 'POST'])
def product_coat(id):
product = Coat.query.get(id)
return render_template('product_coat.html', title='Product',
product=product)
Currently my navbar in "base.html" looks like this:
<nav>
<h3>Coats</h3>
<h3>Shirts</h3>
<h3>Trousers</h3>
<h3>Shoes</h3>
</nav>
I realise this is a rather large post so to summarise,
If I were to combine all four database tables into one product table with a new 'category' column, how would I:
Create navbar links for these categories?
Create a route that handles displaying only products from the selected category?
Thanks for reading.
You can pass query parameters to a new route,
#app.route('/product/', methods=['GET'])
def product_list(id):
item = request.args.get('item')
product = products.all.filter(type=item)
return render_template(f"{item}.html", title=item,product=product)
then when you load the page, you can call url/?item=coat and it will filter the data.
*that product filter line might be incorrect.
What you can do is attempt to follow a more RESTful design pattern.
The main idea is to add a parameter to your routes to represent the category. This would be similar to what you've done for the product ID. Importantly, you would have only one HTML template for the products page (the page that lists all the products of a particular category) and one HTML template for the product page (the page for listing the details of one product of a particular category).
So your two existing routes could become something like this:
(Note your logic should ensure an invalid product category and/or ID is correctly handled)
#app.route('/products/<product_category>/<product_id>', methods=['GET', 'POST'])
def product(product_category, product_id):
product = <your database logic utilizing the product_category and product_id + error handling>
return render_template('product.html', product=product, title=product.name)
#app.route('/products/<product_category>', methods=['GET'])
def products(product_category):
products = <your database logic utilizing the product_category + error handling>
return render_template('products.html', products=products, title=product_category.title())
Your navbar HTML could end up looking like this:
(Here we specify the 2nd route and supply just the category)
<nav>
<h3>Coats</h3>
<h3>Shirts</h3>
<h3>Trousers</h3>
<h3>Shoes</h3>
</nav>
Finally, your product display HTML could end up like this:
(Here we specify the 1st route and supply the category and product ID -- since we're linking to a specific product in this case)
<div class="product">
<a href="{{ url_for('product', product_category=product.category, product_id=product.id) }}">
<img src="{{ url_for('static', filename='images/coats/' + product.image1) }} class="image"></a>
<p class="name">{{ product.name }}</p>
<p class="price">{{ product.price }}</p>
</div>
Also, I suggest investigating the MVC design pattern which is highly prevalent in Flask applications.

Flask SQLAlchemy Query

I have created a Flask project using Miguel Grinbergs' excellent tutorials. I am adapting the example code to make my own site.
I have two tables in my db. Drivers and Teams. I have created a One-to-Many relationship in models.py and I want to output a list of drivers and the team they belong to. I have included routes.py and the html file that will display the results.
The query in routes.py works fine if I specify one driver within the query, but when I try Drivers.query.all(), it does not return any data on the web page.
NOTE = I have ran the Drivers.query.all() within a Python Shell and I get the correct response, it is only when I try to implement the solution on the web page that I experience the problem.
output from Python Shell
>>> details = Driver.query.all()
>>> details
[<Driver Lewis Hamilton>, <Driver Kimi Raikkonen>, <Driver Max Verstappen>]
>>> for d in details:
... print(d.driverName, d.driverTeam)
...
Lewis Hamilton Mercedes
Kimi Raikkonen Ferrari
Max Verstappen Red Bull
>>>
models.py
class Team(db.Model):
id = db.Column(db.Integer, primary_key=True)
teamName = db.Column(db.String(64))
teamDriver = db.relationship('Driver', backref='driverTeam', lazy='dynamic')
def __repr__(self):
return '{}'.format(self.teamName)
class Driver(db.Model):
id = db.Column(db.Integer, primary_key=True)
driverName = db.Column(db.String(64))
team_id = db.Column(db.Integer, db.ForeignKey('team.id'))
def __repr__(self):
return '<Driver {}>'.format(self.driverName)
routes.py
#app.route('/driverDetails', methods=['GET', 'POST'])
def driverDetails():
## Output works fine with one driver specified in query
# details = Driver.query.filter_by(driverName='Kimi Raikkonen').first()
## No data is output when using query below
details = Driver.query.all()
return render_template('driverDetails.html', title='Driver Details', details=details)
driverDetails.html
{% extends "base.html" %}
{% block content %}
<h1>Driver Details</h1>
<!-- Output works correctly for one driver as specified in query on routes.py
<div><p>{{ details.driverName }} is in: <b>{{ details.driverTeam }}</b></p></div>
-->
<!-- However, when trying to iterate over whole list of drivers, output shows
the text "drives for:". But there is no data being populated from the db
-->
{% for d in details %}
<div><p>{{ details.driverName }} drives for: {{ details.driverTeam }}</p></div>
{% endfor %}
{% endblock %}
I would appreciate any advice on where I am going wrong.
your code is basically correct, you just need to actually use the object you are binding to d:
{% for d in details %}
<div><p>{{ d.driverName }} drives for: {{ d.driverTeam }}</p></div>
{% endfor %}

django template access to models

I am having an issue with templating system in Django, I am doing a Movie list app just to learn more about Django and I have already declared my models like following:
class Pelicula (models.Model):
nombre = models.CharField(max_length = 30)
genero = models.CharField(max_length = 20)
año = models.IntegerField()
def __str__(self):
return self.nombre
Then I have declared my view like following
def index(request):
pelicula = Pelicula.objects.all()
return render_to_response("index.html", dict(pelicula = pelicula, usuario = request.user))
So as far as i know, I am not having any trouble here
Then the problem starts on my template
<!DOCTYPE html>
<html>
<head>
<title>Mis peliculas favoritas</title>
</head>
<body>
<h1>Mis peliculas favoritas</h1>
<ol>
{% for pelicula in pelicula.object_list %}
<li>{{ pelicula.nombre }}</li>
{% endfor %}
</ol>
agregar pelicula nueva
</body>
</html>
So as you see, I am using a for loop to get all the objects in my db, I have 3, but when i run the server, the title shows up and the other link bellow named "agregar" shows up except for the movies, I am wondering if the problem is something between the "pelicula" name instead of using Pelicula as i used on declaring my model, anyway I have also changed that to Pelicula.object_list and it didn't worked anyway
I believe I am having a trouble understanding how to use the information i have through Django template tags
Thanks in advance and please forgive my grammar as I am not a native english speaker
In the view code you have pelicula = Pelicula.objects.all() which creates a list of all the abjects from the database. You then send this to the template with the same name, which means all you have to do in the template is iterate over them. Try changing the template to this:
<h1>Mis peliculas favoritas</h1>
{% if pelicula %}
<ol>
{% for item in pelicula %}
<li>{{ item.nombre }}</li>
{% endfor %}
</ol>
{% else %}
didn't find any...
{% endif %}
Part of the confusion seems to come from the fact that the template code and the view code is separate. The template doesn't know anything about the view code. The context dictionary just sets the names that can be used in the template and in the template the only names that you have available to use are the ones that you passed to it via the dictionary. The names in the view and template here are the same in this case because you set them to be the same. They aren't the same variables though, they just have the same name. In the template pelicula is already a list of objects so you don't need to do anything extra to use it in the template loop.

How do I implement markdown in Django 1.6 app?

I have a text field in models.py where I can input text content for a blog using the admin.
I want to be able to write the content for this text field in markdown format, but I'm using Django 1.6 and django.contrib.markup is not supported anymore.
I can't find anywhere that has a tutorial and runs through adding markdown to a text field in Django 1.6. Can someone look at my .py files and help me implement markdown to my app.
models.py
from django.db import models
# Create your models here.
class Post(models.Model):
title = models.CharField(max_length=200)
pub_date = models.DateTimeField()
text = models.TextField()
tags = models.CharField(max_length=80, blank=True)
published = models.BooleanField(default=True)
admin.py
from django.contrib import admin
from blogengine.models import Post
class PostAdmin(admin.ModelAdmin):
# fields display on change list
list_display = ['title', 'text']
# fields to filter the change list with
save_on_top = True
# fields to search in change list
search_fields = ['title', 'text']
# enable the date drill down on change list
date_hierarchy = 'pub_date'
admin.site.register(Post, PostAdmin)
index.html
<html>
<head>
<title>My Django Blog</title>
</head>
<body>
{% for post in post %}
<h1>{{ post.title }}</h1>
<h3>{{ post.pub_date }}</h3>
{{ post.text }}
{{ post.tags }}
{% endfor %}
</body>
</html>
Thank you for your answers and suggestions, but I've decided to use markdown-deux.
Here's how I did it:
pip install django-markdown-deux
Then I did pip freeze > requirements.txt to make sure that my requirements file was updated.
Then I added 'markdown_deux' to the list of INSTALLED_APPS:
INSTALLED_APPS = (
...
'markdown_deux',
...
)
Then I changed my template index.html to:
{% load markdown_deux_tags %}
<html>
<head>
<title>My Django Blog</title>
</head>
<body>
{% for post in post %}
<h1>{{ post.title }}</h1>
<h3>{{ post.pub_date }}</h3>
{{ post.text|markdown }}
{{ post.tags }}
{% endfor %}
</body>
</html>
Ah, I met with the same problem several months ago, and I found the easiest and most robust solution is to use Github Markdown API.
Here is the code I use for my blog, which I believe will help you more or less. btw I use Python 3 so the encoding part may be different from Python 2.
# generate rendered html file with same name as md
headers = {'Content-Type': 'text/plain'}
if type(self.body) == bytes: # sometimes body is str sometimes bytes...
data = self.body
elif type(self.body) == str:
data = self.body.encode('utf-8')
else:
print("somthing is wrong")
r = requests.post('https://api.github.com/markdown/raw', headers=headers, data=data)
# avoid recursive invoke
self.html_file.save(self.title+'.html', ContentFile(r.text.encode('utf-8')), save=False)
self.html_file.close()
My code is hosted on github, you can find it here
And My blog is http://laike9m.com.
You may use substitution of old markup implemented here - https://github.com/jamesturk/django-markupfield

Google App Engine: Retrieving data from database

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

Categories