HTML Web.py Web framework Simple Category search - python

I am trying to create a search filter for categories. The problem, among others, is how the python select is not working the way I mentally would like it. In SQLite I want SELECT * FROM Auctions WHERE Category='arg'. This works on the database when I enter say toys where arg would be.
I have category in a dropdown but am unsure how to extract the value. If the search form is used I would like it to just show what category was selected for now and then will work on the price fields. I'm just trying to figure out the web.py way to do this. Heres what I have. This is a school project.
Form:
FilterForm = web.form.Form(
web.form.Textbox('Search', web.form.notnull, description="Search:"),
web.form.Dropdown(name='Category', args=[]),
web.form.Textbox('minPrice', web.form.notnull, description="Price Min: "),
web.form.Textbox('maxPrice', web.form.notnull, description="Price Max: ")
web.form.Button('Apply Search Filter')
)
def POST(self):
if not FilterForm.validates():
Auctions = model.filter_auctions(FilterForm.d.Category, FilterForm.d.maxPrice, FilterForm.d.minPrice)
FilterForm.Category.args = [(a.ItemID, a.Category) for a in Auctions]
return render.index(Auctions,
AuctionForm,
Bids,
BidForm,
Logins,
LoginForm,
FilterForm,
TimeForm)
raise web.seeother('/')
Model:
def filter_auctions(category, price_min, price_max):
return db.select('Auction', where="Category=category")
HTML:
<ul id="filtermenu">
<li>Add Filter<span class="rarrow">▶</span>
<ul class="subF1">
<form action="" method="post">
$:FilterForm.render()
</form>
</ul>
</li>
</ul>

