I'm very new to Flask and WTForms, but what I'm trying to do is provide a user with a button to allow them to automatically upload a GIF to giphy.com. The GIF is located on the server and rendered to their browser on generate.html. Below the GIF is a Submit button that I intend to have launch an app.route to handle the upload process (opening a new tab which will eventually display a link to the uploaded GIF once that process completes).
Problem: The app.route for the uploading the gif (clicking the submit button) has no data assigned to its HiddenField (gifLocation), despite thinking I assigned it when generate.html was loaded.
My FileLocationForm code:
# class for sending along a file path used for uploading a gif
class FileLocationForm(FlaskForm):
gifLocation = HiddenField()
submit = SubmitField('Upload to GIPHY')
When the GIF is first created on my server, I run the following commands (in the app.route for generate.html):
form = FileLocationForm(gifLocation=gif_outfileloc)
return render_template('generate.html', gif_outfileloc=gif_outfileloc[7:], form=form)
At this point, gif_outfileloc exists and 100% points to a valid GIF on my server. The 2nd parameter's formating is because I'm displaying a file in the "static" folder, and need to remove "static" from the path, since it's added by flask in the img tag. That part is working.
The code for generate.html:
{% extends 'layout.html' %}
{% block content %}
<div class="gif_display center-align">
<img class="img-fluid mx-auto my-auto d-block" width="480" height="360" src={{ url_for("static", filename = gif_outfileloc) }}>
</div>
<form id='uploadGifForm' action = {{ url_for('generateGIFpage') }} target = '_blank' method='POST'>
{{ form.gifLocation() }}
{{ form.submit() }}
</form>
{% endblock content %}
We get a submit button on the page and clicking it does open a new tab, but it takes us home, instead of uploading to giphy.
Code for generateGIFpage:
#app.route("/upload", methods=["GET", "POST"])
def generateGIFpage():
if request.method == 'POST':
if request.form.get('gifLocation'):
gifLocation = request.form.get('gifLocation')
giphyobj = Giphy(API_KEY)
# response (below) is the URL for our giphy upload
response = giphyobj.upload([], gifLocation, username="QuoteGIFr")
return render_template('upload.html', response = response)
return redirect(url_for('homepage'))
Any help is appreciated. I've tried some variations on this, but I haven't had any success yet. If you need anymore code to make sense of what I'm doing, or need more information on what I'm trying to do, please ask.
Related
Does anyone know how to check if a template is being accessed through a url route with the django template language?
Here is my situation: There's this template article.html, that shows all posts of a blog when accessed through blog-home url route, and that also shows only posts of a given user through user-posts url route.
This template works like that because what defines which posts will be shown are the classes in views.py.
That being said, here is what I tried to do: (I know its wrong)
{% if url == 'user-posts' %}
"Some HTML h1 tag I want do be shown only when this template is accessed via 'user-posts' url"
{% endif %}
How would be the correct way, if there's any, to write this if statement?
When Django matches a url pattern a ResolverMatch object is created and made available on the request object at request.resolver_match. This ResolverMatch object can be queried in your template to acheive what you want, the url_name attribute is the name of the url pattern that was matched
{% if request.resolver_match.url_name == 'user-posts' %}
You should create a custom templatetag,
my_app/templatetags/utils.py
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def is_active_view(context, *view_names):
request = context.get('request')
for view_name in view_names:
if getattr(request.resolver_match, 'view_name', False) and request.resolver_match.view_name == view_name:
return True
return ''
And use it in your template this way, assuming blog-home is the url name you gave to your url :
blog-home.html
{% load utils %}
{% is_active_view 'blog-home' as display_blog_home_section %}
{% if display_blog_home_section %}
<!-- Your code -->
{% endif %}
NB : this template tag can check after multiple view names at once and support namespaced url :
{% is_active_view 'front:blog-home' 'front:blog-categories' as display %}
Here is a more optimized version of this if you are using boostrap and trying to make the menu options "active" when you are on that URL
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def is_active_tab(context, *view_names):
request = context.get('request')
for view_name in view_names:
if getattr(request.resolver_match, 'view_name', '') == view_name:
return 'active'
return ''
And you use it like
<li class="nav-item {% is_active_tab 'games:list' %}">
<a class="nav-link" href="{% url 'games:list' %}">
{% translate "My Quizzes" %}
</a>
</li>
<li class="nav-item {% is_active_tab 'games:create' %}">
<a class="nav-link" href="{% url 'games:create' %}">
{% translate "New Game" %}
</a>
</li>
It adds an extra active classname to the class of the element if any of the view_names match the current view name. Makes the template writing much cleaner
I'm trying to create an AddtoCart and Checkout functionality using python flask and flask-sqlalchemy. I consider myself to be a beginner at web development in general. How do I take a product item and add it to a cart as a cart item using a button? I would also like to calculate the total price of cart items.
So far I created two models (ProductItem,CartItem). I created successfuly 2 ProductItems (dummy data) and was able to display them in a view using a for loop with jinja2 template. I've tried to create a function to select the product and add it to the cart but I couldn't figure out the way on how to make the add to cart button functionality work.
Thanks in advance!!
class ProductItem(db.Model):
__tablename__='products'
id = db.Column(db.Integer,primary_key=True)
name = db.Column(db.String(64),unique=True)
descr = db.Column(db.Text,unique=True,nullable=True)
price = db.Column(db.Float,nullable=False)
img = db.Column(db.String(64),unique=True)
cartitems = db.relationship('CartItem', backref='Product')
def __repr__(self):
return '<ProductName %r>' % self.name
class CartItem(db.Model):
__tablename__='cartitems'
id = db.Column(db.Integer,primary_key=True)
# adding the foreign key
product_id = db.Column(db.Integer, db.ForeignKey('products.id'))
#app.route('/')
def index():
products = Product.query.all()
return render_template('home.html',products=products)
def getproductitem():
itemid = product.id
productname = product.name
productname = CartItem(product_id=itemid)
db.session.add(product)
db.session.commit()
----------------html jinja----------
{% for product in products %}
<div class="product-item">
<h3>{{ product.name }}</h3>
<img src="static/img/products/{{ product.img }}" alt="" width="200px" height="200px">
<p> {{ product.price }}</p>
<button onclick="getproductitem()" type="button" class="btn btn-primary">Add to Cart</button>
</div>
{% endfor %}
Edit
Realised I didn't answer the question about the button. Seems like you're trying to call you python function from the html (unless you have a javascript function also in your front end template).
Your python lives on the server and your html/javascript will be on the client browser - you need to make them communicate by sending a HTTP request from your page to the server, you can't call functions directly.
Server:
#app.route('/cart/<int:product_id>', methods=['POST'])
def add_to_cart(product_id):
product = Product.query.filter(Product.id == product_id)
cart_item = CartItem(product=product)
db.session.add(cart_item)
db.session.commit()
return render_tempate('home.html', product=products)
add to your html:
<script>
function addToCart(productId) {
fetch('[your.local.host.]/cart/productId',
{method: 'POST'}
)
}
</script>
change the button:
<button onclick="addToCart({{product.id}})" type="button" class="btn btn-primary">Add to Cart</button>
Or something similar. Your page needs to talk to your server via HTTP requests.
Original answer about carts
It's probably not necessary to persist your cart in the database unless you really want your users to be able to access the same cart when logging in across devices, or you anticipate they will need to keep items there more long term.
Persisting will add unnecessary time to user requests (while you add/retrieve them) and that CartItem table will continue to get larger and larger and most rows will become redundant (unlikely people want to view their old shopping cart once they've bought products). One solution would be to also link the carts to a User table so you only have one cart per user (provided your users are logged in when shopping), or make sure you delete carts once they're bought or after a certain time period.
However, if you have no need to persist longer term, consider storing the product ids either in either
The flask session. Essentially a lightweight, in memory store on the server that is linked to a user and can be accessed during the request handling. See a tutorial on sessions here.
Inside a cookie. A cookie is stored in the browser (not the server) and usually signed with a key. This does not make them secure - it just means you can be sure no one has modified its content when you retrieve it on the server. See a tutorial here.
This article discusses a few drawbacks/merits to both approaches.
I'm using Timber (the WordPress plugin implementation of Twig) and the Advanced Custom Fields plugin gallery field. I have a working gallery under a custom post type, so ACF and Timber are working elswhere in the site, but not for a standard page. When I try to add a gallery to a page, all I end up with is empty markup for the img src tag.
I have this in page.php in the template folder:
$context = Timber::get_context();
$page = new TimberPost();
$context['page'] = $page;
if ($photos = $page->get_field('photos')) {
$context['photos'] = $photos;
}
I have this in default.twig in the templates/page/ folder in the theme (html removed for simplicity):
{% if page.photos %}
{% for p in page.photos %}
<img src="{{ p.sizes.gallery|relative }}" alt="{{ p.alt }}" />
{{ p.caption }}
{% endfor %}
{% endif %}
This results in the page source <img src="" alt="">.
If I use {{ dump(photos) }} inside the for p in page.photos statement, it dumps the array of images I have entered in the Gallery field on the backend. So the image array exists and it being output. The relative extension runs for all post types; removing it makes no difference here.
So why is the p.sizes.gallery|relative function not outputting each image's url and caption?
You append the data to the $context['photos'] so I believe you should change your code to check for if photos and iterate as for p in photos
I am trying to make a simple Pyramid app, and having a hell of a time figuring out what part of the syntax I'm missing. What's happening is I have an edit page for my model, and I can not figure out how to pass in the id of the entry I'm editing.
My view reads like this:
#view_config(route_name='action', match_param='action=edit', renderer='string')
def update(request):
this_id = request.matchdict.get('id', -1)
entry = Entry.by_id(this_id)
if not entry:
return HTTPNotFound()
form = EntryUpdateForm(request.POST, entry)
if request.method == 'POST' and form.validate():
form.populate_obj(entry)
return HTTPFound(location=request.route_url('blog', id=entry.id, slug=entry.slug))
return {'form': form, 'action': request.matchdict.get('action')}
I have created an edit template, it looks like this, and is working for the create page, which uses a different model:
{% extends "templates/layout.jinja2" %}
{% block body %}
<h2>Create a Journal Entry</h2>
<form action="" method="POST">
{% for field in form %}
{% if field.errors %}
<ul>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<p>{{ field.label }}: {{ field }}</p>
{% endfor %}
<p><input type="submit" name="submit" value="Submit" /></p>
</form>
{% endblock %}
And the link I have made to the template looks like:
Edit Entry
Which yields the url http://0.0.0.0:6543/journal/edit?id=1. This is new and weird to me, because I'm used to Rails, where the url would look like http://0.0.0.0:6543/journal/1/edit but poking around and reading the Pyramid blogr tutorial, this seems like how Pyramid likes to have routes. Unfortunately, it still gives me a 404. It seems like I am successful passing the id of the entry into a query string, but somehow I am not telling the edit page to be at that location.
Thanks for any help you can give.
I can't see where the problem is as this minimal example works, if you navigate your browser to localhost:8080/journal/edit?id=723
#!/usr/bin/env python
from pyramid.response import Response
from pyramid.view import view_config
from pyramid.config import Configurator
from waitress import serve
#view_config(route_name="root", renderer="string")
def root_view(request):
return "root_view", request.params
#view_config(route_name='action', match_param='action=edit', renderer="string")
def action_view(request):
return "action_view", request.params
if __name__ == '__main__':
config = Configurator()
config.add_route('root', '')
config.add_route('action', '/journal/{action}')
config.scan()
app = config.make_wsgi_app()
serve(app)
Maybe you have some other problem with your routes. Can you paste them here all? Are you sure you do not have another function named update in your view?
Aside from that, you are completely free to build your routes as you wish with Pyramid.
config.add_route('action2', '/different/edit/{id}')
config.add_route('action3', '/someother/{id}/edit')
I personally would rather use one of the schemes above than the match_param predicate...
I'm new to Orchard and have watched both the Pluralsight "Orchard Fundamentals" and "Advanced Orchard" tutorials. Its a great platform, but I'm having a hard time wrapping my head around a couple of things.
I'd like to create a blog showcase banner on the home page only that rotates blog posts on the site. I have the HTML sliced up and functioning on an HTML template. The banner looks like this:
http://arerra.com/news-slideshow.jpg
So far I have done the following:
I've created a Blog called "Articles" and have placed a single post in there for testing.
Added a Layer called "ArticleList" where I have placed a Widget for "Recent Blog Posts"
I've created a custom layout for the home page called "Layout-Url-HomePage.cshtml" in my theme.
In my Theme "Views" folder, I have created a file called "Widget.Wrapper.cshtml" with only #Display(Model.Child) in it to remove the <article><header/><footer /><article> tags globally from the widgets.
Added a file in "Views > Parts > Blogs.RecentBlogPosts.cshtml" to control the layout of my shape. The code is the following:
#using Orchard.ContentManagement;
#{
IEnumerable<object> blogPosts = Model.ContentItems.ContentItems;
}
#if (blogPosts != null) {
<div class="container news-slider">
<ul class="slide-images">
#foreach (dynamic post in blogPosts) {
string title = post.Title;
ContentItem item = post.ContentItem;
<img src="/Themes/MountainWestHoops/Content/img/placeholder-700x380.jpg" alt="#title" class="active" />
}
</ul>
#foreach (dynamic post in blogPosts) {
string title = post.Title;
string body = post.Body;
ContentItem item = post.ContentItem;
<div class="featured-story threeD active">
<h1>#title</h1>
<p>#body #Html.ItemDisplayLink("READ MORE", item)</p>
</div>
}
<aside>
<ul class="tabs">
#foreach (dynamic post in blogPosts) {
string title = post.Title;
string url = post.Url;
ContentItem item = post.ContentItem;
<li><h3>#title</h3></li>
}
</ul>
<div class="ad-three-day-trial">
<img src="/Themes/Content/img/placeholder-260x190.gif" />
</div>
</aside>
</div>
}
My HTML is rendering properly, but none of the values that I have specified are showing up.
I am using the "Shape Tracer" module to see what template is being used. What is funny, is that the #Html.ItemDisplayLink("READ MORE", item) is rendering the article's URL, and if I replace the "READ MORE" with the string title, the title renders properly.
What am I doing wrong here that is causing strings to not display? Am I missing a larger point and misunderstanding the fundamentals? The tutorials seems to say that you can simply move around parts, but in this case, I need to have very specific markup for this slider to work.
Seems like your source was http://weblogs.asp.net/bleroy/archive/2011/03/27/taking-over-list-rendering-in-orchard.aspx
That is a rather old post, and the way the title is handled has changed since then.
The DisplayLink works because the only correct property here is post.ContentItem, which is what that API takes. post.Title and post.Body on the other hand are very likely null, which is why you see nothing. To access the title, you can use post.ContentItem.TitlePart.Title and to get the body, post.ContentItem.BodyPart.Text.