So this ended up being a typo and after reading the man pages things became more clear. If someone runs into a similiar issue I will at least post what worked.The model
Model:
def filter_auctions(category, price_min, price_max):
return db.select('Auction', where="Category=$category AND Price>$price_min
AND Price<$price_max", vars=locals())
Form:
if FilterForm.validates():
Auctions = model.filter_auctions(FilterForm.d.Search, FilterForm.d.Category, FilterForm.d.minPrice,
FilterForm.d.maxPrice)

Related

Flask retrieve element from for loop

I have a music app project which I am working on that uses Flask and SQLalchemy to display search results when you search for a given song. At the moment, I am trying to create a "song page" if you will that displays details regarding the specific song selected. My flask for loop is as such:
{%for row in result%}
<li class="product-result">
<div class="result">
<h3 style="width: 60%; font-size: 95%;" class="result_title">{{row.title}}</h3>
Similar to {{search}}</h4>
</div>
</li>
{%endfor%}
I want to store the row.title in some sort of session variable that can be passed to the song details page when the result is clicked. I am not sure the proper course of action to do this- hence the question. Any help would be greatly appreciated.
Instead try the following in your html loop:
Detail
and then in your route:
# get a specified post by id
#post_bp.route('/detail/<int:id>/<title>/', methods=['POST', 'GET'])
def detail(id, title):
if request.method =='GET':
# get comments under specific post
comment = db.session.query(Comment).filter(Comment.post_id==id).order_by(Comment.id.desc())
# count comments under specific post
no_of_comments = db.session.query(Comment).filter(Comment.post_id == id).count()
# get post under specific id
post = Post.query.get(id)
user = User.query.get(post.author_id)
return render_template('post.html', post=post, user=user, comments = comment, total_comment = no_of_comments)
N.B: This is my sample. You can convert to your own.

How can I create a Dynamic Route in Flask from an existing View?

I am somewhat stuck trying to figure out how to configure a route in Flask properly. I'll try and give some contextual information first:
My web app allows Users to join Clubs. A user can join 1 or many clubs.
Each Club has a dashboard page, and I am serving that to the user via Blueprint:
Route:
#club_bp.route("/clubdashboard/<int:Club_id>", methods=["GET"])
#login_required
def clubdashboard(Club_id):
"""Logged-in User Club Dashboard."""
selectedClub=Club.query.get(Club_id)
return render_template(
"clubdashboard.html",
title="Club Dashboard",
current_user=current_user,
clubname=selectedClub.name,
members = selectedClub.members
)
Jinja2 Template Snippet:
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
{% for club in current_user.users %}
<a class="dropdown-item" href="{{ url_for('club_bp.clubdashboard', Club_id=club.id) }}">{{ club.name }}</a>
{% endfor %}
</div>
The currentuser.users property contains a List of id values for clubs the user is a member of (the .users property is crappy naming on my part, it's on my todo list to fix it.)
The Jinja snippet creates a dropdown menu in the navbar to allow the clubdashboard route to serve an individual dashboard per club.
What I am looking to do is within the Club Dashboard view have a link called "Customize" that, in future, can do a bunch of things (not exactly relevant just yet), but first i'd like to just figure out how to serve the page properly.
For any individual club dashboard I get a URL such as:
http://127.0.0.1:5000/clubdashboard/763074009760399361
What i'd like is simply to serve a URL like this:
http://127.0.0.1:5000/clubdashboard/763074009760399361/customize
But I can't seem to figure out how to do it. Any ideas on where to start would be greatly appreciated.
I've managed to get it working how i'd like. Not sure if it's exactly the best way to do it so happy to be corrected still, but here goes:
In my existing Dashboard route, I took the club ID value and stored it in a user session variable:
#club_bp.route("/clubdashboard/<int:Club_id>", methods=["GET"])
#login_required
def clubdashboard(Club_id):
"""Logged-in User Club Dashboard."""
selectedClub=Club.query.get(Club_id)
session['selectedclubid'] = selectedClub.id
return render_template(
"clubdashboard.html",
title="Club Dashboard",
current_user=current_user,
clubname=selectedClub.name,
members = selectedClub.members
)
I then pass the session variable into the Club Rules (Customise) route from the Jinja template:
<a class="nav-link" href="{{ url_for('club_bp.rules', Club_id=session['selectedclubid']) }}">Club Rules</a>
Route:
#club_bp.route("/clubdashboard/<int:Club_id>/rules", methods=["GET"])
#login_required
def rules(Club_id):
"""Logged-in User Club Dashboard."""
selectedClub=Club.query.get(Club_id)
return render_template(
"clubrules.html",
title="Club Rules",
current_user=current_user,
clubname=selectedClub.name,
members = selectedClub.members
)
This route performs a query for the relevant data and displays. I might have been overthinking the issue.
Simply add the route:
#club_bp.route("/clubdashboard/<int:club_id>/customize", methods=["GET"])
#login_required
def club_customize(club_id):
"""Customize whatever is specific to your club"""
return "TODO: Actually make it work"
And then you can route to it with a simple url_for("club_bp.club_customize", club_id=club.id)
In order to have access to the club ID in clubdashboard.html you need to pass in either the selected club or the club ID in your clubdashboard function:
def clubdashboard(Club_id):
"""Logged-in User Club Dashboard."""
selectedClub=Club.query.get(Club_id)
return render_template(
"clubdashboard.html",
title = "Club Dashboard",
current_user = current_user,
clubname = selectedClub.name,
club = selectedClub, # <-- This lets `club.id` work in the template
members = selectedClub.members
)

Passing information using HTML and Flask

I'm working on a uni project that involves logging films, sort of like Letterboxd. I've made a search page where users enter a keyword when looking for a movie, this then prints the results supplied by the tmdb api. Below is the code in my routes.py file for the search and results page:
#app.route('/search-movie', methods=['GET', 'POST'])
def m_search():
form = MovieSearch()
if form.validate_on_submit():
user_search = urllib.parse.quote(form.movieName.data)
complete_url = search_url + user_search + "&page=1"
conn = urllib.request.urlopen(complete_url)
json_data = json.loads(conn.read())
return render_template('search_results.html', results=json_data["results"], term=form.movieName.data)
return render_template('movie_search.html', form=form)
The following is the code in the html file for that page:
{% block content %}
<h1> results for "{{term}}"</h1>
<h2> click movie name to log it</h2>
<div class="movie-list-container">
{% for movie in results %}
<div class="movie-card">
<img src="https://image.tmdb.org/t/p/w500/{{movie.poster_path}}" alt="">
<!-- <h3> {{movie.title}} ({{movie.release_date[:4]}})</h3> -->
<h3><a class="log-link" href="{{ url_for('log_movie', movieid=movie.id) }}"> {{movie.title}} ({{movie.release_date[:4]}}) </a></h3>
</div>
{% endfor %}
</div>
{% endblock %}
As you can see in the line I commented out, previously it would just display the movie title and release year. However I wanted to change is so that if the user presses the movie name, they are taken to a page where they add information through a form such as their rating, the date they watched the movie, and a review.
This is how it's done on Letterboxd I wanted mine to be pretty much the same.
I want to be able to show the movie name, release date and poster for the movie they pressed on the logging page, and I tried this in the h3 by passing through movieid=movie.id . From there in my routes.py file I wrote the following code
#app.route('/log-movie', methods=['GET', 'POST'])
def log_movie(movieid):
log_url = info_url + movieid + "?api_key=" + api_key
conn = urllib.request.urlopen(log_url)
json_data = json.loads(conn.read())
form = LogMovie()
if form.validate_on_submit():
log_data = Diary(date_watched=form.dateWatched.data, movie_name=mname, release_date=myear, user_rating=form.movieRating.data,
rewatch=form.movieRewatch.data, review=form.movieReview.data, logger=current_user)
db.session.add(log_data)
db.session.commit()
return redirect(url_for('home'))
return render_template('log_movie.html', form=form, results=json_data["results"])
My idea was to simply get the movieid so that I can then request the information from the api again, so that I can pass it through like I did when displaying the results in the html code I added above.
When storing to the database, I have the variables mname, myear. These were from a previous attempt where I wished to pass in the movie year and release date from the HTML without needing to call upon the api again in routes.py. When I couldn't get the multiple variables to pass, that's when I changed it to just movieid. I forgot to change these back but should I manage to pass the information from the HTML, I may go back to this version.
I keep getting an error TypeError: log_movie() missing 1 required positional argument: 'movieid' and I can't seem to find an answer on google. I was wondering if anyone knew why, or a better way of achieving what I want?
I've never asked a question before so let me know if I need to provide more information.
Many Thanks! :)
#app.route('/log-movie', methods=['GET', 'POST'])
def log_movie(movieid):
I'm just pointing out that. This is where your error is. If you want to get the movieid here, you're route should be
#app.route('/log-movie/<movieid>', methods=['GET', 'POST'])
In that way, if you GET /log-movie/12345, the movieid will be 12345.
Edit :
Just to be clear, i just pointed out where your error is, if you need more help about this, you can still make a comment, i'll edit my answer to other question :)

How do I count hits of each element in a list in Django?

So I have a page where multiple articles are listed. (To be precise, TITLES that are outlinked to the articles written on Notion template.) And I want to have a filed in my model that counts the number of clicks of each article. (I don't want to use django-hitcount library).
Let me first show you my code. models.py
class Article(models.Model):
number = models.IntegerField(primary_key=True)
title = models.CharField(max_length=20, default="")
url = models.URLField(max_length=100, default="")
hits = models.IntegerField(default=0)
template
...
<div class="col text-center">
{% for q in allArticles %}
<h2 id={{q.number}}>{{q.title}}</h2>
{% endfor %}
</div>
...
I was thinking of using onclick() event in JavaScript, but then passing data from JavaScript to Django seemed too challenging to me at the moment.
I'd very much appreciate your help. Thanks.
Well, when you dont take up new challenges you stop learning !
The onclick method looks like the best imo, lets see what others suggest.
honestly, using JS and AJAX to communicate with your django server might be dauting at first but it is quite easy really.
if you know how to create a function in your views.py and know a bit of JS, it's just like any other classic functionnality.
Set up your urls.py for the view function that will add a click to the counter:
path('ajax/add_click', views.add_click name="add_click"),
Then, create your view function (pseudo code):
def add_click(request):
# retrieve the article
article_id = request.GET.get("articleId", None)
# then retrieve the object in database, add 1 to the counter save and return a response
Now the "complicated" part, the ajax request:
function add_one_click(articleId) {
$.ajax({
type: "GET",
url: '/ajax/add_click', // you also can use {% url "app_name:add_click" %}
data: {
'articleId': articleId,
},
success: function() {
console.log("hit added to article");
}
});
}
You need to add JS and Ajax lib to your html template for it to works.
Also you need to pass in the onclick attribute the name of the function + the id of the article
onclick="add_one_click({{article.id}})"
One more thing, this type of view, if not protected can lead to get false results.
Instead of having q.url have a new URL(/article_count?id=q.id) which you will define on your Django project
def article_count(req):
_id = req.GET.get('id', '')
# Query Aritcle and get object
q = Article.objects.get(id=_id)
# update the fields for clicks
q.hits += 1
q.save()
# redirect the page
return redirect(q.url)
Edit:
Create a new url that would handle your article click, lets say-
path('article/clicked/<article_number>', views.click_handler, name='click_counter')
Now, in your template use this url for all the article
<div class="col text-center">
{% for q in allArticles %}
<h2 id={{q.number}}>{{q.title}}</h2>
{% endfor %}
</div>
and in your views.py create a new controller
def click_handler(request, article_number):
article = Article.objects.get(number=article_number)
article.hits += 1
article.save()
# now redirect user to the outer link
return redirect(article.url)

How do I filter a model object and update a view in django?

I have a a template with an html table in django. At the top of the view is a small form for input. Something like:
{% csrf_token %}
<form method="post" name="some_name" action="/myApp/">
search button here
...
</form>
<table>
loop through data and make table here
</table>
I when I access the webpage, I initialize data into the table. I have a backend sqlite database with > 25 million rows. I'd like to use the user input from the form to filter the data. I've already tried to get the data from the form, but when I try to apply objects.all.filter(some_condition), I don't see the table update. What am I doing wrong? Or has anyone else come up with a solution to this type of problem? I know this should be simple, but I've had a time figuring it out. Thanks!
UPDATE:
views.py
def showTable(request):
if request.method == 'POST':
#I have a table of aircraft entries.The EntriesTable has a ForeignKey
#referencing an AircraftTable that has the numbers of aircraft
#some_name will reference the number of the aircraft
aircraft = request.POST.get('some_name', None)
query_results = EntriesTable.objects.all().filter(aircraft__exact = filterable)
template=loader.get_template('myApp/showTable.html')
context=RequestContext(request, {'query_results': query_results,})
return HttpResponse(template.render(context))
else:
query_results = EntriesTable.objects.all().filter(start_time__range(start, stop)) #Assume that I have correctly filtered on start/stop times. I am able to render that so far without issue.
context= RequestContext(request, {'query_results': query_results,})
return HttpResponse(template.render(context))
query_results = EntriesTable.objects.filter(aircraft__exact = filterable)
You have an error in your view, the syntax should me:
NameOfModelClass.objects.filter(conditions)
I believe that will fix the problem

Categories